4 * Copyright(c) 2006-2007, Ext JS, LLC.
6 * Originally Released Under LGPL - original licence link has changed is not relivant.
9 * <script type="text/javascript">
14 * @class Roo.data.SortTypes
16 * Defines the default sorting (casting?) comparison functions used when sorting data.
18 Roo.data.SortTypes = {
20 * Default sort that does nothing
21 * @param {Mixed} s The value being converted
22 * @return {Mixed} The comparison value
29 * The regular expression used to strip tags
33 stripTagsRE : /<\/?[^>]+>/gi,
36 * Strips all HTML tags to sort on text only
37 * @param {Mixed} s The value being converted
38 * @return {String} The comparison value
41 return String(s).replace(this.stripTagsRE, "");
45 * Strips all HTML tags to sort on text only - Case insensitive
46 * @param {Mixed} s The value being converted
47 * @return {String} The comparison value
49 asUCText : function(s){
50 return String(s).toUpperCase().replace(this.stripTagsRE, "");
54 * Case insensitive string
55 * @param {Mixed} s The value being converted
56 * @return {String} The comparison value
58 asUCString : function(s) {
59 return String(s).toUpperCase();
64 * @param {Mixed} s The value being converted
65 * @return {Number} The comparison value
67 asDate : function(s) {
71 if(s instanceof Date){
74 return Date.parse(String(s));
79 * @param {Mixed} s The value being converted
80 * @return {Float} The comparison value
82 asFloat : function(s) {
83 var val = parseFloat(String(s).replace(/,/g, ""));
92 * @param {Mixed} s The value being converted
93 * @return {Number} The comparison value
96 var val = parseInt(String(s).replace(/,/g, ""));
104 * Ext JS Library 1.1.1
105 * Copyright(c) 2006-2007, Ext JS, LLC.
107 * Originally Released Under LGPL - original licence link has changed is not relivant.
110 * <script type="text/javascript">
114 * @class Roo.data.Record
115 * Instances of this class encapsulate both record <em>definition</em> information, and record
116 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117 * to access Records cached in an {@link Roo.data.Store} object.<br>
119 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
123 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
125 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126 * {@link #create}. The parameters are the same.
127 * @param {Array} data An associative Array of data values keyed by the field name.
128 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130 * not specified an integer id is generated.
132 Roo.data.Record = function(data, id){
133 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
138 * Generate a constructor for a specific record layout.
139 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141 * Each field definition object may contain the following properties: <ul>
142 * <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,
143 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146 * is being used, then this is a string containing the javascript expression to reference the data relative to
147 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148 * to the data item relative to the record element. If the mapping expression is the same as the field name,
149 * this may be omitted.</p></li>
150 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151 * <ul><li>auto (Default, implies no conversion)</li>
156 * <li>date</li></ul></p></li>
157 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160 * by the Reader into an object that will be stored in the Record. It is passed the
161 * following parameters:<ul>
162 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
164 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
166 * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168 {name: 'title', mapping: 'topic_title'},
169 {name: 'author', mapping: 'username'},
170 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171 {name: 'lastPost', mapping: 'post_time', type: 'date'},
172 {name: 'lastPoster', mapping: 'user2'},
173 {name: 'excerpt', mapping: 'post_text'}
176 var myNewRecord = new TopicRecord({
177 title: 'Do my job please',
180 lastPost: new Date(),
181 lastPoster: 'Animal',
182 excerpt: 'No way dude!'
184 myStore.add(myNewRecord);
189 Roo.data.Record.create = function(o){
191 f.superclass.constructor.apply(this, arguments);
193 Roo.extend(f, Roo.data.Record);
195 p.fields = new Roo.util.MixedCollection(false, function(field){
198 for(var i = 0, len = o.length; i < len; i++){
199 p.fields.add(new Roo.data.Field(o[i]));
201 f.getField = function(name){
202 return p.fields.get(name);
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
212 Roo.data.Record.prototype = {
214 * Readonly flag - true if this record has been modified.
223 join : function(store){
228 * Set the named field to the specified value.
229 * @param {String} name The name of the field to set.
230 * @param {Object} value The value to set the field to.
232 set : function(name, value){
233 if(this.data[name] == value){
240 if(typeof this.modified[name] == 'undefined'){
241 this.modified[name] = this.data[name];
243 this.data[name] = value;
244 if(!this.editing && this.store){
245 this.store.afterEdit(this);
250 * Get the value of the named field.
251 * @param {String} name The name of the field to get the value of.
252 * @return {Object} The value of the field.
254 get : function(name){
255 return this.data[name];
259 beginEdit : function(){
265 cancelEdit : function(){
266 this.editing = false;
267 delete this.modified;
271 endEdit : function(){
272 this.editing = false;
273 if(this.dirty && this.store){
274 this.store.afterEdit(this);
279 * Usually called by the {@link Roo.data.Store} which owns the Record.
280 * Rejects all changes made to the Record since either creation, or the last commit operation.
281 * Modified fields are reverted to their original values.
283 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284 * of reject operations.
287 var m = this.modified;
289 if(typeof m[n] != "function"){
294 delete this.modified;
295 this.editing = false;
297 this.store.afterReject(this);
302 * Usually called by the {@link Roo.data.Store} which owns the Record.
303 * Commits all changes made to the Record since either creation, or the last commit operation.
305 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306 * of commit operations.
310 delete this.modified;
311 this.editing = false;
313 this.store.afterCommit(this);
318 hasError : function(){
319 return this.error != null;
323 clearError : function(){
328 * Creates a copy of this record.
329 * @param {String} id (optional) A new record id if you don't want to use this record's id
332 copy : function(newId) {
333 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
337 * Ext JS Library 1.1.1
338 * Copyright(c) 2006-2007, Ext JS, LLC.
340 * Originally Released Under LGPL - original licence link has changed is not relivant.
343 * <script type="text/javascript">
349 * @class Roo.data.Store
350 * @extends Roo.util.Observable
351 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
354 * 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
355 * has no knowledge of the format of the data returned by the Proxy.<br>
357 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358 * instances from the data object. These records are cached and made available through accessor functions.
360 * Creates a new Store.
361 * @param {Object} config A config object containing the objects needed for the Store to access data,
362 * and read the data into Records.
364 Roo.data.Store = function(config){
365 this.data = new Roo.util.MixedCollection(false);
366 this.data.getKey = function(o){
369 this.baseParams = {};
376 "multisort" : "_multisort"
379 if(config && config.data){
380 this.inlineData = config.data;
384 Roo.apply(this, config);
386 if(this.reader){ // reader passed
387 this.reader = Roo.factory(this.reader, Roo.data);
388 this.reader.xmodule = this.xmodule || false;
389 if(!this.recordType){
390 this.recordType = this.reader.recordType;
392 if(this.reader.onMetaChange){
393 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
398 this.fields = this.recordType.prototype.fields;
405 * Fires when the data cache has changed, and a widget which is using this Store
406 * as a Record cache should refresh its view.
407 * @param {Store} this
412 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413 * @param {Store} this
414 * @param {Object} meta The JSON metadata
419 * Fires when Records have been added to the Store
420 * @param {Store} this
421 * @param {Roo.data.Record[]} records The array of Records added
422 * @param {Number} index The index at which the record(s) were added
427 * Fires when a Record has been removed from the Store
428 * @param {Store} this
429 * @param {Roo.data.Record} record The Record that was removed
430 * @param {Number} index The index at which the record was removed
435 * Fires when a Record has been updated
436 * @param {Store} this
437 * @param {Roo.data.Record} record The Record that was updated
438 * @param {String} operation The update operation being performed. Value may be one of:
441 Roo.data.Record.REJECT
442 Roo.data.Record.COMMIT
448 * Fires when the data cache has been cleared.
449 * @param {Store} this
454 * Fires before a request is made for a new data object. If the beforeload handler returns false
455 * the load action will be canceled.
456 * @param {Store} this
457 * @param {Object} options The loading options that were specified (see {@link #load} for details)
461 * @event beforeloadadd
462 * Fires after a new set of Records has been loaded.
463 * @param {Store} this
464 * @param {Roo.data.Record[]} records The Records that were loaded
465 * @param {Object} options The loading options that were specified (see {@link #load} for details)
467 beforeloadadd : true,
470 * Fires after a new set of Records has been loaded, before they are added to the store.
471 * @param {Store} this
472 * @param {Roo.data.Record[]} records The Records that were loaded
473 * @param {Object} options The loading options that were specified (see {@link #load} for details)
474 * @params {Object} return from reader
478 * @event loadexception
479 * Fires if an exception occurs in the Proxy during loading.
480 * Called with the signature of the Proxy's "loadexception" event.
481 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
484 * @param {Object} return from JsonData.reader() - success, totalRecords, records
485 * @param {Object} load options
486 * @param {Object} jsonData from your request (normally this contains the Exception)
492 this.proxy = Roo.factory(this.proxy, Roo.data);
493 this.proxy.xmodule = this.xmodule || false;
494 this.relayEvents(this.proxy, ["loadexception"]);
496 this.sortToggle = {};
497 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
499 Roo.data.Store.superclass.constructor.call(this);
502 this.loadData(this.inlineData);
503 delete this.inlineData;
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
509 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
510 * without a remote query - used by combo/forms at present.
514 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
517 * @cfg {Array} data Inline data to be loaded when the store is initialized.
520 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
521 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
524 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525 * on any HTTP request
528 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
531 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
535 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
541 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542 * loaded or when a record is removed. (defaults to false).
544 pruneModifiedRecords : false,
550 * Add Records to the Store and fires the add event.
551 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
553 add : function(records){
554 records = [].concat(records);
555 for(var i = 0, len = records.length; i < len; i++){
556 records[i].join(this);
558 var index = this.data.length;
559 this.data.addAll(records);
560 this.fireEvent("add", this, records, index);
564 * Remove a Record from the Store and fires the remove event.
565 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
567 remove : function(record){
568 var index = this.data.indexOf(record);
569 this.data.removeAt(index);
570 if(this.pruneModifiedRecords){
571 this.modified.remove(record);
573 this.fireEvent("remove", this, record, index);
577 * Remove all Records from the Store and fires the clear event.
579 removeAll : function(){
581 if(this.pruneModifiedRecords){
584 this.fireEvent("clear", this);
588 * Inserts Records to the Store at the given index and fires the add event.
589 * @param {Number} index The start index at which to insert the passed Records.
590 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592 insert : function(index, records){
593 records = [].concat(records);
594 for(var i = 0, len = records.length; i < len; i++){
595 this.data.insert(index, records[i]);
596 records[i].join(this);
598 this.fireEvent("add", this, records, index);
602 * Get the index within the cache of the passed Record.
603 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
604 * @return {Number} The index of the passed Record. Returns -1 if not found.
606 indexOf : function(record){
607 return this.data.indexOf(record);
611 * Get the index within the cache of the Record with the passed id.
612 * @param {String} id The id of the Record to find.
613 * @return {Number} The index of the Record. Returns -1 if not found.
615 indexOfId : function(id){
616 return this.data.indexOfKey(id);
620 * Get the Record with the specified id.
621 * @param {String} id The id of the Record to find.
622 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624 getById : function(id){
625 return this.data.key(id);
629 * Get the Record at the specified index.
630 * @param {Number} index The index of the Record to find.
631 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633 getAt : function(index){
634 return this.data.itemAt(index);
638 * Returns a range of Records between specified indices.
639 * @param {Number} startIndex (optional) The starting index (defaults to 0)
640 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
641 * @return {Roo.data.Record[]} An array of Records
643 getRange : function(start, end){
644 return this.data.getRange(start, end);
648 storeOptions : function(o){
649 o = Roo.apply({}, o);
652 this.lastOptions = o;
656 * Loads the Record cache from the configured Proxy using the configured Reader.
658 * If using remote paging, then the first load call must specify the <em>start</em>
659 * and <em>limit</em> properties in the options.params property to establish the initial
660 * position within the dataset, and the number of Records to cache on each read from the Proxy.
662 * <strong>It is important to note that for remote data sources, loading is asynchronous,
663 * and this call will return before the new data has been loaded. Perform any post-processing
664 * in a callback function, or in a "load" event handler.</strong>
666 * @param {Object} options An object containing properties which control loading options:<ul>
667 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
668 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
669 * passed the following arguments:<ul>
670 * <li>r : Roo.data.Record[]</li>
671 * <li>options: Options object from the load call</li>
672 * <li>success: Boolean success indicator</li></ul></li>
673 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
674 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
677 load : function(options){
678 options = options || {};
679 if(this.fireEvent("beforeload", this, options) !== false){
680 this.storeOptions(options);
681 var p = Roo.apply(options.params || {}, this.baseParams);
682 // if meta was not loaded from remote source.. try requesting it.
683 if (!this.reader.metaFromRemote) {
686 if(this.sortInfo && this.remoteSort){
687 var pn = this.paramNames;
688 p[pn["sort"]] = this.sortInfo.field;
689 p[pn["dir"]] = this.sortInfo.direction;
691 if (this.multiSort) {
692 var pn = this.paramNames;
693 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
696 this.proxy.load(p, this.reader, this.loadRecords, this, options);
701 * Reloads the Record cache from the configured Proxy using the configured Reader and
702 * the options from the last load operation performed.
703 * @param {Object} options (optional) An object containing properties which may override the options
704 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
705 * the most recently used options are reused).
707 reload : function(options){
708 this.load(Roo.applyIf(options||{}, this.lastOptions));
712 // Called as a callback by the Reader during a load operation.
713 loadRecords : function(o, options, success){
714 if(!o || success === false){
715 if(success !== false){
716 this.fireEvent("load", this, [], options, o);
718 if(options.callback){
719 options.callback.call(options.scope || this, [], options, false);
723 // if data returned failure - throw an exception.
724 if (o.success === false) {
725 // show a message if no listener is registered.
726 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
727 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
729 // loadmask wil be hooked into this..
730 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
733 var r = o.records, t = o.totalRecords || r.length;
735 this.fireEvent("beforeloadadd", this, r, options, o);
737 if(!options || options.add !== true){
738 if(this.pruneModifiedRecords){
741 for(var i = 0, len = r.length; i < len; i++){
745 this.data = this.snapshot;
746 delete this.snapshot;
750 this.totalLength = t;
752 this.fireEvent("datachanged", this);
754 this.totalLength = Math.max(t, this.data.length+r.length);
757 this.fireEvent("load", this, r, options, o);
758 if(options.callback){
759 options.callback.call(options.scope || this, r, options, true);
765 * Loads data from a passed data block. A Reader which understands the format of the data
766 * must have been configured in the constructor.
767 * @param {Object} data The data block from which to read the Records. The format of the data expected
768 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
769 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
771 loadData : function(o, append){
772 var r = this.reader.readRecords(o);
773 this.loadRecords(r, {add: append}, true);
777 * Gets the number of cached records.
779 * <em>If using paging, this may not be the total size of the dataset. If the data object
780 * used by the Reader contains the dataset size, then the getTotalCount() function returns
781 * the data set size</em>
783 getCount : function(){
784 return this.data.length || 0;
788 * Gets the total number of records in the dataset as returned by the server.
790 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
791 * the dataset size</em>
793 getTotalCount : function(){
794 return this.totalLength || 0;
798 * Returns the sort state of the Store as an object with two properties:
800 field {String} The name of the field by which the Records are sorted
801 direction {String} The sort order, "ASC" or "DESC"
804 getSortState : function(){
805 return this.sortInfo;
809 applySort : function(){
810 if(this.sortInfo && !this.remoteSort){
811 var s = this.sortInfo, f = s.field;
812 var st = this.fields.get(f).sortType;
813 var fn = function(r1, r2){
814 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
815 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
817 this.data.sort(s.direction, fn);
818 if(this.snapshot && this.snapshot != this.data){
819 this.snapshot.sort(s.direction, fn);
825 * Sets the default sort column and order to be used by the next load operation.
826 * @param {String} fieldName The name of the field to sort by.
827 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
829 setDefaultSort : function(field, dir){
830 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
835 * If remote sorting is used, the sort is performed on the server, and the cache is
836 * reloaded. If local sorting is used, the cache is sorted internally.
837 * @param {String} fieldName The name of the field to sort by.
838 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
840 sort : function(fieldName, dir){
841 var f = this.fields.get(fieldName);
843 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
845 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
846 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
851 this.sortToggle[f.name] = dir;
852 this.sortInfo = {field: f.name, direction: dir};
853 if(!this.remoteSort){
855 this.fireEvent("datachanged", this);
857 this.load(this.lastOptions);
862 * Calls the specified function for each of the Records in the cache.
863 * @param {Function} fn The function to call. The Record is passed as the first parameter.
864 * Returning <em>false</em> aborts and exits the iteration.
865 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
867 each : function(fn, scope){
868 this.data.each(fn, scope);
872 * Gets all records modified since the last commit. Modified records are persisted across load operations
873 * (e.g., during paging).
874 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
876 getModifiedRecords : function(){
877 return this.modified;
881 createFilterFn : function(property, value, anyMatch){
882 if(!value.exec){ // not a regex
883 value = String(value);
884 if(value.length == 0){
887 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
890 return value.test(r.data[property]);
895 * Sums the value of <i>property</i> for each record between start and end and returns the result.
896 * @param {String} property A field on your records
897 * @param {Number} start The record index to start at (defaults to 0)
898 * @param {Number} end The last record index to include (defaults to length - 1)
899 * @return {Number} The sum
901 sum : function(property, start, end){
902 var rs = this.data.items, v = 0;
904 end = (end || end === 0) ? end : rs.length-1;
906 for(var i = start; i <= end; i++){
907 v += (rs[i].data[property] || 0);
913 * Filter the records by a specified property.
914 * @param {String} field A field on your records
915 * @param {String/RegExp} value Either a string that the field
916 * should start with or a RegExp to test against the field
917 * @param {Boolean} anyMatch True to match any part not just the beginning
919 filter : function(property, value, anyMatch){
920 var fn = this.createFilterFn(property, value, anyMatch);
921 return fn ? this.filterBy(fn) : this.clearFilter();
925 * Filter by a function. The specified function will be called with each
926 * record in this data source. If the function returns true the record is included,
927 * otherwise it is filtered.
928 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
929 * @param {Object} scope (optional) The scope of the function (defaults to this)
931 filterBy : function(fn, scope){
932 this.snapshot = this.snapshot || this.data;
933 this.data = this.queryBy(fn, scope||this);
934 this.fireEvent("datachanged", this);
938 * Query the records by a specified property.
939 * @param {String} field A field on your records
940 * @param {String/RegExp} value Either a string that the field
941 * should start with or a RegExp to test against the field
942 * @param {Boolean} anyMatch True to match any part not just the beginning
943 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
945 query : function(property, value, anyMatch){
946 var fn = this.createFilterFn(property, value, anyMatch);
947 return fn ? this.queryBy(fn) : this.data.clone();
951 * Query by a function. The specified function will be called with each
952 * record in this data source. If the function returns true the record is included
954 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
955 * @param {Object} scope (optional) The scope of the function (defaults to this)
956 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
958 queryBy : function(fn, scope){
959 var data = this.snapshot || this.data;
960 return data.filterBy(fn, scope||this);
964 * Collects unique values for a particular dataIndex from this store.
965 * @param {String} dataIndex The property to collect
966 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
967 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
968 * @return {Array} An array of the unique values
970 collect : function(dataIndex, allowNull, bypassFilter){
971 var d = (bypassFilter === true && this.snapshot) ?
972 this.snapshot.items : this.data.items;
973 var v, sv, r = [], l = {};
974 for(var i = 0, len = d.length; i < len; i++){
975 v = d[i].data[dataIndex];
977 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
986 * Revert to a view of the Record cache with no filtering applied.
987 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
989 clearFilter : function(suppressEvent){
990 if(this.snapshot && this.snapshot != this.data){
991 this.data = this.snapshot;
992 delete this.snapshot;
993 if(suppressEvent !== true){
994 this.fireEvent("datachanged", this);
1000 afterEdit : function(record){
1001 if(this.modified.indexOf(record) == -1){
1002 this.modified.push(record);
1004 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1008 afterReject : function(record){
1009 this.modified.remove(record);
1010 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1014 afterCommit : function(record){
1015 this.modified.remove(record);
1016 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1020 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1021 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1023 commitChanges : function(){
1024 var m = this.modified.slice(0);
1026 for(var i = 0, len = m.length; i < len; i++){
1032 * Cancel outstanding changes on all changed records.
1034 rejectChanges : function(){
1035 var m = this.modified.slice(0);
1037 for(var i = 0, len = m.length; i < len; i++){
1042 onMetaChange : function(meta, rtype, o){
1043 this.recordType = rtype;
1044 this.fields = rtype.prototype.fields;
1045 delete this.snapshot;
1046 this.sortInfo = meta.sortInfo || this.sortInfo;
1048 this.fireEvent('metachange', this, this.reader.meta);
1051 moveIndex : function(data, type)
1053 var index = this.indexOf(data);
1055 var newIndex = index + type;
1059 this.insert(newIndex, data);
1064 * Ext JS Library 1.1.1
1065 * Copyright(c) 2006-2007, Ext JS, LLC.
1067 * Originally Released Under LGPL - original licence link has changed is not relivant.
1070 * <script type="text/javascript">
1074 * @class Roo.data.SimpleStore
1075 * @extends Roo.data.Store
1076 * Small helper class to make creating Stores from Array data easier.
1077 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1078 * @cfg {Array} fields An array of field definition objects, or field name strings.
1079 * @cfg {Array} data The multi-dimensional array of data
1081 * @param {Object} config
1083 Roo.data.SimpleStore = function(config){
1084 Roo.data.SimpleStore.superclass.constructor.call(this, {
1086 reader: new Roo.data.ArrayReader({
1089 Roo.data.Record.create(config.fields)
1091 proxy : new Roo.data.MemoryProxy(config.data)
1095 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1097 * Ext JS Library 1.1.1
1098 * Copyright(c) 2006-2007, Ext JS, LLC.
1100 * Originally Released Under LGPL - original licence link has changed is not relivant.
1103 * <script type="text/javascript">
1108 * @extends Roo.data.Store
1109 * @class Roo.data.JsonStore
1110 * Small helper class to make creating Stores for JSON data easier. <br/>
1112 var store = new Roo.data.JsonStore({
1113 url: 'get-images.php',
1115 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1118 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1119 * JsonReader and HttpProxy (unless inline data is provided).</b>
1120 * @cfg {Array} fields An array of field definition objects, or field name strings.
1122 * @param {Object} config
1124 Roo.data.JsonStore = function(c){
1125 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1126 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1127 reader: new Roo.data.JsonReader(c, c.fields)
1130 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1132 * Ext JS Library 1.1.1
1133 * Copyright(c) 2006-2007, Ext JS, LLC.
1135 * Originally Released Under LGPL - original licence link has changed is not relivant.
1138 * <script type="text/javascript">
1142 Roo.data.Field = function(config){
1143 if(typeof config == "string"){
1144 config = {name: config};
1146 Roo.apply(this, config);
1152 var st = Roo.data.SortTypes;
1153 // named sortTypes are supported, here we look them up
1154 if(typeof this.sortType == "string"){
1155 this.sortType = st[this.sortType];
1158 // set default sortType for strings and dates
1162 this.sortType = st.asUCString;
1165 this.sortType = st.asDate;
1168 this.sortType = st.none;
1173 var stripRe = /[\$,%]/g;
1175 // prebuilt conversion function for this field, instead of
1176 // switching every time we're reading a value
1178 var cv, dateFormat = this.dateFormat;
1183 cv = function(v){ return v; };
1186 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1190 return v !== undefined && v !== null && v !== '' ?
1191 parseInt(String(v).replace(stripRe, ""), 10) : '';
1196 return v !== undefined && v !== null && v !== '' ?
1197 parseFloat(String(v).replace(stripRe, ""), 10) : '';
1202 cv = function(v){ return v === true || v === "true" || v == 1; };
1209 if(v instanceof Date){
1213 if(dateFormat == "timestamp"){
1214 return new Date(v*1000);
1216 return Date.parseDate(v, dateFormat);
1218 var parsed = Date.parse(v);
1219 return parsed ? new Date(parsed) : null;
1228 Roo.data.Field.prototype = {
1236 * Ext JS Library 1.1.1
1237 * Copyright(c) 2006-2007, Ext JS, LLC.
1239 * Originally Released Under LGPL - original licence link has changed is not relivant.
1242 * <script type="text/javascript">
1245 // Base class for reading structured data from a data source. This class is intended to be
1246 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1249 * @class Roo.data.DataReader
1250 * Base class for reading structured data from a data source. This class is intended to be
1251 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1254 Roo.data.DataReader = function(meta, recordType){
1258 this.recordType = recordType instanceof Array ?
1259 Roo.data.Record.create(recordType) : recordType;
1262 Roo.data.DataReader.prototype = {
1264 * Create an empty record
1265 * @param {Object} data (optional) - overlay some values
1266 * @return {Roo.data.Record} record created.
1268 newRow : function(d) {
1270 this.recordType.prototype.fields.each(function(c) {
1272 case 'int' : da[c.name] = 0; break;
1273 case 'date' : da[c.name] = new Date(); break;
1274 case 'float' : da[c.name] = 0.0; break;
1275 case 'boolean' : da[c.name] = false; break;
1276 default : da[c.name] = ""; break;
1280 return new this.recordType(Roo.apply(da, d));
1285 * Ext JS Library 1.1.1
1286 * Copyright(c) 2006-2007, Ext JS, LLC.
1288 * Originally Released Under LGPL - original licence link has changed is not relivant.
1291 * <script type="text/javascript">
1295 * @class Roo.data.DataProxy
1296 * @extends Roo.data.Observable
1297 * This class is an abstract base class for implementations which provide retrieval of
1298 * unformatted data objects.<br>
1300 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1301 * (of the appropriate type which knows how to parse the data object) to provide a block of
1302 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1304 * Custom implementations must implement the load method as described in
1305 * {@link Roo.data.HttpProxy#load}.
1307 Roo.data.DataProxy = function(){
1311 * Fires before a network request is made to retrieve a data object.
1312 * @param {Object} This DataProxy object.
1313 * @param {Object} params The params parameter to the load function.
1318 * Fires before the load method's callback is called.
1319 * @param {Object} This DataProxy object.
1320 * @param {Object} o The data object.
1321 * @param {Object} arg The callback argument object passed to the load function.
1325 * @event loadexception
1326 * Fires if an Exception occurs during data retrieval.
1327 * @param {Object} This DataProxy object.
1328 * @param {Object} o The data object.
1329 * @param {Object} arg The callback argument object passed to the load function.
1330 * @param {Object} e The Exception.
1332 loadexception : true
1334 Roo.data.DataProxy.superclass.constructor.call(this);
1337 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1340 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1344 * Ext JS Library 1.1.1
1345 * Copyright(c) 2006-2007, Ext JS, LLC.
1347 * Originally Released Under LGPL - original licence link has changed is not relivant.
1350 * <script type="text/javascript">
1353 * @class Roo.data.MemoryProxy
1354 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1355 * to the Reader when its load method is called.
1357 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1359 Roo.data.MemoryProxy = function(data){
1363 Roo.data.MemoryProxy.superclass.constructor.call(this);
1367 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1370 * Load data from the requested source (in this case an in-memory
1371 * data object passed to the constructor), read the data object into
1372 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1373 * process that block using the passed callback.
1374 * @param {Object} params This parameter is not used by the MemoryProxy class.
1375 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1376 * object into a block of Roo.data.Records.
1377 * @param {Function} callback The function into which to pass the block of Roo.data.records.
1378 * The function must be passed <ul>
1379 * <li>The Record block object</li>
1380 * <li>The "arg" argument from the load function</li>
1381 * <li>A boolean success indicator</li>
1383 * @param {Object} scope The scope in which to call the callback
1384 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1386 load : function(params, reader, callback, scope, arg){
1387 params = params || {};
1390 result = reader.readRecords(this.data);
1392 this.fireEvent("loadexception", this, arg, null, e);
1393 callback.call(scope, null, arg, false);
1396 callback.call(scope, result, arg, true);
1400 update : function(params, records){
1405 * Ext JS Library 1.1.1
1406 * Copyright(c) 2006-2007, Ext JS, LLC.
1408 * Originally Released Under LGPL - original licence link has changed is not relivant.
1411 * <script type="text/javascript">
1414 * @class Roo.data.HttpProxy
1415 * @extends Roo.data.DataProxy
1416 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1417 * configured to reference a certain URL.<br><br>
1419 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1420 * from which the running page was served.<br><br>
1422 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1424 * Be aware that to enable the browser to parse an XML document, the server must set
1425 * the Content-Type header in the HTTP response to "text/xml".
1427 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1428 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
1429 * will be used to make the request.
1431 Roo.data.HttpProxy = function(conn){
1432 Roo.data.HttpProxy.superclass.constructor.call(this);
1433 // is conn a conn config or a real conn?
1435 this.useAjax = !conn || !conn.events;
1439 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1440 // thse are take from connection...
1443 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1446 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1447 * extra parameters to each request made by this object. (defaults to undefined)
1450 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1451 * to each request made by this object. (defaults to undefined)
1454 * @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)
1457 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1460 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1466 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1470 * Return the {@link Roo.data.Connection} object being used by this Proxy.
1471 * @return {Connection} The Connection object. This object may be used to subscribe to events on
1472 * a finer-grained basis than the DataProxy events.
1474 getConnection : function(){
1475 return this.useAjax ? Roo.Ajax : this.conn;
1479 * Load data from the configured {@link Roo.data.Connection}, read the data object into
1480 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1481 * process that block using the passed callback.
1482 * @param {Object} params An object containing properties which are to be used as HTTP parameters
1483 * for the request to the remote server.
1484 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1485 * object into a block of Roo.data.Records.
1486 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1487 * The function must be passed <ul>
1488 * <li>The Record block object</li>
1489 * <li>The "arg" argument from the load function</li>
1490 * <li>A boolean success indicator</li>
1492 * @param {Object} scope The scope in which to call the callback
1493 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1495 load : function(params, reader, callback, scope, arg){
1496 if(this.fireEvent("beforeload", this, params) !== false){
1498 params : params || {},
1500 callback : callback,
1505 callback : this.loadResponse,
1509 Roo.applyIf(o, this.conn);
1510 if(this.activeRequest){
1511 Roo.Ajax.abort(this.activeRequest);
1513 this.activeRequest = Roo.Ajax.request(o);
1515 this.conn.request(o);
1518 callback.call(scope||this, null, arg, false);
1523 loadResponse : function(o, success, response){
1524 delete this.activeRequest;
1526 this.fireEvent("loadexception", this, o, response);
1527 o.request.callback.call(o.request.scope, null, o.request.arg, false);
1532 result = o.reader.read(response);
1534 this.fireEvent("loadexception", this, o, response, e);
1535 o.request.callback.call(o.request.scope, null, o.request.arg, false);
1539 this.fireEvent("load", this, o, o.request.arg);
1540 o.request.callback.call(o.request.scope, result, o.request.arg, true);
1544 update : function(dataSet){
1549 updateResponse : function(dataSet){
1554 * Ext JS Library 1.1.1
1555 * Copyright(c) 2006-2007, Ext JS, LLC.
1557 * Originally Released Under LGPL - original licence link has changed is not relivant.
1560 * <script type="text/javascript">
1564 * @class Roo.data.ScriptTagProxy
1565 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1566 * other than the originating domain of the running page.<br><br>
1568 * <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
1569 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1571 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1572 * source code that is used as the source inside a <script> tag.<br><br>
1574 * In order for the browser to process the returned data, the server must wrap the data object
1575 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1576 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1577 * depending on whether the callback name was passed:
1580 boolean scriptTag = false;
1581 String cb = request.getParameter("callback");
1584 response.setContentType("text/javascript");
1586 response.setContentType("application/x-json");
1588 Writer out = response.getWriter();
1590 out.write(cb + "(");
1592 out.print(dataBlock.toJsonString());
1599 * @param {Object} config A configuration object.
1601 Roo.data.ScriptTagProxy = function(config){
1602 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1603 Roo.apply(this, config);
1604 this.head = document.getElementsByTagName("head")[0];
1607 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1609 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1611 * @cfg {String} url The URL from which to request the data object.
1614 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1618 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1619 * the server the name of the callback function set up by the load call to process the returned data object.
1620 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1621 * javascript output which calls this named function passing the data object as its only parameter.
1623 callbackParam : "callback",
1625 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1626 * name to the request.
1631 * Load data from the configured URL, read the data object into
1632 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1633 * process that block using the passed callback.
1634 * @param {Object} params An object containing properties which are to be used as HTTP parameters
1635 * for the request to the remote server.
1636 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1637 * object into a block of Roo.data.Records.
1638 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1639 * The function must be passed <ul>
1640 * <li>The Record block object</li>
1641 * <li>The "arg" argument from the load function</li>
1642 * <li>A boolean success indicator</li>
1644 * @param {Object} scope The scope in which to call the callback
1645 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1647 load : function(params, reader, callback, scope, arg){
1648 if(this.fireEvent("beforeload", this, params) !== false){
1650 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1653 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1655 url += "&_dc=" + (new Date().getTime());
1657 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1660 cb : "stcCallback"+transId,
1661 scriptId : "stcScript"+transId,
1665 callback : callback,
1671 window[trans.cb] = function(o){
1672 conn.handleResponse(o, trans);
1675 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1677 if(this.autoAbort !== false){
1681 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1683 var script = document.createElement("script");
1684 script.setAttribute("src", url);
1685 script.setAttribute("type", "text/javascript");
1686 script.setAttribute("id", trans.scriptId);
1687 this.head.appendChild(script);
1691 callback.call(scope||this, null, arg, false);
1696 isLoading : function(){
1697 return this.trans ? true : false;
1701 * Abort the current server request.
1704 if(this.isLoading()){
1705 this.destroyTrans(this.trans);
1710 destroyTrans : function(trans, isLoaded){
1711 this.head.removeChild(document.getElementById(trans.scriptId));
1712 clearTimeout(trans.timeoutId);
1714 window[trans.cb] = undefined;
1716 delete window[trans.cb];
1719 // if hasn't been loaded, wait for load to remove it to prevent script error
1720 window[trans.cb] = function(){
1721 window[trans.cb] = undefined;
1723 delete window[trans.cb];
1730 handleResponse : function(o, trans){
1732 this.destroyTrans(trans, true);
1735 result = trans.reader.readRecords(o);
1737 this.fireEvent("loadexception", this, o, trans.arg, e);
1738 trans.callback.call(trans.scope||window, null, trans.arg, false);
1741 this.fireEvent("load", this, o, trans.arg);
1742 trans.callback.call(trans.scope||window, result, trans.arg, true);
1746 handleFailure : function(trans){
1748 this.destroyTrans(trans, false);
1749 this.fireEvent("loadexception", this, null, trans.arg);
1750 trans.callback.call(trans.scope||window, null, trans.arg, false);
1754 * Ext JS Library 1.1.1
1755 * Copyright(c) 2006-2007, Ext JS, LLC.
1757 * Originally Released Under LGPL - original licence link has changed is not relivant.
1760 * <script type="text/javascript">
1764 * @class Roo.data.JsonReader
1765 * @extends Roo.data.DataReader
1766 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1767 * based on mappings in a provided Roo.data.Record constructor.
1769 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1770 * in the reply previously.
1775 var RecordDef = Roo.data.Record.create([
1776 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
1777 {name: 'occupation'} // This field will use "occupation" as the mapping.
1779 var myReader = new Roo.data.JsonReader({
1780 totalProperty: "results", // The property which contains the total dataset size (optional)
1781 root: "rows", // The property which contains an Array of row objects
1782 id: "id" // The property within each row object that provides an ID for the record (optional)
1786 * This would consume a JSON file like this:
1788 { 'results': 2, 'rows': [
1789 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1790 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1793 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1794 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1795 * paged from the remote server.
1796 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1797 * @cfg {String} root name of the property which contains the Array of row objects.
1798 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1799 * @cfg {Array} fields Array of field definition objects
1801 * Create a new JsonReader
1802 * @param {Object} meta Metadata configuration options
1803 * @param {Object} recordType Either an Array of field definition objects,
1804 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1806 Roo.data.JsonReader = function(meta, recordType){
1809 // set some defaults:
1811 totalProperty: 'total',
1812 successProperty : 'success',
1817 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1819 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1822 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
1823 * Used by Store query builder to append _requestMeta to params.
1826 metaFromRemote : false,
1828 * This method is only used by a DataProxy which has retrieved data from a remote server.
1829 * @param {Object} response The XHR object which contains the JSON data in its responseText.
1830 * @return {Object} data A data block which is used by an Roo.data.Store object as
1831 * a cache of Roo.data.Records.
1833 read : function(response){
1834 var json = response.responseText;
1836 var o = /* eval:var:o */ eval("("+json+")");
1838 throw {message: "JsonReader.read: Json object not found"};
1844 this.metaFromRemote = true;
1845 this.meta = o.metaData;
1846 this.recordType = Roo.data.Record.create(o.metaData.fields);
1847 this.onMetaChange(this.meta, this.recordType, o);
1849 return this.readRecords(o);
1852 // private function a store will implement
1853 onMetaChange : function(meta, recordType, o){
1860 simpleAccess: function(obj, subsc) {
1867 getJsonAccessor: function(){
1869 return function(expr) {
1871 return(re.test(expr))
1872 ? new Function("obj", "return obj." + expr)
1882 * Create a data block containing Roo.data.Records from an XML document.
1883 * @param {Object} o An object which contains an Array of row objects in the property specified
1884 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1885 * which contains the total size of the dataset.
1886 * @return {Object} data A data block which is used by an Roo.data.Store object as
1887 * a cache of Roo.data.Records.
1889 readRecords : function(o){
1891 * After any data loads, the raw JSON data is available for further custom processing.
1895 var s = this.meta, Record = this.recordType,
1896 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1898 // Generate extraction functions for the totalProperty, the root, the id, and for each field
1900 if(s.totalProperty) {
1901 this.getTotal = this.getJsonAccessor(s.totalProperty);
1903 if(s.successProperty) {
1904 this.getSuccess = this.getJsonAccessor(s.successProperty);
1906 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1908 var g = this.getJsonAccessor(s.id);
1909 this.getId = function(rec) {
1911 return (r === undefined || r === "") ? null : r;
1914 this.getId = function(){return null;};
1917 for(var jj = 0; jj < fl; jj++){
1919 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1920 this.ef[jj] = this.getJsonAccessor(map);
1924 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1925 if(s.totalProperty){
1926 var vt = parseInt(this.getTotal(o), 10);
1931 if(s.successProperty){
1932 var vs = this.getSuccess(o);
1933 if(vs === false || vs === 'false'){
1938 for(var i = 0; i < c; i++){
1941 var id = this.getId(n);
1942 for(var j = 0; j < fl; j++){
1944 var v = this.ef[j](n);
1946 Roo.log('missing convert for ' + f.name);
1950 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1952 var record = new Record(values, id);
1954 records[i] = record;
1960 totalRecords : totalRecords
1965 * Ext JS Library 1.1.1
1966 * Copyright(c) 2006-2007, Ext JS, LLC.
1968 * Originally Released Under LGPL - original licence link has changed is not relivant.
1971 * <script type="text/javascript">
1975 * @class Roo.data.XmlReader
1976 * @extends Roo.data.DataReader
1977 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
1978 * based on mappings in a provided Roo.data.Record constructor.<br><br>
1980 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
1981 * header in the HTTP response must be set to "text/xml".</em>
1985 var RecordDef = Roo.data.Record.create([
1986 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
1987 {name: 'occupation'} // This field will use "occupation" as the mapping.
1989 var myReader = new Roo.data.XmlReader({
1990 totalRecords: "results", // The element which contains the total dataset size (optional)
1991 record: "row", // The repeated element which contains row information
1992 id: "id" // The element within the row that provides an ID for the record (optional)
1996 * This would consume an XML file like this:
2000 <results>2</results>
2003 <name>Bill</name>
2004 <occupation>Gardener</occupation>
2008 <name>Ben</name>
2009 <occupation>Horticulturalist</occupation>
2013 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2014 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2015 * paged from the remote server.
2016 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2017 * @cfg {String} success The DomQuery path to the success attribute used by forms.
2018 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2019 * a record identifier value.
2021 * Create a new XmlReader
2022 * @param {Object} meta Metadata configuration options
2023 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
2024 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2025 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
2027 Roo.data.XmlReader = function(meta, recordType){
2029 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2031 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2033 * This method is only used by a DataProxy which has retrieved data from a remote server.
2034 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
2035 * to contain a method called 'responseXML' that returns an XML document object.
2036 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2037 * a cache of Roo.data.Records.
2039 read : function(response){
2040 var doc = response.responseXML;
2042 throw {message: "XmlReader.read: XML Document not available"};
2044 return this.readRecords(doc);
2048 * Create a data block containing Roo.data.Records from an XML document.
2049 * @param {Object} doc A parsed XML document.
2050 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2051 * a cache of Roo.data.Records.
2053 readRecords : function(doc){
2055 * After any data loads/reads, the raw XML Document is available for further custom processing.
2059 var root = doc.documentElement || doc;
2060 var q = Roo.DomQuery;
2061 var recordType = this.recordType, fields = recordType.prototype.fields;
2062 var sid = this.meta.id;
2063 var totalRecords = 0, success = true;
2064 if(this.meta.totalRecords){
2065 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2068 if(this.meta.success){
2069 var sv = q.selectValue(this.meta.success, root, true);
2070 success = sv !== false && sv !== 'false';
2073 var ns = q.select(this.meta.record, root);
2074 for(var i = 0, len = ns.length; i < len; i++) {
2077 var id = sid ? q.selectValue(sid, n) : undefined;
2078 for(var j = 0, jlen = fields.length; j < jlen; j++){
2079 var f = fields.items[j];
2080 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2084 var record = new recordType(values, id);
2086 records[records.length] = record;
2092 totalRecords : totalRecords || records.length
2097 * Ext JS Library 1.1.1
2098 * Copyright(c) 2006-2007, Ext JS, LLC.
2100 * Originally Released Under LGPL - original licence link has changed is not relivant.
2103 * <script type="text/javascript">
2107 * @class Roo.data.ArrayReader
2108 * @extends Roo.data.DataReader
2109 * Data reader class to create an Array of Roo.data.Record objects from an Array.
2110 * Each element of that Array represents a row of data fields. The
2111 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2112 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2116 var RecordDef = Roo.data.Record.create([
2117 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
2118 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
2120 var myReader = new Roo.data.ArrayReader({
2121 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
2125 * This would consume an Array like this:
2127 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2129 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
2131 * Create a new JsonReader
2132 * @param {Object} meta Metadata configuration options.
2133 * @param {Object} recordType Either an Array of field definition objects
2134 * as specified to {@link Roo.data.Record#create},
2135 * or an {@link Roo.data.Record} object
2136 * created using {@link Roo.data.Record#create}.
2138 Roo.data.ArrayReader = function(meta, recordType){
2139 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
2142 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2144 * Create a data block containing Roo.data.Records from an XML document.
2145 * @param {Object} o An Array of row objects which represents the dataset.
2146 * @return {Object} data A data block which is used by an Roo.data.Store object as
2147 * a cache of Roo.data.Records.
2149 readRecords : function(o){
2150 var sid = this.meta ? this.meta.id : null;
2151 var recordType = this.recordType, fields = recordType.prototype.fields;
2154 for(var i = 0; i < root.length; i++){
2157 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2158 for(var j = 0, jlen = fields.length; j < jlen; j++){
2159 var f = fields.items[j];
2160 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2161 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2165 var record = new recordType(values, id);
2167 records[records.length] = record;
2171 totalRecords : records.length
2176 * Ext JS Library 1.1.1
2177 * Copyright(c) 2006-2007, Ext JS, LLC.
2179 * Originally Released Under LGPL - original licence link has changed is not relivant.
2182 * <script type="text/javascript">
2187 * @class Roo.data.Tree
2188 * @extends Roo.util.Observable
2189 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2190 * in the tree have most standard DOM functionality.
2192 * @param {Node} root (optional) The root node
2194 Roo.data.Tree = function(root){
2197 * The root node for this tree
2202 this.setRootNode(root);
2207 * Fires when a new child node is appended to a node in this tree.
2208 * @param {Tree} tree The owner tree
2209 * @param {Node} parent The parent node
2210 * @param {Node} node The newly appended node
2211 * @param {Number} index The index of the newly appended node
2216 * Fires when a child node is removed from a node in this tree.
2217 * @param {Tree} tree The owner tree
2218 * @param {Node} parent The parent node
2219 * @param {Node} node The child node removed
2224 * Fires when a node is moved to a new location in the tree
2225 * @param {Tree} tree The owner tree
2226 * @param {Node} node The node moved
2227 * @param {Node} oldParent The old parent of this node
2228 * @param {Node} newParent The new parent of this node
2229 * @param {Number} index The index it was moved to
2234 * Fires when a new child node is inserted in a node in this tree.
2235 * @param {Tree} tree The owner tree
2236 * @param {Node} parent The parent node
2237 * @param {Node} node The child node inserted
2238 * @param {Node} refNode The child node the node was inserted before
2242 * @event beforeappend
2243 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2244 * @param {Tree} tree The owner tree
2245 * @param {Node} parent The parent node
2246 * @param {Node} node The child node to be appended
2248 "beforeappend" : true,
2250 * @event beforeremove
2251 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2252 * @param {Tree} tree The owner tree
2253 * @param {Node} parent The parent node
2254 * @param {Node} node The child node to be removed
2256 "beforeremove" : true,
2259 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2260 * @param {Tree} tree The owner tree
2261 * @param {Node} node The node being moved
2262 * @param {Node} oldParent The parent of the node
2263 * @param {Node} newParent The new parent the node is moving to
2264 * @param {Number} index The index it is being moved to
2266 "beforemove" : true,
2268 * @event beforeinsert
2269 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2270 * @param {Tree} tree The owner tree
2271 * @param {Node} parent The parent node
2272 * @param {Node} node The child node to be inserted
2273 * @param {Node} refNode The child node the node is being inserted before
2275 "beforeinsert" : true
2278 Roo.data.Tree.superclass.constructor.call(this);
2281 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2284 proxyNodeEvent : function(){
2285 return this.fireEvent.apply(this, arguments);
2289 * Returns the root node for this tree.
2292 getRootNode : function(){
2297 * Sets the root node for this tree.
2298 * @param {Node} node
2301 setRootNode : function(node){
2303 node.ownerTree = this;
2305 this.registerNode(node);
2310 * Gets a node in this tree by its id.
2311 * @param {String} id
2314 getNodeById : function(id){
2315 return this.nodeHash[id];
2318 registerNode : function(node){
2319 this.nodeHash[node.id] = node;
2322 unregisterNode : function(node){
2323 delete this.nodeHash[node.id];
2326 toString : function(){
2327 return "[Tree"+(this.id?" "+this.id:"")+"]";
2332 * @class Roo.data.Node
2333 * @extends Roo.util.Observable
2334 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2335 * @cfg {String} id The id for this node. If one is not specified, one is generated.
2337 * @param {Object} attributes The attributes/config for the node
2339 Roo.data.Node = function(attributes){
2341 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2344 this.attributes = attributes || {};
2345 this.leaf = this.attributes.leaf;
2347 * The node id. @type String
2349 this.id = this.attributes.id;
2351 this.id = Roo.id(null, "ynode-");
2352 this.attributes.id = this.id;
2357 * All child nodes of this node. @type Array
2359 this.childNodes = [];
2360 if(!this.childNodes.indexOf){ // indexOf is a must
2361 this.childNodes.indexOf = function(o){
2362 for(var i = 0, len = this.length; i < len; i++){
2371 * The parent node for this node. @type Node
2373 this.parentNode = null;
2375 * The first direct child node of this node, or null if this node has no child nodes. @type Node
2377 this.firstChild = null;
2379 * The last direct child node of this node, or null if this node has no child nodes. @type Node
2381 this.lastChild = null;
2383 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2385 this.previousSibling = null;
2387 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2389 this.nextSibling = null;
2394 * Fires when a new child node is appended
2395 * @param {Tree} tree The owner tree
2396 * @param {Node} this This node
2397 * @param {Node} node The newly appended node
2398 * @param {Number} index The index of the newly appended node
2403 * Fires when a child node is removed
2404 * @param {Tree} tree The owner tree
2405 * @param {Node} this This node
2406 * @param {Node} node The removed node
2411 * Fires when this node is moved to a new location in the tree
2412 * @param {Tree} tree The owner tree
2413 * @param {Node} this This node
2414 * @param {Node} oldParent The old parent of this node
2415 * @param {Node} newParent The new parent of this node
2416 * @param {Number} index The index it was moved to
2421 * Fires when a new child node is inserted.
2422 * @param {Tree} tree The owner tree
2423 * @param {Node} this This node
2424 * @param {Node} node The child node inserted
2425 * @param {Node} refNode The child node the node was inserted before
2429 * @event beforeappend
2430 * Fires before a new child is appended, return false to cancel the append.
2431 * @param {Tree} tree The owner tree
2432 * @param {Node} this This node
2433 * @param {Node} node The child node to be appended
2435 "beforeappend" : true,
2437 * @event beforeremove
2438 * Fires before a child is removed, return false to cancel the remove.
2439 * @param {Tree} tree The owner tree
2440 * @param {Node} this This node
2441 * @param {Node} node The child node to be removed
2443 "beforeremove" : true,
2446 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2447 * @param {Tree} tree The owner tree
2448 * @param {Node} this This node
2449 * @param {Node} oldParent The parent of this node
2450 * @param {Node} newParent The new parent this node is moving to
2451 * @param {Number} index The index it is being moved to
2453 "beforemove" : true,
2455 * @event beforeinsert
2456 * Fires before a new child is inserted, return false to cancel the insert.
2457 * @param {Tree} tree The owner tree
2458 * @param {Node} this This node
2459 * @param {Node} node The child node to be inserted
2460 * @param {Node} refNode The child node the node is being inserted before
2462 "beforeinsert" : true
2464 this.listeners = this.attributes.listeners;
2465 Roo.data.Node.superclass.constructor.call(this);
2468 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2469 fireEvent : function(evtName){
2470 // first do standard event for this node
2471 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2474 // then bubble it up to the tree if the event wasn't cancelled
2475 var ot = this.getOwnerTree();
2477 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2485 * Returns true if this node is a leaf
2488 isLeaf : function(){
2489 return this.leaf === true;
2493 setFirstChild : function(node){
2494 this.firstChild = node;
2498 setLastChild : function(node){
2499 this.lastChild = node;
2504 * Returns true if this node is the last child of its parent
2507 isLast : function(){
2508 return (!this.parentNode ? true : this.parentNode.lastChild == this);
2512 * Returns true if this node is the first child of its parent
2515 isFirst : function(){
2516 return (!this.parentNode ? true : this.parentNode.firstChild == this);
2519 hasChildNodes : function(){
2520 return !this.isLeaf() && this.childNodes.length > 0;
2524 * Insert node(s) as the last child node of this node.
2525 * @param {Node/Array} node The node or Array of nodes to append
2526 * @return {Node} The appended node if single append, or null if an array was passed
2528 appendChild : function(node){
2530 if(node instanceof Array){
2532 }else if(arguments.length > 1){
2535 // if passed an array or multiple args do them one by one
2537 for(var i = 0, len = multi.length; i < len; i++) {
2538 this.appendChild(multi[i]);
2541 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2544 var index = this.childNodes.length;
2545 var oldParent = node.parentNode;
2546 // it's a move, make sure we move it cleanly
2548 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2551 oldParent.removeChild(node);
2553 index = this.childNodes.length;
2555 this.setFirstChild(node);
2557 this.childNodes.push(node);
2558 node.parentNode = this;
2559 var ps = this.childNodes[index-1];
2561 node.previousSibling = ps;
2562 ps.nextSibling = node;
2564 node.previousSibling = null;
2566 node.nextSibling = null;
2567 this.setLastChild(node);
2568 node.setOwnerTree(this.getOwnerTree());
2569 this.fireEvent("append", this.ownerTree, this, node, index);
2571 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2578 * Removes a child node from this node.
2579 * @param {Node} node The node to remove
2580 * @return {Node} The removed node
2582 removeChild : function(node){
2583 var index = this.childNodes.indexOf(node);
2587 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2591 // remove it from childNodes collection
2592 this.childNodes.splice(index, 1);
2595 if(node.previousSibling){
2596 node.previousSibling.nextSibling = node.nextSibling;
2598 if(node.nextSibling){
2599 node.nextSibling.previousSibling = node.previousSibling;
2602 // update child refs
2603 if(this.firstChild == node){
2604 this.setFirstChild(node.nextSibling);
2606 if(this.lastChild == node){
2607 this.setLastChild(node.previousSibling);
2610 node.setOwnerTree(null);
2611 // clear any references from the node
2612 node.parentNode = null;
2613 node.previousSibling = null;
2614 node.nextSibling = null;
2615 this.fireEvent("remove", this.ownerTree, this, node);
2620 * Inserts the first node before the second node in this nodes childNodes collection.
2621 * @param {Node} node The node to insert
2622 * @param {Node} refNode The node to insert before (if null the node is appended)
2623 * @return {Node} The inserted node
2625 insertBefore : function(node, refNode){
2626 if(!refNode){ // like standard Dom, refNode can be null for append
2627 return this.appendChild(node);
2630 if(node == refNode){
2634 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2637 var index = this.childNodes.indexOf(refNode);
2638 var oldParent = node.parentNode;
2639 var refIndex = index;
2641 // when moving internally, indexes will change after remove
2642 if(oldParent == this && this.childNodes.indexOf(node) < index){
2646 // it's a move, make sure we move it cleanly
2648 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2651 oldParent.removeChild(node);
2654 this.setFirstChild(node);
2656 this.childNodes.splice(refIndex, 0, node);
2657 node.parentNode = this;
2658 var ps = this.childNodes[refIndex-1];
2660 node.previousSibling = ps;
2661 ps.nextSibling = node;
2663 node.previousSibling = null;
2665 node.nextSibling = refNode;
2666 refNode.previousSibling = node;
2667 node.setOwnerTree(this.getOwnerTree());
2668 this.fireEvent("insert", this.ownerTree, this, node, refNode);
2670 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2676 * Returns the child node at the specified index.
2677 * @param {Number} index
2680 item : function(index){
2681 return this.childNodes[index];
2685 * Replaces one child node in this node with another.
2686 * @param {Node} newChild The replacement node
2687 * @param {Node} oldChild The node to replace
2688 * @return {Node} The replaced node
2690 replaceChild : function(newChild, oldChild){
2691 this.insertBefore(newChild, oldChild);
2692 this.removeChild(oldChild);
2697 * Returns the index of a child node
2698 * @param {Node} node
2699 * @return {Number} The index of the node or -1 if it was not found
2701 indexOf : function(child){
2702 return this.childNodes.indexOf(child);
2706 * Returns the tree this node is in.
2709 getOwnerTree : function(){
2710 // if it doesn't have one, look for one
2711 if(!this.ownerTree){
2715 this.ownerTree = p.ownerTree;
2721 return this.ownerTree;
2725 * Returns depth of this node (the root node has a depth of 0)
2728 getDepth : function(){
2731 while(p.parentNode){
2739 setOwnerTree : function(tree){
2740 // if it's move, we need to update everyone
2741 if(tree != this.ownerTree){
2743 this.ownerTree.unregisterNode(this);
2745 this.ownerTree = tree;
2746 var cs = this.childNodes;
2747 for(var i = 0, len = cs.length; i < len; i++) {
2748 cs[i].setOwnerTree(tree);
2751 tree.registerNode(this);
2757 * Returns the path for this node. The path can be used to expand or select this node programmatically.
2758 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2759 * @return {String} The path
2761 getPath : function(attr){
2762 attr = attr || "id";
2763 var p = this.parentNode;
2764 var b = [this.attributes[attr]];
2766 b.unshift(p.attributes[attr]);
2769 var sep = this.getOwnerTree().pathSeparator;
2770 return sep + b.join(sep);
2774 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2775 * function call will be the scope provided or the current node. The arguments to the function
2776 * will be the args provided or the current node. If the function returns false at any point,
2777 * the bubble is stopped.
2778 * @param {Function} fn The function to call
2779 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2780 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2782 bubble : function(fn, scope, args){
2785 if(fn.call(scope || p, args || p) === false){
2793 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2794 * function call will be the scope provided or the current node. The arguments to the function
2795 * will be the args provided or the current node. If the function returns false at any point,
2796 * the cascade is stopped on that branch.
2797 * @param {Function} fn The function to call
2798 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2799 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2801 cascade : function(fn, scope, args){
2802 if(fn.call(scope || this, args || this) !== false){
2803 var cs = this.childNodes;
2804 for(var i = 0, len = cs.length; i < len; i++) {
2805 cs[i].cascade(fn, scope, args);
2811 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2812 * function call will be the scope provided or the current node. The arguments to the function
2813 * will be the args provided or the current node. If the function returns false at any point,
2814 * the iteration stops.
2815 * @param {Function} fn The function to call
2816 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2817 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2819 eachChild : function(fn, scope, args){
2820 var cs = this.childNodes;
2821 for(var i = 0, len = cs.length; i < len; i++) {
2822 if(fn.call(scope || this, args || cs[i]) === false){
2829 * Finds the first child that has the attribute with the specified value.
2830 * @param {String} attribute The attribute name
2831 * @param {Mixed} value The value to search for
2832 * @return {Node} The found child or null if none was found
2834 findChild : function(attribute, value){
2835 var cs = this.childNodes;
2836 for(var i = 0, len = cs.length; i < len; i++) {
2837 if(cs[i].attributes[attribute] == value){
2845 * Finds the first child by a custom function. The child matches if the function passed
2847 * @param {Function} fn
2848 * @param {Object} scope (optional)
2849 * @return {Node} The found child or null if none was found
2851 findChildBy : function(fn, scope){
2852 var cs = this.childNodes;
2853 for(var i = 0, len = cs.length; i < len; i++) {
2854 if(fn.call(scope||cs[i], cs[i]) === true){
2862 * Sorts this nodes children using the supplied sort function
2863 * @param {Function} fn
2864 * @param {Object} scope (optional)
2866 sort : function(fn, scope){
2867 var cs = this.childNodes;
2868 var len = cs.length;
2870 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2872 for(var i = 0; i < len; i++){
2874 n.previousSibling = cs[i-1];
2875 n.nextSibling = cs[i+1];
2877 this.setFirstChild(n);
2880 this.setLastChild(n);
2887 * Returns true if this node is an ancestor (at any point) of the passed node.
2888 * @param {Node} node
2891 contains : function(node){
2892 return node.isAncestor(this);
2896 * Returns true if the passed node is an ancestor (at any point) of this node.
2897 * @param {Node} node
2900 isAncestor : function(node){
2901 var p = this.parentNode;
2911 toString : function(){
2912 return "[Node"+(this.id?" "+this.id:"")+"]";
2916 * Ext JS Library 1.1.1
2917 * Copyright(c) 2006-2007, Ext JS, LLC.
2919 * Originally Released Under LGPL - original licence link has changed is not relivant.
2922 * <script type="text/javascript">
2927 * @extends Roo.Element
2928 * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2929 * automatic maintaining of shadow/shim positions.
2930 * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2931 * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2932 * you can pass a string with a CSS class name. False turns off the shadow.
2933 * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2934 * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2935 * @cfg {String} cls CSS class to add to the element
2936 * @cfg {Number} zindex Starting z-index (defaults to 11000)
2937 * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2939 * @param {Object} config An object with config options.
2940 * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
2943 Roo.Layer = function(config, existingEl){
2944 config = config || {};
2945 var dh = Roo.DomHelper;
2946 var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
2948 this.dom = Roo.getDom(existingEl);
2951 var o = config.dh || {tag: "div", cls: "x-layer"};
2952 this.dom = dh.append(pel, o);
2955 this.addClass(config.cls);
2957 this.constrain = config.constrain !== false;
2958 this.visibilityMode = Roo.Element.VISIBILITY;
2960 this.id = this.dom.id = config.id;
2962 this.id = Roo.id(this.dom);
2964 this.zindex = config.zindex || this.getZIndex();
2965 this.position("absolute", this.zindex);
2967 this.shadowOffset = config.shadowOffset || 4;
2968 this.shadow = new Roo.Shadow({
2969 offset : this.shadowOffset,
2970 mode : config.shadow
2973 this.shadowOffset = 0;
2975 this.useShim = config.shim !== false && Roo.useShims;
2976 this.useDisplay = config.useDisplay;
2980 var supr = Roo.Element.prototype;
2982 // shims are shared among layer to keep from having 100 iframes
2985 Roo.extend(Roo.Layer, Roo.Element, {
2987 getZIndex : function(){
2988 return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
2991 getShim : function(){
2998 var shim = shims.shift();
3000 shim = this.createShim();
3001 shim.enableDisplayMode('block');
3002 shim.dom.style.display = 'none';
3003 shim.dom.style.visibility = 'visible';
3005 var pn = this.dom.parentNode;
3006 if(shim.dom.parentNode != pn){
3007 pn.insertBefore(shim.dom, this.dom);
3009 shim.setStyle('z-index', this.getZIndex()-2);
3014 hideShim : function(){
3016 this.shim.setDisplayed(false);
3017 shims.push(this.shim);
3022 disableShadow : function(){
3024 this.shadowDisabled = true;
3026 this.lastShadowOffset = this.shadowOffset;
3027 this.shadowOffset = 0;
3031 enableShadow : function(show){
3033 this.shadowDisabled = false;
3034 this.shadowOffset = this.lastShadowOffset;
3035 delete this.lastShadowOffset;
3043 // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3044 // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3045 sync : function(doShow){
3046 var sw = this.shadow;
3047 if(!this.updating && this.isVisible() && (sw || this.useShim)){
3048 var sh = this.getShim();
3050 var w = this.getWidth(),
3051 h = this.getHeight();
3053 var l = this.getLeft(true),
3054 t = this.getTop(true);
3056 if(sw && !this.shadowDisabled){
3057 if(doShow && !sw.isVisible()){
3060 sw.realign(l, t, w, h);
3066 // fit the shim behind the shadow, so it is shimmed too
3067 var a = sw.adjusts, s = sh.dom.style;
3068 s.left = (Math.min(l, l+a.l))+"px";
3069 s.top = (Math.min(t, t+a.t))+"px";
3070 s.width = (w+a.w)+"px";
3071 s.height = (h+a.h)+"px";
3078 sh.setLeftTop(l, t);
3085 destroy : function(){
3090 this.removeAllListeners();
3091 var pn = this.dom.parentNode;
3093 pn.removeChild(this.dom);
3095 Roo.Element.uncache(this.id);
3098 remove : function(){
3103 beginUpdate : function(){
3104 this.updating = true;
3108 endUpdate : function(){
3109 this.updating = false;
3114 hideUnders : function(negOffset){
3122 constrainXY : function(){
3124 var vw = Roo.lib.Dom.getViewWidth(),
3125 vh = Roo.lib.Dom.getViewHeight();
3126 var s = Roo.get(document).getScroll();
3128 var xy = this.getXY();
3129 var x = xy[0], y = xy[1];
3130 var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3131 // only move it if it needs it
3133 // first validate right/bottom
3134 if((x + w) > vw+s.left){
3135 x = vw - w - this.shadowOffset;
3138 if((y + h) > vh+s.top){
3139 y = vh - h - this.shadowOffset;
3142 // then make sure top/left isn't negative
3153 var ay = this.avoidY;
3154 if(y <= ay && (y+h) >= ay){
3160 supr.setXY.call(this, xy);
3166 isVisible : function(){
3167 return this.visible;
3171 showAction : function(){
3172 this.visible = true; // track visibility to prevent getStyle calls
3173 if(this.useDisplay === true){
3174 this.setDisplayed("");
3175 }else if(this.lastXY){
3176 supr.setXY.call(this, this.lastXY);
3177 }else if(this.lastLT){
3178 supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3183 hideAction : function(){
3184 this.visible = false;
3185 if(this.useDisplay === true){
3186 this.setDisplayed(false);
3188 this.setLeftTop(-10000,-10000);
3192 // overridden Element method
3193 setVisible : function(v, a, d, c, e){
3198 var cb = function(){
3203 }.createDelegate(this);
3204 supr.setVisible.call(this, true, true, d, cb, e);
3207 this.hideUnders(true);
3216 }.createDelegate(this);
3218 supr.setVisible.call(this, v, a, d, cb, e);
3227 storeXY : function(xy){
3232 storeLeftTop : function(left, top){
3234 this.lastLT = [left, top];
3238 beforeFx : function(){
3239 this.beforeAction();
3240 return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3244 afterFx : function(){
3245 Roo.Layer.superclass.afterFx.apply(this, arguments);
3246 this.sync(this.isVisible());
3250 beforeAction : function(){
3251 if(!this.updating && this.shadow){
3256 // overridden Element method
3257 setLeft : function(left){
3258 this.storeLeftTop(left, this.getTop(true));
3259 supr.setLeft.apply(this, arguments);
3263 setTop : function(top){
3264 this.storeLeftTop(this.getLeft(true), top);
3265 supr.setTop.apply(this, arguments);
3269 setLeftTop : function(left, top){
3270 this.storeLeftTop(left, top);
3271 supr.setLeftTop.apply(this, arguments);
3275 setXY : function(xy, a, d, c, e){
3277 this.beforeAction();
3279 var cb = this.createCB(c);
3280 supr.setXY.call(this, xy, a, d, cb, e);
3287 createCB : function(c){
3298 // overridden Element method
3299 setX : function(x, a, d, c, e){
3300 this.setXY([x, this.getY()], a, d, c, e);
3303 // overridden Element method
3304 setY : function(y, a, d, c, e){
3305 this.setXY([this.getX(), y], a, d, c, e);
3308 // overridden Element method
3309 setSize : function(w, h, a, d, c, e){
3310 this.beforeAction();
3311 var cb = this.createCB(c);
3312 supr.setSize.call(this, w, h, a, d, cb, e);
3318 // overridden Element method
3319 setWidth : function(w, a, d, c, e){
3320 this.beforeAction();
3321 var cb = this.createCB(c);
3322 supr.setWidth.call(this, w, a, d, cb, e);
3328 // overridden Element method
3329 setHeight : function(h, a, d, c, e){
3330 this.beforeAction();
3331 var cb = this.createCB(c);
3332 supr.setHeight.call(this, h, a, d, cb, e);
3338 // overridden Element method
3339 setBounds : function(x, y, w, h, a, d, c, e){
3340 this.beforeAction();
3341 var cb = this.createCB(c);
3343 this.storeXY([x, y]);
3344 supr.setXY.call(this, [x, y]);
3345 supr.setSize.call(this, w, h, a, d, cb, e);
3348 supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3354 * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3355 * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3356 * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3357 * @param {Number} zindex The new z-index to set
3358 * @return {this} The Layer
3360 setZIndex : function(zindex){
3361 this.zindex = zindex;
3362 this.setStyle("z-index", zindex + 2);
3364 this.shadow.setZIndex(zindex + 1);
3367 this.shim.setStyle("z-index", zindex);
3373 * Ext JS Library 1.1.1
3374 * Copyright(c) 2006-2007, Ext JS, LLC.
3376 * Originally Released Under LGPL - original licence link has changed is not relivant.
3379 * <script type="text/javascript">
3385 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
3386 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
3387 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3389 * Create a new Shadow
3390 * @param {Object} config The config object
3392 Roo.Shadow = function(config){
3393 Roo.apply(this, config);
3394 if(typeof this.mode != "string"){
3395 this.mode = this.defaultMode;
3397 var o = this.offset, a = {h: 0};
3398 var rad = Math.floor(this.offset/2);
3399 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3405 a.l -= this.offset + rad;
3406 a.t -= this.offset + rad;
3417 a.l -= (this.offset - rad);
3418 a.t -= this.offset + rad;
3420 a.w -= (this.offset - rad)*2;
3431 a.l -= (this.offset - rad);
3432 a.t -= (this.offset - rad);
3434 a.w -= (this.offset + rad + 1);
3435 a.h -= (this.offset + rad);
3444 Roo.Shadow.prototype = {
3446 * @cfg {String} mode
3447 * The shadow display mode. Supports the following options:<br />
3448 * sides: Shadow displays on both sides and bottom only<br />
3449 * frame: Shadow displays equally on all four sides<br />
3450 * drop: Traditional bottom-right drop shadow (default)
3453 * @cfg {String} offset
3454 * The number of pixels to offset the shadow from the element (defaults to 4)
3459 defaultMode: "drop",
3462 * Displays the shadow under the target element
3463 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3465 show : function(target){
3466 target = Roo.get(target);
3468 this.el = Roo.Shadow.Pool.pull();
3469 if(this.el.dom.nextSibling != target.dom){
3470 this.el.insertBefore(target);
3473 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3475 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3478 target.getLeft(true),
3479 target.getTop(true),
3483 this.el.dom.style.display = "block";
3487 * Returns true if the shadow is visible, else false
3489 isVisible : function(){
3490 return this.el ? true : false;
3494 * Direct alignment when values are already available. Show must be called at least once before
3495 * calling this method to ensure it is initialized.
3496 * @param {Number} left The target element left position
3497 * @param {Number} top The target element top position
3498 * @param {Number} width The target element width
3499 * @param {Number} height The target element height
3501 realign : function(l, t, w, h){
3505 var a = this.adjusts, d = this.el.dom, s = d.style;
3507 s.left = (l+a.l)+"px";
3508 s.top = (t+a.t)+"px";
3509 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3511 if(s.width != sws || s.height != shs){
3515 var cn = d.childNodes;
3516 var sww = Math.max(0, (sw-12))+"px";
3517 cn[0].childNodes[1].style.width = sww;
3518 cn[1].childNodes[1].style.width = sww;
3519 cn[2].childNodes[1].style.width = sww;
3520 cn[1].style.height = Math.max(0, (sh-12))+"px";
3530 this.el.dom.style.display = "none";
3531 Roo.Shadow.Pool.push(this.el);
3537 * Adjust the z-index of this shadow
3538 * @param {Number} zindex The new z-index
3540 setZIndex : function(z){
3543 this.el.setStyle("z-index", z);
3548 // Private utility class that manages the internal Shadow cache
3549 Roo.Shadow.Pool = function(){
3551 var markup = Roo.isIE ?
3552 '<div class="x-ie-shadow"></div>' :
3553 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
3558 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3559 sh.autoBoxAdjust = false;
3564 push : function(sh){
3570 * Ext JS Library 1.1.1
3571 * Copyright(c) 2006-2007, Ext JS, LLC.
3573 * Originally Released Under LGPL - original licence link has changed is not relivant.
3576 * <script type="text/javascript">
3581 * @class Roo.SplitBar
3582 * @extends Roo.util.Observable
3583 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3587 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3588 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3589 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3590 split.minSize = 100;
3591 split.maxSize = 600;
3592 split.animate = true;
3593 split.on('moved', splitterMoved);
3596 * Create a new SplitBar
3597 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
3598 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
3599 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3600 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
3601 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3602 position of the SplitBar).
3604 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3607 this.el = Roo.get(dragElement, true);
3608 this.el.dom.unselectable = "on";
3610 this.resizingEl = Roo.get(resizingElement, true);
3614 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3615 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3618 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3621 * The minimum size of the resizing element. (Defaults to 0)
3627 * The maximum size of the resizing element. (Defaults to 2000)
3630 this.maxSize = 2000;
3633 * Whether to animate the transition to the new size
3636 this.animate = false;
3639 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3642 this.useShim = false;
3649 this.proxy = Roo.SplitBar.createProxy(this.orientation);
3651 this.proxy = Roo.get(existingProxy).dom;
3654 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3657 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3660 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3663 this.dragSpecs = {};
3666 * @private The adapter to use to positon and resize elements
3668 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3669 this.adapter.init(this);
3671 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3673 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3674 this.el.addClass("x-splitbar-h");
3677 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3678 this.el.addClass("x-splitbar-v");
3684 * Fires when the splitter is moved (alias for {@link #event-moved})
3685 * @param {Roo.SplitBar} this
3686 * @param {Number} newSize the new width or height
3691 * Fires when the splitter is moved
3692 * @param {Roo.SplitBar} this
3693 * @param {Number} newSize the new width or height
3697 * @event beforeresize
3698 * Fires before the splitter is dragged
3699 * @param {Roo.SplitBar} this
3701 "beforeresize" : true,
3703 "beforeapply" : true
3706 Roo.util.Observable.call(this);
3709 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3710 onStartProxyDrag : function(x, y){
3711 this.fireEvent("beforeresize", this);
3713 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
3715 o.enableDisplayMode("block");
3716 // all splitbars share the same overlay
3717 Roo.SplitBar.prototype.overlay = o;
3719 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3720 this.overlay.show();
3721 Roo.get(this.proxy).setDisplayed("block");
3722 var size = this.adapter.getElementSize(this);
3723 this.activeMinSize = this.getMinimumSize();;
3724 this.activeMaxSize = this.getMaximumSize();;
3725 var c1 = size - this.activeMinSize;
3726 var c2 = Math.max(this.activeMaxSize - size, 0);
3727 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3728 this.dd.resetConstraints();
3729 this.dd.setXConstraint(
3730 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
3731 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3733 this.dd.setYConstraint(0, 0);
3735 this.dd.resetConstraints();
3736 this.dd.setXConstraint(0, 0);
3737 this.dd.setYConstraint(
3738 this.placement == Roo.SplitBar.TOP ? c1 : c2,
3739 this.placement == Roo.SplitBar.TOP ? c2 : c1
3742 this.dragSpecs.startSize = size;
3743 this.dragSpecs.startPoint = [x, y];
3744 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3748 * @private Called after the drag operation by the DDProxy
3750 onEndProxyDrag : function(e){
3751 Roo.get(this.proxy).setDisplayed(false);
3752 var endPoint = Roo.lib.Event.getXY(e);
3754 this.overlay.hide();
3757 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3758 newSize = this.dragSpecs.startSize +
3759 (this.placement == Roo.SplitBar.LEFT ?
3760 endPoint[0] - this.dragSpecs.startPoint[0] :
3761 this.dragSpecs.startPoint[0] - endPoint[0]
3764 newSize = this.dragSpecs.startSize +
3765 (this.placement == Roo.SplitBar.TOP ?
3766 endPoint[1] - this.dragSpecs.startPoint[1] :
3767 this.dragSpecs.startPoint[1] - endPoint[1]
3770 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3771 if(newSize != this.dragSpecs.startSize){
3772 if(this.fireEvent('beforeapply', this, newSize) !== false){
3773 this.adapter.setElementSize(this, newSize);
3774 this.fireEvent("moved", this, newSize);
3775 this.fireEvent("resize", this, newSize);
3781 * Get the adapter this SplitBar uses
3782 * @return The adapter object
3784 getAdapter : function(){
3785 return this.adapter;
3789 * Set the adapter this SplitBar uses
3790 * @param {Object} adapter A SplitBar adapter object
3792 setAdapter : function(adapter){
3793 this.adapter = adapter;
3794 this.adapter.init(this);
3798 * Gets the minimum size for the resizing element
3799 * @return {Number} The minimum size
3801 getMinimumSize : function(){
3802 return this.minSize;
3806 * Sets the minimum size for the resizing element
3807 * @param {Number} minSize The minimum size
3809 setMinimumSize : function(minSize){
3810 this.minSize = minSize;
3814 * Gets the maximum size for the resizing element
3815 * @return {Number} The maximum size
3817 getMaximumSize : function(){
3818 return this.maxSize;
3822 * Sets the maximum size for the resizing element
3823 * @param {Number} maxSize The maximum size
3825 setMaximumSize : function(maxSize){
3826 this.maxSize = maxSize;
3830 * Sets the initialize size for the resizing element
3831 * @param {Number} size The initial size
3833 setCurrentSize : function(size){
3834 var oldAnimate = this.animate;
3835 this.animate = false;
3836 this.adapter.setElementSize(this, size);
3837 this.animate = oldAnimate;
3841 * Destroy this splitbar.
3842 * @param {Boolean} removeEl True to remove the element
3844 destroy : function(removeEl){
3849 this.proxy.parentNode.removeChild(this.proxy);
3857 * @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.
3859 Roo.SplitBar.createProxy = function(dir){
3860 var proxy = new Roo.Element(document.createElement("div"));
3861 proxy.unselectable();
3862 var cls = 'x-splitbar-proxy';
3863 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3864 document.body.appendChild(proxy.dom);
3869 * @class Roo.SplitBar.BasicLayoutAdapter
3870 * Default Adapter. It assumes the splitter and resizing element are not positioned
3871 * elements and only gets/sets the width of the element. Generally used for table based layouts.
3873 Roo.SplitBar.BasicLayoutAdapter = function(){
3876 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3877 // do nothing for now
3882 * Called before drag operations to get the current size of the resizing element.
3883 * @param {Roo.SplitBar} s The SplitBar using this adapter
3885 getElementSize : function(s){
3886 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3887 return s.resizingEl.getWidth();
3889 return s.resizingEl.getHeight();
3894 * Called after drag operations to set the size of the resizing element.
3895 * @param {Roo.SplitBar} s The SplitBar using this adapter
3896 * @param {Number} newSize The new size to set
3897 * @param {Function} onComplete A function to be invoked when resizing is complete
3899 setElementSize : function(s, newSize, onComplete){
3900 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3902 s.resizingEl.setWidth(newSize);
3904 onComplete(s, newSize);
3907 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3912 s.resizingEl.setHeight(newSize);
3914 onComplete(s, newSize);
3917 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3924 *@class Roo.SplitBar.AbsoluteLayoutAdapter
3925 * @extends Roo.SplitBar.BasicLayoutAdapter
3926 * Adapter that moves the splitter element to align with the resized sizing element.
3927 * Used with an absolute positioned SplitBar.
3928 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3929 * document.body, make sure you assign an id to the body element.
3931 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3932 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3933 this.container = Roo.get(container);
3936 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3941 getElementSize : function(s){
3942 return this.basic.getElementSize(s);
3945 setElementSize : function(s, newSize, onComplete){
3946 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3949 moveSplitter : function(s){
3950 var yes = Roo.SplitBar;
3951 switch(s.placement){
3953 s.el.setX(s.resizingEl.getRight());
3956 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3959 s.el.setY(s.resizingEl.getBottom());
3962 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3969 * Orientation constant - Create a vertical SplitBar
3973 Roo.SplitBar.VERTICAL = 1;
3976 * Orientation constant - Create a horizontal SplitBar
3980 Roo.SplitBar.HORIZONTAL = 2;
3983 * Placement constant - The resizing element is to the left of the splitter element
3987 Roo.SplitBar.LEFT = 1;
3990 * Placement constant - The resizing element is to the right of the splitter element
3994 Roo.SplitBar.RIGHT = 2;
3997 * Placement constant - The resizing element is positioned above the splitter element
4001 Roo.SplitBar.TOP = 3;
4004 * Placement constant - The resizing element is positioned under splitter element
4008 Roo.SplitBar.BOTTOM = 4;
4011 * Ext JS Library 1.1.1
4012 * Copyright(c) 2006-2007, Ext JS, LLC.
4014 * Originally Released Under LGPL - original licence link has changed is not relivant.
4017 * <script type="text/javascript">
4022 * @extends Roo.util.Observable
4023 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
4024 * This class also supports single and multi selection modes. <br>
4025 * Create a data model bound view:
4027 var store = new Roo.data.Store(...);
4029 var view = new Roo.View({
4031 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
4034 selectedClass: "ydataview-selected",
4038 // listen for node click?
4039 view.on("click", function(vw, index, node, e){
4040 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4044 dataModel.load("foobar.xml");
4046 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4048 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4049 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4051 * Note: old style constructor is still suported (container, template, config)
4055 * @param {Object} config The config object
4058 Roo.View = function(config, depreciated_tpl, depreciated_config){
4060 this.parent = false;
4062 if (typeof(depreciated_tpl) == 'undefined') {
4063 // new way.. - universal constructor.
4064 Roo.apply(this, config);
4065 this.el = Roo.get(this.el);
4068 this.el = Roo.get(config);
4069 this.tpl = depreciated_tpl;
4070 Roo.apply(this, depreciated_config);
4072 this.wrapEl = this.el.wrap().wrap();
4073 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4076 if(typeof(this.tpl) == "string"){
4077 this.tpl = new Roo.Template(this.tpl);
4079 // support xtype ctors..
4080 this.tpl = new Roo.factory(this.tpl, Roo);
4089 * @event beforeclick
4090 * Fires before a click is processed. Returns false to cancel the default action.
4091 * @param {Roo.View} this
4092 * @param {Number} index The index of the target node
4093 * @param {HTMLElement} node The target node
4094 * @param {Roo.EventObject} e The raw event object
4096 "beforeclick" : true,
4099 * Fires when a template node is clicked.
4100 * @param {Roo.View} this
4101 * @param {Number} index The index of the target node
4102 * @param {HTMLElement} node The target node
4103 * @param {Roo.EventObject} e The raw event object
4108 * Fires when a template node is double clicked.
4109 * @param {Roo.View} this
4110 * @param {Number} index The index of the target node
4111 * @param {HTMLElement} node The target node
4112 * @param {Roo.EventObject} e The raw event object
4116 * @event contextmenu
4117 * Fires when a template node is right clicked.
4118 * @param {Roo.View} this
4119 * @param {Number} index The index of the target node
4120 * @param {HTMLElement} node The target node
4121 * @param {Roo.EventObject} e The raw event object
4123 "contextmenu" : true,
4125 * @event selectionchange
4126 * Fires when the selected nodes change.
4127 * @param {Roo.View} this
4128 * @param {Array} selections Array of the selected nodes
4130 "selectionchange" : true,
4133 * @event beforeselect
4134 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4135 * @param {Roo.View} this
4136 * @param {HTMLElement} node The node to be selected
4137 * @param {Array} selections Array of currently selected nodes
4139 "beforeselect" : true,
4141 * @event preparedata
4142 * Fires on every row to render, to allow you to change the data.
4143 * @param {Roo.View} this
4144 * @param {Object} data to be rendered (change this)
4146 "preparedata" : true
4154 "click": this.onClick,
4155 "dblclick": this.onDblClick,
4156 "contextmenu": this.onContextMenu,
4160 this.selections = [];
4162 this.cmp = new Roo.CompositeElementLite([]);
4164 this.store = Roo.factory(this.store, Roo.data);
4165 this.setStore(this.store, true);
4168 if ( this.footer && this.footer.xtype) {
4170 var fctr = this.wrapEl.appendChild(document.createElement("div"));
4172 this.footer.dataSource = this.store;
4173 this.footer.container = fctr;
4174 this.footer = Roo.factory(this.footer, Roo);
4175 fctr.insertFirst(this.el);
4177 // this is a bit insane - as the paging toolbar seems to detach the el..
4178 // dom.parentNode.parentNode.parentNode
4179 // they get detached?
4183 Roo.View.superclass.constructor.call(this);
4188 Roo.extend(Roo.View, Roo.util.Observable, {
4191 * @cfg {Roo.data.Store} store Data store to load data from.
4196 * @cfg {String|Roo.Element} el The container element.
4201 * @cfg {String|Roo.Template} tpl The template used by this View
4205 * @cfg {String} dataName the named area of the template to use as the data area
4206 * Works with domtemplates roo-name="name"
4210 * @cfg {String} selectedClass The css class to add to selected nodes
4212 selectedClass : "x-view-selected",
4214 * @cfg {String} emptyText The empty text to show when nothing is loaded.
4219 * @cfg {String} text to display on mask (default Loading)
4223 * @cfg {Boolean} multiSelect Allow multiple selection
4225 multiSelect : false,
4227 * @cfg {Boolean} singleSelect Allow single selection
4229 singleSelect: false,
4232 * @cfg {Boolean} toggleSelect - selecting
4234 toggleSelect : false,
4237 * @cfg {Boolean} tickable - selecting
4242 * Returns the element this view is bound to.
4243 * @return {Roo.Element}
4252 * Refreshes the view. - called by datachanged on the store. - do not call directly.
4254 refresh : function(){
4255 //Roo.log('refresh');
4258 // if we are using something like 'domtemplate', then
4259 // the what gets used is:
4260 // t.applySubtemplate(NAME, data, wrapping data..)
4261 // the outer template then get' applied with
4262 // the store 'extra data'
4263 // and the body get's added to the
4264 // roo-name="data" node?
4265 // <span class='roo-tpl-{name}'></span> ?????
4269 this.clearSelections();
4272 var records = this.store.getRange();
4273 if(records.length < 1) {
4275 // is this valid?? = should it render a template??
4277 this.el.update(this.emptyText);
4281 if (this.dataName) {
4282 this.el.update(t.apply(this.store.meta)); //????
4283 el = this.el.child('.roo-tpl-' + this.dataName);
4286 for(var i = 0, len = records.length; i < len; i++){
4287 var data = this.prepareData(records[i].data, i, records[i]);
4288 this.fireEvent("preparedata", this, data, i, records[i]);
4290 var d = Roo.apply({}, data);
4293 Roo.apply(d, {'roo-id' : Roo.id()});
4297 Roo.each(this.parent.item, function(item){
4298 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4301 Roo.apply(d, {'roo-data-checked' : 'checked'});
4305 html[html.length] = Roo.util.Format.trim(
4307 t.applySubtemplate(this.dataName, d, this.store.meta) :
4314 el.update(html.join(""));
4315 this.nodes = el.dom.childNodes;
4316 this.updateIndexes(0);
4321 * Function to override to reformat the data that is sent to
4322 * the template for each node.
4323 * DEPRICATED - use the preparedata event handler.
4324 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4325 * a JSON object for an UpdateManager bound view).
4327 prepareData : function(data, index, record)
4329 this.fireEvent("preparedata", this, data, index, record);
4333 onUpdate : function(ds, record){
4334 // Roo.log('on update');
4335 this.clearSelections();
4336 var index = this.store.indexOf(record);
4337 var n = this.nodes[index];
4338 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4339 n.parentNode.removeChild(n);
4340 this.updateIndexes(index, index);
4346 onAdd : function(ds, records, index)
4348 //Roo.log(['on Add', ds, records, index] );
4349 this.clearSelections();
4350 if(this.nodes.length == 0){
4354 var n = this.nodes[index];
4355 for(var i = 0, len = records.length; i < len; i++){
4356 var d = this.prepareData(records[i].data, i, records[i]);
4358 this.tpl.insertBefore(n, d);
4361 this.tpl.append(this.el, d);
4364 this.updateIndexes(index);
4367 onRemove : function(ds, record, index){
4368 // Roo.log('onRemove');
4369 this.clearSelections();
4370 var el = this.dataName ?
4371 this.el.child('.roo-tpl-' + this.dataName) :
4374 el.dom.removeChild(this.nodes[index]);
4375 this.updateIndexes(index);
4379 * Refresh an individual node.
4380 * @param {Number} index
4382 refreshNode : function(index){
4383 this.onUpdate(this.store, this.store.getAt(index));
4386 updateIndexes : function(startIndex, endIndex){
4387 var ns = this.nodes;
4388 startIndex = startIndex || 0;
4389 endIndex = endIndex || ns.length - 1;
4390 for(var i = startIndex; i <= endIndex; i++){
4391 ns[i].nodeIndex = i;
4396 * Changes the data store this view uses and refresh the view.
4397 * @param {Store} store
4399 setStore : function(store, initial){
4400 if(!initial && this.store){
4401 this.store.un("datachanged", this.refresh);
4402 this.store.un("add", this.onAdd);
4403 this.store.un("remove", this.onRemove);
4404 this.store.un("update", this.onUpdate);
4405 this.store.un("clear", this.refresh);
4406 this.store.un("beforeload", this.onBeforeLoad);
4407 this.store.un("load", this.onLoad);
4408 this.store.un("loadexception", this.onLoad);
4412 store.on("datachanged", this.refresh, this);
4413 store.on("add", this.onAdd, this);
4414 store.on("remove", this.onRemove, this);
4415 store.on("update", this.onUpdate, this);
4416 store.on("clear", this.refresh, this);
4417 store.on("beforeload", this.onBeforeLoad, this);
4418 store.on("load", this.onLoad, this);
4419 store.on("loadexception", this.onLoad, this);
4427 * onbeforeLoad - masks the loading area.
4430 onBeforeLoad : function(store,opts)
4432 //Roo.log('onBeforeLoad');
4436 this.el.mask(this.mask ? this.mask : "Loading" );
4438 onLoad : function ()
4445 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4446 * @param {HTMLElement} node
4447 * @return {HTMLElement} The template node
4449 findItemFromChild : function(node){
4450 var el = this.dataName ?
4451 this.el.child('.roo-tpl-' + this.dataName,true) :
4454 if(!node || node.parentNode == el){
4457 var p = node.parentNode;
4458 while(p && p != el){
4459 if(p.parentNode == el){
4468 onClick : function(e){
4469 var item = this.findItemFromChild(e.getTarget());
4471 var index = this.indexOf(item);
4472 if(this.onItemClick(item, index, e) !== false){
4473 this.fireEvent("click", this, index, item, e);
4476 this.clearSelections();
4481 onContextMenu : function(e){
4482 var item = this.findItemFromChild(e.getTarget());
4484 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4489 onDblClick : function(e){
4490 var item = this.findItemFromChild(e.getTarget());
4492 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4496 onItemClick : function(item, index, e)
4498 if(this.fireEvent("beforeclick", this, index, item, e) === false){
4501 if (this.toggleSelect) {
4502 var m = this.isSelected(item) ? 'unselect' : 'select';
4505 _t[m](item, true, false);
4508 if(this.multiSelect || this.singleSelect){
4509 if(this.multiSelect && e.shiftKey && this.lastSelection){
4510 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4512 this.select(item, this.multiSelect && e.ctrlKey);
4513 this.lastSelection = item;
4525 * Get the number of selected nodes.
4528 getSelectionCount : function(){
4529 return this.selections.length;
4533 * Get the currently selected nodes.
4534 * @return {Array} An array of HTMLElements
4536 getSelectedNodes : function(){
4537 return this.selections;
4541 * Get the indexes of the selected nodes.
4544 getSelectedIndexes : function(){
4545 var indexes = [], s = this.selections;
4546 for(var i = 0, len = s.length; i < len; i++){
4547 indexes.push(s[i].nodeIndex);
4553 * Clear all selections
4554 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4556 clearSelections : function(suppressEvent){
4557 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4558 this.cmp.elements = this.selections;
4559 this.cmp.removeClass(this.selectedClass);
4560 this.selections = [];
4562 this.fireEvent("selectionchange", this, this.selections);
4568 * Returns true if the passed node is selected
4569 * @param {HTMLElement/Number} node The node or node index
4572 isSelected : function(node){
4573 var s = this.selections;
4577 node = this.getNode(node);
4578 return s.indexOf(node) !== -1;
4583 * @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
4584 * @param {Boolean} keepExisting (optional) true to keep existing selections
4585 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4587 select : function(nodeInfo, keepExisting, suppressEvent){
4588 if(nodeInfo instanceof Array){
4590 this.clearSelections(true);
4592 for(var i = 0, len = nodeInfo.length; i < len; i++){
4593 this.select(nodeInfo[i], true, true);
4597 var node = this.getNode(nodeInfo);
4598 if(!node || this.isSelected(node)){
4599 return; // already selected.
4602 this.clearSelections(true);
4605 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4606 Roo.fly(node).addClass(this.selectedClass);
4607 this.selections.push(node);
4609 this.fireEvent("selectionchange", this, this.selections);
4617 * @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
4618 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4619 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4621 unselect : function(nodeInfo, keepExisting, suppressEvent)
4623 if(nodeInfo instanceof Array){
4624 Roo.each(this.selections, function(s) {
4625 this.unselect(s, nodeInfo);
4629 var node = this.getNode(nodeInfo);
4630 if(!node || !this.isSelected(node)){
4631 //Roo.log("not selected");
4632 return; // not selected.
4636 Roo.each(this.selections, function(s) {
4638 Roo.fly(node).removeClass(this.selectedClass);
4645 this.selections= ns;
4646 this.fireEvent("selectionchange", this, this.selections);
4650 * Gets a template node.
4651 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4652 * @return {HTMLElement} The node or null if it wasn't found
4654 getNode : function(nodeInfo){
4655 if(typeof nodeInfo == "string"){
4656 return document.getElementById(nodeInfo);
4657 }else if(typeof nodeInfo == "number"){
4658 return this.nodes[nodeInfo];
4664 * Gets a range template nodes.
4665 * @param {Number} startIndex
4666 * @param {Number} endIndex
4667 * @return {Array} An array of nodes
4669 getNodes : function(start, end){
4670 var ns = this.nodes;
4672 end = typeof end == "undefined" ? ns.length - 1 : end;
4675 for(var i = start; i <= end; i++){
4679 for(var i = start; i >= end; i--){
4687 * Finds the index of the passed node
4688 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4689 * @return {Number} The index of the node or -1
4691 indexOf : function(node){
4692 node = this.getNode(node);
4693 if(typeof node.nodeIndex == "number"){
4694 return node.nodeIndex;
4696 var ns = this.nodes;
4697 for(var i = 0, len = ns.length; i < len; i++){
4707 * Ext JS Library 1.1.1
4708 * Copyright(c) 2006-2007, Ext JS, LLC.
4710 * Originally Released Under LGPL - original licence link has changed is not relivant.
4713 * <script type="text/javascript">
4717 * @class Roo.JsonView
4719 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4721 var view = new Roo.JsonView({
4722 container: "my-element",
4723 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
4728 // listen for node click?
4729 view.on("click", function(vw, index, node, e){
4730 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4733 // direct load of JSON data
4734 view.load("foobar.php");
4736 // Example from my blog list
4737 var tpl = new Roo.Template(
4738 '<div class="entry">' +
4739 '<a class="entry-title" href="{link}">{title}</a>' +
4740 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
4741 "</div><hr />"
4744 var moreView = new Roo.JsonView({
4745 container : "entry-list",
4749 moreView.on("beforerender", this.sortEntries, this);
4751 url: "/blog/get-posts.php",
4752 params: "allposts=true",
4753 text: "Loading Blog Entries..."
4757 * Note: old code is supported with arguments : (container, template, config)
4761 * Create a new JsonView
4763 * @param {Object} config The config object
4766 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4769 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4771 var um = this.el.getUpdateManager();
4772 um.setRenderer(this);
4773 um.on("update", this.onLoad, this);
4774 um.on("failure", this.onLoadException, this);
4777 * @event beforerender
4778 * Fires before rendering of the downloaded JSON data.
4779 * @param {Roo.JsonView} this
4780 * @param {Object} data The JSON data loaded
4784 * Fires when data is loaded.
4785 * @param {Roo.JsonView} this
4786 * @param {Object} data The JSON data loaded
4787 * @param {Object} response The raw Connect response object
4790 * @event loadexception
4791 * Fires when loading fails.
4792 * @param {Roo.JsonView} this
4793 * @param {Object} response The raw Connect response object
4796 'beforerender' : true,
4798 'loadexception' : true
4801 Roo.extend(Roo.JsonView, Roo.View, {
4803 * @type {String} The root property in the loaded JSON object that contains the data
4808 * Refreshes the view.
4810 refresh : function(){
4811 this.clearSelections();
4814 var o = this.jsonData;
4815 if(o && o.length > 0){
4816 for(var i = 0, len = o.length; i < len; i++){
4817 var data = this.prepareData(o[i], i, o);
4818 html[html.length] = this.tpl.apply(data);
4821 html.push(this.emptyText);
4823 this.el.update(html.join(""));
4824 this.nodes = this.el.dom.childNodes;
4825 this.updateIndexes(0);
4829 * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
4830 * @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:
4833 url: "your-url.php",
4834 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4835 callback: yourFunction,
4836 scope: yourObject, //(optional scope)
4844 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4845 * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
4846 * @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}
4847 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4848 * @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.
4851 var um = this.el.getUpdateManager();
4852 um.update.apply(um, arguments);
4855 // note - render is a standard framework call...
4856 // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4857 render : function(el, response){
4859 this.clearSelections();
4863 if (response != '') {
4864 o = Roo.util.JSON.decode(response.responseText);
4867 o = o[this.jsonRoot];
4873 * The current JSON data or null
4876 this.beforeRender();
4881 * Get the number of records in the current JSON dataset
4884 getCount : function(){
4885 return this.jsonData ? this.jsonData.length : 0;
4889 * Returns the JSON object for the specified node(s)
4890 * @param {HTMLElement/Array} node The node or an array of nodes
4891 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4892 * you get the JSON object for the node
4894 getNodeData : function(node){
4895 if(node instanceof Array){
4897 for(var i = 0, len = node.length; i < len; i++){
4898 data.push(this.getNodeData(node[i]));
4902 return this.jsonData[this.indexOf(node)] || null;
4905 beforeRender : function(){
4906 this.snapshot = this.jsonData;
4908 this.sort.apply(this, this.sortInfo);
4910 this.fireEvent("beforerender", this, this.jsonData);
4913 onLoad : function(el, o){
4914 this.fireEvent("load", this, this.jsonData, o);
4917 onLoadException : function(el, o){
4918 this.fireEvent("loadexception", this, o);
4922 * Filter the data by a specific property.
4923 * @param {String} property A property on your JSON objects
4924 * @param {String/RegExp} value Either string that the property values
4925 * should start with, or a RegExp to test against the property
4927 filter : function(property, value){
4930 var ss = this.snapshot;
4931 if(typeof value == "string"){
4932 var vlen = value.length;
4937 value = value.toLowerCase();
4938 for(var i = 0, len = ss.length; i < len; i++){
4940 if(o[property].substr(0, vlen).toLowerCase() == value){
4944 } else if(value.exec){ // regex?
4945 for(var i = 0, len = ss.length; i < len; i++){
4947 if(value.test(o[property])){
4954 this.jsonData = data;
4960 * Filter by a function. The passed function will be called with each
4961 * object in the current dataset. If the function returns true the value is kept,
4962 * otherwise it is filtered.
4963 * @param {Function} fn
4964 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4966 filterBy : function(fn, scope){
4969 var ss = this.snapshot;
4970 for(var i = 0, len = ss.length; i < len; i++){
4972 if(fn.call(scope || this, o)){
4976 this.jsonData = data;
4982 * Clears the current filter.
4984 clearFilter : function(){
4985 if(this.snapshot && this.jsonData != this.snapshot){
4986 this.jsonData = this.snapshot;
4993 * Sorts the data for this view and refreshes it.
4994 * @param {String} property A property on your JSON objects to sort on
4995 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4996 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4998 sort : function(property, dir, sortType){
4999 this.sortInfo = Array.prototype.slice.call(arguments, 0);
5002 var dsc = dir && dir.toLowerCase() == "desc";
5003 var f = function(o1, o2){
5004 var v1 = sortType ? sortType(o1[p]) : o1[p];
5005 var v2 = sortType ? sortType(o2[p]) : o2[p];
5008 return dsc ? +1 : -1;
5010 return dsc ? -1 : +1;
5015 this.jsonData.sort(f);
5017 if(this.jsonData != this.snapshot){
5018 this.snapshot.sort(f);
5024 * Ext JS Library 1.1.1
5025 * Copyright(c) 2006-2007, Ext JS, LLC.
5027 * Originally Released Under LGPL - original licence link has changed is not relivant.
5030 * <script type="text/javascript">
5035 * @class Roo.ColorPalette
5036 * @extends Roo.Component
5037 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
5038 * Here's an example of typical usage:
5040 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
5041 cp.render('my-div');
5043 cp.on('select', function(palette, selColor){
5044 // do something with selColor
5048 * Create a new ColorPalette
5049 * @param {Object} config The config object
5051 Roo.ColorPalette = function(config){
5052 Roo.ColorPalette.superclass.constructor.call(this, config);
5056 * Fires when a color is selected
5057 * @param {ColorPalette} this
5058 * @param {String} color The 6-digit color hex code (without the # symbol)
5064 this.on("select", this.handler, this.scope, true);
5067 Roo.extend(Roo.ColorPalette, Roo.Component, {
5069 * @cfg {String} itemCls
5070 * The CSS class to apply to the containing element (defaults to "x-color-palette")
5072 itemCls : "x-color-palette",
5074 * @cfg {String} value
5075 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
5076 * the hex codes are case-sensitive.
5081 ctype: "Roo.ColorPalette",
5084 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5086 allowReselect : false,
5089 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
5090 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
5091 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5092 * of colors with the width setting until the box is symmetrical.</p>
5093 * <p>You can override individual colors if needed:</p>
5095 var cp = new Roo.ColorPalette();
5096 cp.colors[0] = "FF0000"; // change the first box to red
5099 Or you can provide a custom array of your own for complete control:
5101 var cp = new Roo.ColorPalette();
5102 cp.colors = ["000000", "993300", "333300"];
5107 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5108 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5109 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5110 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5111 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5115 onRender : function(container, position){
5116 var t = new Roo.MasterTemplate(
5117 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
5119 var c = this.colors;
5120 for(var i = 0, len = c.length; i < len; i++){
5123 var el = document.createElement("div");
5124 el.className = this.itemCls;
5126 container.dom.insertBefore(el, position);
5127 this.el = Roo.get(el);
5128 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
5129 if(this.clickEvent != 'click'){
5130 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
5135 afterRender : function(){
5136 Roo.ColorPalette.superclass.afterRender.call(this);
5145 handleClick : function(e, t){
5148 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5149 this.select(c.toUpperCase());
5154 * Selects the specified color in the palette (fires the select event)
5155 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5157 select : function(color){
5158 color = color.replace("#", "");
5159 if(color != this.value || this.allowReselect){
5162 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5164 el.child("a.color-"+color).addClass("x-color-palette-sel");
5166 this.fireEvent("select", this, color);
5171 * Ext JS Library 1.1.1
5172 * Copyright(c) 2006-2007, Ext JS, LLC.
5174 * Originally Released Under LGPL - original licence link has changed is not relivant.
5177 * <script type="text/javascript">
5181 * @class Roo.DatePicker
5182 * @extends Roo.Component
5183 * Simple date picker class.
5185 * Create a new DatePicker
5186 * @param {Object} config The config object
5188 Roo.DatePicker = function(config){
5189 Roo.DatePicker.superclass.constructor.call(this, config);
5191 this.value = config && config.value ?
5192 config.value.clearTime() : new Date().clearTime();
5197 * Fires when a date is selected
5198 * @param {DatePicker} this
5199 * @param {Date} date The selected date
5203 * @event monthchange
5204 * Fires when the displayed month changes
5205 * @param {DatePicker} this
5206 * @param {Date} date The selected month
5212 this.on("select", this.handler, this.scope || this);
5214 // build the disabledDatesRE
5215 if(!this.disabledDatesRE && this.disabledDates){
5216 var dd = this.disabledDates;
5218 for(var i = 0; i < dd.length; i++){
5220 if(i != dd.length-1) {
5224 this.disabledDatesRE = new RegExp(re + ")");
5228 Roo.extend(Roo.DatePicker, Roo.Component, {
5230 * @cfg {String} todayText
5231 * The text to display on the button that selects the current date (defaults to "Today")
5233 todayText : "Today",
5235 * @cfg {String} okText
5236 * The text to display on the ok button
5238 okText : " OK ", //   to give the user extra clicking room
5240 * @cfg {String} cancelText
5241 * The text to display on the cancel button
5243 cancelText : "Cancel",
5245 * @cfg {String} todayTip
5246 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5248 todayTip : "{0} (Spacebar)",
5250 * @cfg {Date} minDate
5251 * Minimum allowable date (JavaScript date object, defaults to null)
5255 * @cfg {Date} maxDate
5256 * Maximum allowable date (JavaScript date object, defaults to null)
5260 * @cfg {String} minText
5261 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5263 minText : "This date is before the minimum date",
5265 * @cfg {String} maxText
5266 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5268 maxText : "This date is after the maximum date",
5270 * @cfg {String} format
5271 * The default date format string which can be overriden for localization support. The format must be
5272 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5276 * @cfg {Array} disabledDays
5277 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5279 disabledDays : null,
5281 * @cfg {String} disabledDaysText
5282 * The tooltip to display when the date falls on a disabled day (defaults to "")
5284 disabledDaysText : "",
5286 * @cfg {RegExp} disabledDatesRE
5287 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5289 disabledDatesRE : null,
5291 * @cfg {String} disabledDatesText
5292 * The tooltip text to display when the date falls on a disabled date (defaults to "")
5294 disabledDatesText : "",
5296 * @cfg {Boolean} constrainToViewport
5297 * True to constrain the date picker to the viewport (defaults to true)
5299 constrainToViewport : true,
5301 * @cfg {Array} monthNames
5302 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5304 monthNames : Date.monthNames,
5306 * @cfg {Array} dayNames
5307 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5309 dayNames : Date.dayNames,
5311 * @cfg {String} nextText
5312 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5314 nextText: 'Next Month (Control+Right)',
5316 * @cfg {String} prevText
5317 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5319 prevText: 'Previous Month (Control+Left)',
5321 * @cfg {String} monthYearText
5322 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5324 monthYearText: 'Choose a month (Control+Up/Down to move years)',
5326 * @cfg {Number} startDay
5327 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5331 * @cfg {Bool} showClear
5332 * Show a clear button (usefull for date form elements that can be blank.)
5338 * Sets the value of the date field
5339 * @param {Date} value The date to set
5341 setValue : function(value){
5342 var old = this.value;
5344 if (typeof(value) == 'string') {
5346 value = Date.parseDate(value, this.format);
5352 this.value = value.clearTime(true);
5354 this.update(this.value);
5359 * Gets the current selected value of the date field
5360 * @return {Date} The selected date
5362 getValue : function(){
5369 this.update(this.activeDate);
5374 onRender : function(container, position){
5377 '<table cellspacing="0">',
5378 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'"> </a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'"> </a></td></tr>',
5379 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5380 var dn = this.dayNames;
5381 for(var i = 0; i < 7; i++){
5382 var d = this.startDay+i;
5386 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5388 m[m.length] = "</tr></thead><tbody><tr>";
5389 for(var i = 0; i < 42; i++) {
5390 if(i % 7 == 0 && i != 0){
5391 m[m.length] = "</tr><tr>";
5393 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5395 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5396 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5398 var el = document.createElement("div");
5399 el.className = "x-date-picker";
5400 el.innerHTML = m.join("");
5402 container.dom.insertBefore(el, position);
5404 this.el = Roo.get(el);
5405 this.eventEl = Roo.get(el.firstChild);
5407 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5408 handler: this.showPrevMonth,
5410 preventDefault:true,
5414 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5415 handler: this.showNextMonth,
5417 preventDefault:true,
5421 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
5423 this.monthPicker = this.el.down('div.x-date-mp');
5424 this.monthPicker.enableDisplayMode('block');
5426 var kn = new Roo.KeyNav(this.eventEl, {
5427 "left" : function(e){
5429 this.showPrevMonth() :
5430 this.update(this.activeDate.add("d", -1));
5433 "right" : function(e){
5435 this.showNextMonth() :
5436 this.update(this.activeDate.add("d", 1));
5441 this.showNextYear() :
5442 this.update(this.activeDate.add("d", -7));
5445 "down" : function(e){
5447 this.showPrevYear() :
5448 this.update(this.activeDate.add("d", 7));
5451 "pageUp" : function(e){
5452 this.showNextMonth();
5455 "pageDown" : function(e){
5456 this.showPrevMonth();
5459 "enter" : function(e){
5460 e.stopPropagation();
5467 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
5469 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
5471 this.el.unselectable();
5473 this.cells = this.el.select("table.x-date-inner tbody td");
5474 this.textNodes = this.el.query("table.x-date-inner tbody span");
5476 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5478 tooltip: this.monthYearText
5481 this.mbtn.on('click', this.showMonthPicker, this);
5482 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5485 var today = (new Date()).dateFormat(this.format);
5487 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5488 if (this.showClear) {
5489 baseTb.add( new Roo.Toolbar.Fill());
5492 text: String.format(this.todayText, today),
5493 tooltip: String.format(this.todayTip, today),
5494 handler: this.selectToday,
5498 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5501 if (this.showClear) {
5503 baseTb.add( new Roo.Toolbar.Fill());
5506 cls: 'x-btn-icon x-btn-clear',
5507 handler: function() {
5509 this.fireEvent("select", this, '');
5519 this.update(this.value);
5522 createMonthPicker : function(){
5523 if(!this.monthPicker.dom.firstChild){
5524 var buf = ['<table border="0" cellspacing="0">'];
5525 for(var i = 0; i < 6; i++){
5527 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5528 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5530 '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
5531 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5535 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5537 '</button><button type="button" class="x-date-mp-cancel">',
5539 '</button></td></tr>',
5542 this.monthPicker.update(buf.join(''));
5543 this.monthPicker.on('click', this.onMonthClick, this);
5544 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5546 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5547 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5549 this.mpMonths.each(function(m, a, i){
5552 m.dom.xmonth = 5 + Math.round(i * .5);
5554 m.dom.xmonth = Math.round((i-1) * .5);
5560 showMonthPicker : function(){
5561 this.createMonthPicker();
5562 var size = this.el.getSize();
5563 this.monthPicker.setSize(size);
5564 this.monthPicker.child('table').setSize(size);
5566 this.mpSelMonth = (this.activeDate || this.value).getMonth();
5567 this.updateMPMonth(this.mpSelMonth);
5568 this.mpSelYear = (this.activeDate || this.value).getFullYear();
5569 this.updateMPYear(this.mpSelYear);
5571 this.monthPicker.slideIn('t', {duration:.2});
5574 updateMPYear : function(y){
5576 var ys = this.mpYears.elements;
5577 for(var i = 1; i <= 10; i++){
5578 var td = ys[i-1], y2;
5580 y2 = y + Math.round(i * .5);
5581 td.firstChild.innerHTML = y2;
5584 y2 = y - (5-Math.round(i * .5));
5585 td.firstChild.innerHTML = y2;
5588 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5592 updateMPMonth : function(sm){
5593 this.mpMonths.each(function(m, a, i){
5594 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5598 selectMPMonth: function(m){
5602 onMonthClick : function(e, t){
5604 var el = new Roo.Element(t), pn;
5605 if(el.is('button.x-date-mp-cancel')){
5606 this.hideMonthPicker();
5608 else if(el.is('button.x-date-mp-ok')){
5609 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5610 this.hideMonthPicker();
5612 else if(pn = el.up('td.x-date-mp-month', 2)){
5613 this.mpMonths.removeClass('x-date-mp-sel');
5614 pn.addClass('x-date-mp-sel');
5615 this.mpSelMonth = pn.dom.xmonth;
5617 else if(pn = el.up('td.x-date-mp-year', 2)){
5618 this.mpYears.removeClass('x-date-mp-sel');
5619 pn.addClass('x-date-mp-sel');
5620 this.mpSelYear = pn.dom.xyear;
5622 else if(el.is('a.x-date-mp-prev')){
5623 this.updateMPYear(this.mpyear-10);
5625 else if(el.is('a.x-date-mp-next')){
5626 this.updateMPYear(this.mpyear+10);
5630 onMonthDblClick : function(e, t){
5632 var el = new Roo.Element(t), pn;
5633 if(pn = el.up('td.x-date-mp-month', 2)){
5634 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5635 this.hideMonthPicker();
5637 else if(pn = el.up('td.x-date-mp-year', 2)){
5638 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5639 this.hideMonthPicker();
5643 hideMonthPicker : function(disableAnim){
5644 if(this.monthPicker){
5645 if(disableAnim === true){
5646 this.monthPicker.hide();
5648 this.monthPicker.slideOut('t', {duration:.2});
5654 showPrevMonth : function(e){
5655 this.update(this.activeDate.add("mo", -1));
5659 showNextMonth : function(e){
5660 this.update(this.activeDate.add("mo", 1));
5664 showPrevYear : function(){
5665 this.update(this.activeDate.add("y", -1));
5669 showNextYear : function(){
5670 this.update(this.activeDate.add("y", 1));
5674 handleMouseWheel : function(e){
5675 var delta = e.getWheelDelta();
5677 this.showPrevMonth();
5679 } else if(delta < 0){
5680 this.showNextMonth();
5686 handleDateClick : function(e, t){
5688 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5689 this.setValue(new Date(t.dateValue));
5690 this.fireEvent("select", this, this.value);
5695 selectToday : function(){
5696 this.setValue(new Date().clearTime());
5697 this.fireEvent("select", this, this.value);
5701 update : function(date)
5703 var vd = this.activeDate;
5704 this.activeDate = date;
5706 var t = date.getTime();
5707 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5708 this.cells.removeClass("x-date-selected");
5709 this.cells.each(function(c){
5710 if(c.dom.firstChild.dateValue == t){
5711 c.addClass("x-date-selected");
5712 setTimeout(function(){
5713 try{c.dom.firstChild.focus();}catch(e){}
5722 var days = date.getDaysInMonth();
5723 var firstOfMonth = date.getFirstDateOfMonth();
5724 var startingPos = firstOfMonth.getDay()-this.startDay;
5726 if(startingPos <= this.startDay){
5730 var pm = date.add("mo", -1);
5731 var prevStart = pm.getDaysInMonth()-startingPos;
5733 var cells = this.cells.elements;
5734 var textEls = this.textNodes;
5735 days += startingPos;
5737 // convert everything to numbers so it's fast
5739 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5740 var today = new Date().clearTime().getTime();
5741 var sel = date.clearTime().getTime();
5742 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5743 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5744 var ddMatch = this.disabledDatesRE;
5745 var ddText = this.disabledDatesText;
5746 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5747 var ddaysText = this.disabledDaysText;
5748 var format = this.format;
5750 var setCellClass = function(cal, cell){
5752 var t = d.getTime();
5753 cell.firstChild.dateValue = t;
5755 cell.className += " x-date-today";
5756 cell.title = cal.todayText;
5759 cell.className += " x-date-selected";
5760 setTimeout(function(){
5761 try{cell.firstChild.focus();}catch(e){}
5766 cell.className = " x-date-disabled";
5767 cell.title = cal.minText;
5771 cell.className = " x-date-disabled";
5772 cell.title = cal.maxText;
5776 if(ddays.indexOf(d.getDay()) != -1){
5777 cell.title = ddaysText;
5778 cell.className = " x-date-disabled";
5781 if(ddMatch && format){
5782 var fvalue = d.dateFormat(format);
5783 if(ddMatch.test(fvalue)){
5784 cell.title = ddText.replace("%0", fvalue);
5785 cell.className = " x-date-disabled";
5791 for(; i < startingPos; i++) {
5792 textEls[i].innerHTML = (++prevStart);
5793 d.setDate(d.getDate()+1);
5794 cells[i].className = "x-date-prevday";
5795 setCellClass(this, cells[i]);
5797 for(; i < days; i++){
5798 intDay = i - startingPos + 1;
5799 textEls[i].innerHTML = (intDay);
5800 d.setDate(d.getDate()+1);
5801 cells[i].className = "x-date-active";
5802 setCellClass(this, cells[i]);
5805 for(; i < 42; i++) {
5806 textEls[i].innerHTML = (++extraDays);
5807 d.setDate(d.getDate()+1);
5808 cells[i].className = "x-date-nextday";
5809 setCellClass(this, cells[i]);
5812 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5813 this.fireEvent('monthchange', this, date);
5815 if(!this.internalRender){
5816 var main = this.el.dom.firstChild;
5817 var w = main.offsetWidth;
5818 this.el.setWidth(w + this.el.getBorderWidth("lr"));
5819 Roo.fly(main).setWidth(w);
5820 this.internalRender = true;
5821 // opera does not respect the auto grow header center column
5822 // then, after it gets a width opera refuses to recalculate
5823 // without a second pass
5824 if(Roo.isOpera && !this.secondPass){
5825 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5826 this.secondPass = true;
5827 this.update.defer(10, this, [date]);
5835 * Ext JS Library 1.1.1
5836 * Copyright(c) 2006-2007, Ext JS, LLC.
5838 * Originally Released Under LGPL - original licence link has changed is not relivant.
5841 * <script type="text/javascript">
5844 * @class Roo.TabPanel
5845 * @extends Roo.util.Observable
5846 * A lightweight tab container.
5850 // basic tabs 1, built from existing content
5851 var tabs = new Roo.TabPanel("tabs1");
5852 tabs.addTab("script", "View Script");
5853 tabs.addTab("markup", "View Markup");
5854 tabs.activate("script");
5856 // more advanced tabs, built from javascript
5857 var jtabs = new Roo.TabPanel("jtabs");
5858 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5860 // set up the UpdateManager
5861 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5862 var updater = tab2.getUpdateManager();
5863 updater.setDefaultUrl("ajax1.htm");
5864 tab2.on('activate', updater.refresh, updater, true);
5866 // Use setUrl for Ajax loading
5867 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5868 tab3.setUrl("ajax2.htm", null, true);
5871 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5874 jtabs.activate("jtabs-1");
5877 * Create a new TabPanel.
5878 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5879 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5881 Roo.TabPanel = function(container, config){
5883 * The container element for this TabPanel.
5886 this.el = Roo.get(container, true);
5888 if(typeof config == "boolean"){
5889 this.tabPosition = config ? "bottom" : "top";
5891 Roo.apply(this, config);
5894 if(this.tabPosition == "bottom"){
5895 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5896 this.el.addClass("x-tabs-bottom");
5898 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5899 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5900 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5902 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5904 if(this.tabPosition != "bottom"){
5905 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5908 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5909 this.el.addClass("x-tabs-top");
5913 this.bodyEl.setStyle("position", "relative");
5916 this.activateDelegate = this.activate.createDelegate(this);
5921 * Fires when the active tab changes
5922 * @param {Roo.TabPanel} this
5923 * @param {Roo.TabPanelItem} activePanel The new active tab
5927 * @event beforetabchange
5928 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5929 * @param {Roo.TabPanel} this
5930 * @param {Object} e Set cancel to true on this object to cancel the tab change
5931 * @param {Roo.TabPanelItem} tab The tab being changed to
5933 "beforetabchange" : true
5936 Roo.EventManager.onWindowResize(this.onResize, this);
5937 this.cpad = this.el.getPadding("lr");
5938 this.hiddenCount = 0;
5941 // toolbar on the tabbar support...
5943 var tcfg = this.toolbar;
5944 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
5945 this.toolbar = new Roo.Toolbar(tcfg);
5947 var tbl = tcfg.container.child('table', true);
5948 tbl.setAttribute('width', '100%');
5955 Roo.TabPanel.superclass.constructor.call(this);
5958 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5960 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5962 tabPosition : "top",
5964 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5966 currentTabWidth : 0,
5968 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5972 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5976 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5978 preferredTabWidth : 175,
5980 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5984 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5986 monitorResize : true,
5988 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
5993 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5994 * @param {String} id The id of the div to use <b>or create</b>
5995 * @param {String} text The text for the tab
5996 * @param {String} content (optional) Content to put in the TabPanelItem body
5997 * @param {Boolean} closable (optional) True to create a close icon on the tab
5998 * @return {Roo.TabPanelItem} The created TabPanelItem
6000 addTab : function(id, text, content, closable){
6001 var item = new Roo.TabPanelItem(this, id, text, closable);
6002 this.addTabItem(item);
6004 item.setContent(content);
6010 * Returns the {@link Roo.TabPanelItem} with the specified id/index
6011 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6012 * @return {Roo.TabPanelItem}
6014 getTab : function(id){
6015 return this.items[id];
6019 * Hides the {@link Roo.TabPanelItem} with the specified id/index
6020 * @param {String/Number} id The id or index of the TabPanelItem to hide.
6022 hideTab : function(id){
6023 var t = this.items[id];
6027 this.autoSizeTabs();
6032 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6033 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6035 unhideTab : function(id){
6036 var t = this.items[id];
6040 this.autoSizeTabs();
6045 * Adds an existing {@link Roo.TabPanelItem}.
6046 * @param {Roo.TabPanelItem} item The TabPanelItem to add
6048 addTabItem : function(item){
6049 this.items[item.id] = item;
6050 this.items.push(item);
6051 if(this.resizeTabs){
6052 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6053 this.autoSizeTabs();
6060 * Removes a {@link Roo.TabPanelItem}.
6061 * @param {String/Number} id The id or index of the TabPanelItem to remove.
6063 removeTab : function(id){
6064 var items = this.items;
6065 var tab = items[id];
6066 if(!tab) { return; }
6067 var index = items.indexOf(tab);
6068 if(this.active == tab && items.length > 1){
6069 var newTab = this.getNextAvailable(index);
6074 this.stripEl.dom.removeChild(tab.pnode.dom);
6075 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6076 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6078 items.splice(index, 1);
6079 delete this.items[tab.id];
6080 tab.fireEvent("close", tab);
6081 tab.purgeListeners();
6082 this.autoSizeTabs();
6085 getNextAvailable : function(start){
6086 var items = this.items;
6088 // look for a next tab that will slide over to
6089 // replace the one being removed
6090 while(index < items.length){
6091 var item = items[++index];
6092 if(item && !item.isHidden()){
6096 // if one isn't found select the previous tab (on the left)
6099 var item = items[--index];
6100 if(item && !item.isHidden()){
6108 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6109 * @param {String/Number} id The id or index of the TabPanelItem to disable.
6111 disableTab : function(id){
6112 var tab = this.items[id];
6113 if(tab && this.active != tab){
6119 * Enables a {@link Roo.TabPanelItem} that is disabled.
6120 * @param {String/Number} id The id or index of the TabPanelItem to enable.
6122 enableTab : function(id){
6123 var tab = this.items[id];
6128 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6129 * @param {String/Number} id The id or index of the TabPanelItem to activate.
6130 * @return {Roo.TabPanelItem} The TabPanelItem.
6132 activate : function(id){
6133 var tab = this.items[id];
6137 if(tab == this.active || tab.disabled){
6141 this.fireEvent("beforetabchange", this, e, tab);
6142 if(e.cancel !== true && !tab.disabled){
6146 this.active = this.items[id];
6148 this.fireEvent("tabchange", this, this.active);
6154 * Gets the active {@link Roo.TabPanelItem}.
6155 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6157 getActiveTab : function(){
6162 * Updates the tab body element to fit the height of the container element
6163 * for overflow scrolling
6164 * @param {Number} targetHeight (optional) Override the starting height from the elements height
6166 syncHeight : function(targetHeight){
6167 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6168 var bm = this.bodyEl.getMargins();
6169 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6170 this.bodyEl.setHeight(newHeight);
6174 onResize : function(){
6175 if(this.monitorResize){
6176 this.autoSizeTabs();
6181 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6183 beginUpdate : function(){
6184 this.updating = true;
6188 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6190 endUpdate : function(){
6191 this.updating = false;
6192 this.autoSizeTabs();
6196 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6198 autoSizeTabs : function(){
6199 var count = this.items.length;
6200 var vcount = count - this.hiddenCount;
6201 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6204 var w = Math.max(this.el.getWidth() - this.cpad, 10);
6205 var availWidth = Math.floor(w / vcount);
6206 var b = this.stripBody;
6207 if(b.getWidth() > w){
6208 var tabs = this.items;
6209 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6210 if(availWidth < this.minTabWidth){
6211 /*if(!this.sleft){ // incomplete scrolling code
6212 this.createScrollButtons();
6215 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6218 if(this.currentTabWidth < this.preferredTabWidth){
6219 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6225 * Returns the number of tabs in this TabPanel.
6228 getCount : function(){
6229 return this.items.length;
6233 * Resizes all the tabs to the passed width
6234 * @param {Number} The new width
6236 setTabWidth : function(width){
6237 this.currentTabWidth = width;
6238 for(var i = 0, len = this.items.length; i < len; i++) {
6239 if(!this.items[i].isHidden()) {
6240 this.items[i].setWidth(width);
6246 * Destroys this TabPanel
6247 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6249 destroy : function(removeEl){
6250 Roo.EventManager.removeResizeListener(this.onResize, this);
6251 for(var i = 0, len = this.items.length; i < len; i++){
6252 this.items[i].purgeListeners();
6254 if(removeEl === true){
6262 * @class Roo.TabPanelItem
6263 * @extends Roo.util.Observable
6264 * Represents an individual item (tab plus body) in a TabPanel.
6265 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6266 * @param {String} id The id of this TabPanelItem
6267 * @param {String} text The text for the tab of this TabPanelItem
6268 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6270 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6272 * The {@link Roo.TabPanel} this TabPanelItem belongs to
6273 * @type Roo.TabPanel
6275 this.tabPanel = tabPanel;
6277 * The id for this TabPanelItem
6282 this.disabled = false;
6286 this.loaded = false;
6287 this.closable = closable;
6290 * The body element for this TabPanelItem.
6293 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6294 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6295 this.bodyEl.setStyle("display", "block");
6296 this.bodyEl.setStyle("zoom", "1");
6299 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6301 this.el = Roo.get(els.el, true);
6302 this.inner = Roo.get(els.inner, true);
6303 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6304 this.pnode = Roo.get(els.el.parentNode, true);
6305 this.el.on("mousedown", this.onTabMouseDown, this);
6306 this.el.on("click", this.onTabClick, this);
6309 var c = Roo.get(els.close, true);
6310 c.dom.title = this.closeText;
6311 c.addClassOnOver("close-over");
6312 c.on("click", this.closeClick, this);
6318 * Fires when this tab becomes the active tab.
6319 * @param {Roo.TabPanel} tabPanel The parent TabPanel
6320 * @param {Roo.TabPanelItem} this
6324 * @event beforeclose
6325 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6326 * @param {Roo.TabPanelItem} this
6327 * @param {Object} e Set cancel to true on this object to cancel the close.
6329 "beforeclose": true,
6332 * Fires when this tab is closed.
6333 * @param {Roo.TabPanelItem} this
6338 * Fires when this tab is no longer the active tab.
6339 * @param {Roo.TabPanel} tabPanel The parent TabPanel
6340 * @param {Roo.TabPanelItem} this
6344 this.hidden = false;
6346 Roo.TabPanelItem.superclass.constructor.call(this);
6349 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6350 purgeListeners : function(){
6351 Roo.util.Observable.prototype.purgeListeners.call(this);
6352 this.el.removeAllListeners();
6355 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6358 this.pnode.addClass("on");
6361 this.tabPanel.stripWrap.repaint();
6363 this.fireEvent("activate", this.tabPanel, this);
6367 * Returns true if this tab is the active tab.
6370 isActive : function(){
6371 return this.tabPanel.getActiveTab() == this;
6375 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6378 this.pnode.removeClass("on");
6380 this.fireEvent("deactivate", this.tabPanel, this);
6383 hideAction : function(){
6385 this.bodyEl.setStyle("position", "absolute");
6386 this.bodyEl.setLeft("-20000px");
6387 this.bodyEl.setTop("-20000px");
6390 showAction : function(){
6391 this.bodyEl.setStyle("position", "relative");
6392 this.bodyEl.setTop("");
6393 this.bodyEl.setLeft("");
6398 * Set the tooltip for the tab.
6399 * @param {String} tooltip The tab's tooltip
6401 setTooltip : function(text){
6402 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6403 this.textEl.dom.qtip = text;
6404 this.textEl.dom.removeAttribute('title');
6406 this.textEl.dom.title = text;
6410 onTabClick : function(e){
6412 this.tabPanel.activate(this.id);
6415 onTabMouseDown : function(e){
6417 this.tabPanel.activate(this.id);
6420 getWidth : function(){
6421 return this.inner.getWidth();
6424 setWidth : function(width){
6425 var iwidth = width - this.pnode.getPadding("lr");
6426 this.inner.setWidth(iwidth);
6427 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6428 this.pnode.setWidth(width);
6432 * Show or hide the tab
6433 * @param {Boolean} hidden True to hide or false to show.
6435 setHidden : function(hidden){
6436 this.hidden = hidden;
6437 this.pnode.setStyle("display", hidden ? "none" : "");
6441 * Returns true if this tab is "hidden"
6444 isHidden : function(){
6449 * Returns the text for this tab
6452 getText : function(){
6456 autoSize : function(){
6457 //this.el.beginMeasure();
6458 this.textEl.setWidth(1);
6460 * #2804 [new] Tabs in Roojs
6461 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6463 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6464 //this.el.endMeasure();
6468 * Sets the text for the tab (Note: this also sets the tooltip text)
6469 * @param {String} text The tab's text and tooltip
6471 setText : function(text){
6473 this.textEl.update(text);
6474 this.setTooltip(text);
6475 if(!this.tabPanel.resizeTabs){
6480 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6482 activate : function(){
6483 this.tabPanel.activate(this.id);
6487 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6489 disable : function(){
6490 if(this.tabPanel.active != this){
6491 this.disabled = true;
6492 this.pnode.addClass("disabled");
6497 * Enables this TabPanelItem if it was previously disabled.
6499 enable : function(){
6500 this.disabled = false;
6501 this.pnode.removeClass("disabled");
6505 * Sets the content for this TabPanelItem.
6506 * @param {String} content The content
6507 * @param {Boolean} loadScripts true to look for and load scripts
6509 setContent : function(content, loadScripts){
6510 this.bodyEl.update(content, loadScripts);
6514 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6515 * @return {Roo.UpdateManager} The UpdateManager
6517 getUpdateManager : function(){
6518 return this.bodyEl.getUpdateManager();
6522 * Set a URL to be used to load the content for this TabPanelItem.
6523 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6524 * @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)
6525 * @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)
6526 * @return {Roo.UpdateManager} The UpdateManager
6528 setUrl : function(url, params, loadOnce){
6529 if(this.refreshDelegate){
6530 this.un('activate', this.refreshDelegate);
6532 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6533 this.on("activate", this.refreshDelegate);
6534 return this.bodyEl.getUpdateManager();
6538 _handleRefresh : function(url, params, loadOnce){
6539 if(!loadOnce || !this.loaded){
6540 var updater = this.bodyEl.getUpdateManager();
6541 updater.update(url, params, this._setLoaded.createDelegate(this));
6546 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
6547 * Will fail silently if the setUrl method has not been called.
6548 * This does not activate the panel, just updates its content.
6550 refresh : function(){
6551 if(this.refreshDelegate){
6552 this.loaded = false;
6553 this.refreshDelegate();
6558 _setLoaded : function(){
6563 closeClick : function(e){
6566 this.fireEvent("beforeclose", this, o);
6567 if(o.cancel !== true){
6568 this.tabPanel.removeTab(this.id);
6572 * The text displayed in the tooltip for the close icon.
6575 closeText : "Close this tab"
6579 Roo.TabPanel.prototype.createStrip = function(container){
6580 var strip = document.createElement("div");
6581 strip.className = "x-tabs-wrap";
6582 container.appendChild(strip);
6586 Roo.TabPanel.prototype.createStripList = function(strip){
6587 // div wrapper for retard IE
6588 // returns the "tr" element.
6589 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6590 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6591 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6592 return strip.firstChild.firstChild.firstChild.firstChild;
6595 Roo.TabPanel.prototype.createBody = function(container){
6596 var body = document.createElement("div");
6597 Roo.id(body, "tab-body");
6598 Roo.fly(body).addClass("x-tabs-body");
6599 container.appendChild(body);
6603 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6604 var body = Roo.getDom(id);
6606 body = document.createElement("div");
6609 Roo.fly(body).addClass("x-tabs-item-body");
6610 bodyEl.insertBefore(body, bodyEl.firstChild);
6614 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6615 var td = document.createElement("td");
6616 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6617 //stripEl.appendChild(td);
6619 td.className = "x-tabs-closable";
6621 this.closeTpl = new Roo.Template(
6622 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6623 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6624 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
6627 var el = this.closeTpl.overwrite(td, {"text": text});
6628 var close = el.getElementsByTagName("div")[0];
6629 var inner = el.getElementsByTagName("em")[0];
6630 return {"el": el, "close": close, "inner": inner};
6633 this.tabTpl = new Roo.Template(
6634 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6635 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6638 var el = this.tabTpl.overwrite(td, {"text": text});
6639 var inner = el.getElementsByTagName("em")[0];
6640 return {"el": el, "inner": inner};
6644 * Ext JS Library 1.1.1
6645 * Copyright(c) 2006-2007, Ext JS, LLC.
6647 * Originally Released Under LGPL - original licence link has changed is not relivant.
6650 * <script type="text/javascript">
6655 * @extends Roo.util.Observable
6656 * Simple Button class
6657 * @cfg {String} text The button text
6658 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6659 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6660 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6661 * @cfg {Object} scope The scope of the handler
6662 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6663 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6664 * @cfg {Boolean} hidden True to start hidden (defaults to false)
6665 * @cfg {Boolean} disabled True to start disabled (defaults to false)
6666 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6667 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6668 applies if enableToggle = true)
6669 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6670 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6671 an {@link Roo.util.ClickRepeater} config object (defaults to false).
6673 * Create a new button
6674 * @param {Object} config The config object
6676 Roo.Button = function(renderTo, config)
6680 renderTo = config.renderTo || false;
6683 Roo.apply(this, config);
6687 * Fires when this button is clicked
6688 * @param {Button} this
6689 * @param {EventObject} e The click event
6694 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6695 * @param {Button} this
6696 * @param {Boolean} pressed
6701 * Fires when the mouse hovers over the button
6702 * @param {Button} this
6703 * @param {Event} e The event object
6708 * Fires when the mouse exits the button
6709 * @param {Button} this
6710 * @param {Event} e The event object
6715 * Fires when the button is rendered
6716 * @param {Button} this
6721 this.menu = Roo.menu.MenuMgr.get(this.menu);
6723 // register listeners first!! - so render can be captured..
6724 Roo.util.Observable.call(this);
6726 this.render(renderTo);
6732 Roo.extend(Roo.Button, Roo.util.Observable, {
6738 * Read-only. True if this button is hidden
6743 * Read-only. True if this button is disabled
6748 * Read-only. True if this button is pressed (only if enableToggle = true)
6754 * @cfg {Number} tabIndex
6755 * The DOM tabIndex for this button (defaults to undefined)
6757 tabIndex : undefined,
6760 * @cfg {Boolean} enableToggle
6761 * True to enable pressed/not pressed toggling (defaults to false)
6763 enableToggle: false,
6766 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6770 * @cfg {String} menuAlign
6771 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6773 menuAlign : "tl-bl?",
6776 * @cfg {String} iconCls
6777 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6779 iconCls : undefined,
6781 * @cfg {String} type
6782 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
6787 menuClassTarget: 'tr',
6790 * @cfg {String} clickEvent
6791 * The type of event to map to the button's event handler (defaults to 'click')
6793 clickEvent : 'click',
6796 * @cfg {Boolean} handleMouseEvents
6797 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6799 handleMouseEvents : true,
6802 * @cfg {String} tooltipType
6803 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6805 tooltipType : 'qtip',
6809 * A CSS class to apply to the button's main element.
6813 * @cfg {Roo.Template} template (Optional)
6814 * An {@link Roo.Template} with which to create the Button's main element. This Template must
6815 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6816 * require code modifications if required elements (e.g. a button) aren't present.
6820 render : function(renderTo){
6822 if(this.hideParent){
6823 this.parentEl = Roo.get(renderTo);
6827 if(!Roo.Button.buttonTemplate){
6828 // hideous table template
6829 Roo.Button.buttonTemplate = new Roo.Template(
6830 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6831 '<td class="x-btn-left"><i> </i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i> </i></td>',
6832 "</tr></tbody></table>");
6834 this.template = Roo.Button.buttonTemplate;
6836 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
6837 var btnEl = btn.child("button:first");
6838 btnEl.on('focus', this.onFocus, this);
6839 btnEl.on('blur', this.onBlur, this);
6841 btn.addClass(this.cls);
6844 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6847 btnEl.addClass(this.iconCls);
6849 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6852 if(this.tabIndex !== undefined){
6853 btnEl.dom.tabIndex = this.tabIndex;
6856 if(typeof this.tooltip == 'object'){
6857 Roo.QuickTips.tips(Roo.apply({
6861 btnEl.dom[this.tooltipType] = this.tooltip;
6865 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6869 this.el.dom.id = this.el.id = this.id;
6872 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6873 this.menu.on("show", this.onMenuShow, this);
6874 this.menu.on("hide", this.onMenuHide, this);
6876 btn.addClass("x-btn");
6877 if(Roo.isIE && !Roo.isIE7){
6878 this.autoWidth.defer(1, this);
6882 if(this.handleMouseEvents){
6883 btn.on("mouseover", this.onMouseOver, this);
6884 btn.on("mouseout", this.onMouseOut, this);
6885 btn.on("mousedown", this.onMouseDown, this);
6887 btn.on(this.clickEvent, this.onClick, this);
6888 //btn.on("mouseup", this.onMouseUp, this);
6895 Roo.ButtonToggleMgr.register(this);
6897 this.el.addClass("x-btn-pressed");
6900 var repeater = new Roo.util.ClickRepeater(btn,
6901 typeof this.repeat == "object" ? this.repeat : {}
6903 repeater.on("click", this.onClick, this);
6906 this.fireEvent('render', this);
6910 * Returns the button's underlying element
6911 * @return {Roo.Element} The element
6918 * Destroys this Button and removes any listeners.
6920 destroy : function(){
6921 Roo.ButtonToggleMgr.unregister(this);
6922 this.el.removeAllListeners();
6923 this.purgeListeners();
6928 autoWidth : function(){
6930 this.el.setWidth("auto");
6931 if(Roo.isIE7 && Roo.isStrict){
6932 var ib = this.el.child('button');
6933 if(ib && ib.getWidth() > 20){
6935 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6940 this.el.beginMeasure();
6942 if(this.el.getWidth() < this.minWidth){
6943 this.el.setWidth(this.minWidth);
6946 this.el.endMeasure();
6953 * Assigns this button's click handler
6954 * @param {Function} handler The function to call when the button is clicked
6955 * @param {Object} scope (optional) Scope for the function passed in
6957 setHandler : function(handler, scope){
6958 this.handler = handler;
6963 * Sets this button's text
6964 * @param {String} text The button text
6966 setText : function(text){
6969 this.el.child("td.x-btn-center button.x-btn-text").update(text);
6975 * Gets the text for this button
6976 * @return {String} The button text
6978 getText : function(){
6986 this.hidden = false;
6988 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6998 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7003 * Convenience function for boolean show/hide
7004 * @param {Boolean} visible True to show, false to hide
7006 setVisible: function(visible){
7015 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7016 * @param {Boolean} state (optional) Force a particular state
7018 toggle : function(state){
7019 state = state === undefined ? !this.pressed : state;
7020 if(state != this.pressed){
7022 this.el.addClass("x-btn-pressed");
7023 this.pressed = true;
7024 this.fireEvent("toggle", this, true);
7026 this.el.removeClass("x-btn-pressed");
7027 this.pressed = false;
7028 this.fireEvent("toggle", this, false);
7030 if(this.toggleHandler){
7031 this.toggleHandler.call(this.scope || this, this, state);
7040 this.el.child('button:first').focus();
7044 * Disable this button
7046 disable : function(){
7048 this.el.addClass("x-btn-disabled");
7050 this.disabled = true;
7054 * Enable this button
7056 enable : function(){
7058 this.el.removeClass("x-btn-disabled");
7060 this.disabled = false;
7064 * Convenience function for boolean enable/disable
7065 * @param {Boolean} enabled True to enable, false to disable
7067 setDisabled : function(v){
7068 this[v !== true ? "enable" : "disable"]();
7072 onClick : function(e)
7081 if(this.enableToggle){
7084 if(this.menu && !this.menu.isVisible()){
7085 this.menu.show(this.el, this.menuAlign);
7087 this.fireEvent("click", this, e);
7089 this.el.removeClass("x-btn-over");
7090 this.handler.call(this.scope || this, this, e);
7095 onMouseOver : function(e){
7097 this.el.addClass("x-btn-over");
7098 this.fireEvent('mouseover', this, e);
7102 onMouseOut : function(e){
7103 if(!e.within(this.el, true)){
7104 this.el.removeClass("x-btn-over");
7105 this.fireEvent('mouseout', this, e);
7109 onFocus : function(e){
7111 this.el.addClass("x-btn-focus");
7115 onBlur : function(e){
7116 this.el.removeClass("x-btn-focus");
7119 onMouseDown : function(e){
7120 if(!this.disabled && e.button == 0){
7121 this.el.addClass("x-btn-click");
7122 Roo.get(document).on('mouseup', this.onMouseUp, this);
7126 onMouseUp : function(e){
7128 this.el.removeClass("x-btn-click");
7129 Roo.get(document).un('mouseup', this.onMouseUp, this);
7133 onMenuShow : function(e){
7134 this.el.addClass("x-btn-menu-active");
7137 onMenuHide : function(e){
7138 this.el.removeClass("x-btn-menu-active");
7142 // Private utility class used by Button
7143 Roo.ButtonToggleMgr = function(){
7146 function toggleGroup(btn, state){
7148 var g = groups[btn.toggleGroup];
7149 for(var i = 0, l = g.length; i < l; i++){
7158 register : function(btn){
7159 if(!btn.toggleGroup){
7162 var g = groups[btn.toggleGroup];
7164 g = groups[btn.toggleGroup] = [];
7167 btn.on("toggle", toggleGroup);
7170 unregister : function(btn){
7171 if(!btn.toggleGroup){
7174 var g = groups[btn.toggleGroup];
7177 btn.un("toggle", toggleGroup);
7183 * Ext JS Library 1.1.1
7184 * Copyright(c) 2006-2007, Ext JS, LLC.
7186 * Originally Released Under LGPL - original licence link has changed is not relivant.
7189 * <script type="text/javascript">
7193 * @class Roo.SplitButton
7194 * @extends Roo.Button
7195 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7196 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
7197 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7198 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7199 * @cfg {String} arrowTooltip The title attribute of the arrow
7201 * Create a new menu button
7202 * @param {String/HTMLElement/Element} renderTo The element to append the button to
7203 * @param {Object} config The config object
7205 Roo.SplitButton = function(renderTo, config){
7206 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7209 * Fires when this button's arrow is clicked
7210 * @param {SplitButton} this
7211 * @param {EventObject} e The click event
7213 this.addEvents({"arrowclick":true});
7216 Roo.extend(Roo.SplitButton, Roo.Button, {
7217 render : function(renderTo){
7218 // this is one sweet looking template!
7219 var tpl = new Roo.Template(
7220 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7221 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7222 '<tr><td class="x-btn-left"><i> </i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
7223 "</tbody></table></td><td>",
7224 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7225 '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button"> </button></td><td class="x-btn-right"><i> </i></td></tr>',
7226 "</tbody></table></td></tr></table>"
7228 var btn = tpl.append(renderTo, [this.text, this.type], true);
7229 var btnEl = btn.child("button");
7231 btn.addClass(this.cls);
7234 btnEl.setStyle('background-image', 'url(' +this.icon +')');
7237 btnEl.addClass(this.iconCls);
7239 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7243 if(this.handleMouseEvents){
7244 btn.on("mouseover", this.onMouseOver, this);
7245 btn.on("mouseout", this.onMouseOut, this);
7246 btn.on("mousedown", this.onMouseDown, this);
7247 btn.on("mouseup", this.onMouseUp, this);
7249 btn.on(this.clickEvent, this.onClick, this);
7251 if(typeof this.tooltip == 'object'){
7252 Roo.QuickTips.tips(Roo.apply({
7256 btnEl.dom[this.tooltipType] = this.tooltip;
7259 if(this.arrowTooltip){
7260 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7269 this.el.addClass("x-btn-pressed");
7271 if(Roo.isIE && !Roo.isIE7){
7272 this.autoWidth.defer(1, this);
7277 this.menu.on("show", this.onMenuShow, this);
7278 this.menu.on("hide", this.onMenuHide, this);
7280 this.fireEvent('render', this);
7284 autoWidth : function(){
7286 var tbl = this.el.child("table:first");
7287 var tbl2 = this.el.child("table:last");
7288 this.el.setWidth("auto");
7289 tbl.setWidth("auto");
7290 if(Roo.isIE7 && Roo.isStrict){
7291 var ib = this.el.child('button:first');
7292 if(ib && ib.getWidth() > 20){
7294 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7299 this.el.beginMeasure();
7301 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7302 tbl.setWidth(this.minWidth-tbl2.getWidth());
7305 this.el.endMeasure();
7308 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7312 * Sets this button's click handler
7313 * @param {Function} handler The function to call when the button is clicked
7314 * @param {Object} scope (optional) Scope for the function passed above
7316 setHandler : function(handler, scope){
7317 this.handler = handler;
7322 * Sets this button's arrow click handler
7323 * @param {Function} handler The function to call when the arrow is clicked
7324 * @param {Object} scope (optional) Scope for the function passed above
7326 setArrowHandler : function(handler, scope){
7327 this.arrowHandler = handler;
7336 this.el.child("button:first").focus();
7341 onClick : function(e){
7344 if(e.getTarget(".x-btn-menu-arrow-wrap")){
7345 if(this.menu && !this.menu.isVisible()){
7346 this.menu.show(this.el, this.menuAlign);
7348 this.fireEvent("arrowclick", this, e);
7349 if(this.arrowHandler){
7350 this.arrowHandler.call(this.scope || this, this, e);
7353 this.fireEvent("click", this, e);
7355 this.handler.call(this.scope || this, this, e);
7361 onMouseDown : function(e){
7363 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7367 onMouseUp : function(e){
7368 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7374 Roo.MenuButton = Roo.SplitButton;/*
7376 * Ext JS Library 1.1.1
7377 * Copyright(c) 2006-2007, Ext JS, LLC.
7379 * Originally Released Under LGPL - original licence link has changed is not relivant.
7382 * <script type="text/javascript">
7386 * @class Roo.Toolbar
7387 * Basic Toolbar class.
7389 * Creates a new Toolbar
7390 * @param {Object} container The config object
7392 Roo.Toolbar = function(container, buttons, config)
7394 /// old consturctor format still supported..
7395 if(container instanceof Array){ // omit the container for later rendering
7396 buttons = container;
7400 if (typeof(container) == 'object' && container.xtype) {
7402 container = config.container;
7403 buttons = config.buttons || []; // not really - use items!!
7406 if (config && config.items) {
7407 xitems = config.items;
7408 delete config.items;
7410 Roo.apply(this, config);
7411 this.buttons = buttons;
7414 this.render(container);
7416 this.xitems = xitems;
7417 Roo.each(xitems, function(b) {
7423 Roo.Toolbar.prototype = {
7425 * @cfg {Array} items
7426 * array of button configs or elements to add (will be converted to a MixedCollection)
7430 * @cfg {String/HTMLElement/Element} container
7431 * The id or element that will contain the toolbar
7434 render : function(ct){
7435 this.el = Roo.get(ct);
7437 this.el.addClass(this.cls);
7439 // using a table allows for vertical alignment
7440 // 100% width is needed by Safari...
7441 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7442 this.tr = this.el.child("tr", true);
7444 this.items = new Roo.util.MixedCollection(false, function(o){
7445 return o.id || ("item" + (++autoId));
7448 this.add.apply(this, this.buttons);
7449 delete this.buttons;
7454 * Adds element(s) to the toolbar -- this function takes a variable number of
7455 * arguments of mixed type and adds them to the toolbar.
7456 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7458 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7459 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7460 * <li>Field: Any form field (equivalent to {@link #addField})</li>
7461 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7462 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7463 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7464 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7465 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7466 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7468 * @param {Mixed} arg2
7469 * @param {Mixed} etc.
7472 var a = arguments, l = a.length;
7473 for(var i = 0; i < l; i++){
7478 _add : function(el) {
7481 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7484 if (el.applyTo){ // some kind of form field
7485 return this.addField(el);
7487 if (el.render){ // some kind of Toolbar.Item
7488 return this.addItem(el);
7490 if (typeof el == "string"){ // string
7491 if(el == "separator" || el == "-"){
7492 return this.addSeparator();
7495 return this.addSpacer();
7498 return this.addFill();
7500 return this.addText(el);
7503 if(el.tagName){ // element
7504 return this.addElement(el);
7506 if(typeof el == "object"){ // must be button config?
7507 return this.addButton(el);
7515 * Add an Xtype element
7516 * @param {Object} xtype Xtype Object
7517 * @return {Object} created Object
7519 addxtype : function(e){
7524 * Returns the Element for this toolbar.
7525 * @return {Roo.Element}
7533 * @return {Roo.Toolbar.Item} The separator item
7535 addSeparator : function(){
7536 return this.addItem(new Roo.Toolbar.Separator());
7540 * Adds a spacer element
7541 * @return {Roo.Toolbar.Spacer} The spacer item
7543 addSpacer : function(){
7544 return this.addItem(new Roo.Toolbar.Spacer());
7548 * Adds a fill element that forces subsequent additions to the right side of the toolbar
7549 * @return {Roo.Toolbar.Fill} The fill item
7551 addFill : function(){
7552 return this.addItem(new Roo.Toolbar.Fill());
7556 * Adds any standard HTML element to the toolbar
7557 * @param {String/HTMLElement/Element} el The element or id of the element to add
7558 * @return {Roo.Toolbar.Item} The element's item
7560 addElement : function(el){
7561 return this.addItem(new Roo.Toolbar.Item(el));
7564 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7565 * @type Roo.util.MixedCollection
7570 * Adds any Toolbar.Item or subclass
7571 * @param {Roo.Toolbar.Item} item
7572 * @return {Roo.Toolbar.Item} The item
7574 addItem : function(item){
7575 var td = this.nextBlock();
7577 this.items.add(item);
7582 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7583 * @param {Object/Array} config A button config or array of configs
7584 * @return {Roo.Toolbar.Button/Array}
7586 addButton : function(config){
7587 if(config instanceof Array){
7589 for(var i = 0, len = config.length; i < len; i++) {
7590 buttons.push(this.addButton(config[i]));
7595 if(!(config instanceof Roo.Toolbar.Button)){
7597 new Roo.Toolbar.SplitButton(config) :
7598 new Roo.Toolbar.Button(config);
7600 var td = this.nextBlock();
7607 * Adds text to the toolbar
7608 * @param {String} text The text to add
7609 * @return {Roo.Toolbar.Item} The element's item
7611 addText : function(text){
7612 return this.addItem(new Roo.Toolbar.TextItem(text));
7616 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7617 * @param {Number} index The index where the item is to be inserted
7618 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7619 * @return {Roo.Toolbar.Button/Item}
7621 insertButton : function(index, item){
7622 if(item instanceof Array){
7624 for(var i = 0, len = item.length; i < len; i++) {
7625 buttons.push(this.insertButton(index + i, item[i]));
7629 if (!(item instanceof Roo.Toolbar.Button)){
7630 item = new Roo.Toolbar.Button(item);
7632 var td = document.createElement("td");
7633 this.tr.insertBefore(td, this.tr.childNodes[index]);
7635 this.items.insert(index, item);
7640 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7641 * @param {Object} config
7642 * @return {Roo.Toolbar.Item} The element's item
7644 addDom : function(config, returnEl){
7645 var td = this.nextBlock();
7646 Roo.DomHelper.overwrite(td, config);
7647 var ti = new Roo.Toolbar.Item(td.firstChild);
7654 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7655 * @type Roo.util.MixedCollection
7660 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7661 * Note: the field should not have been rendered yet. For a field that has already been
7662 * rendered, use {@link #addElement}.
7663 * @param {Roo.form.Field} field
7664 * @return {Roo.ToolbarItem}
7668 addField : function(field) {
7671 this.fields = new Roo.util.MixedCollection(false, function(o){
7672 return o.id || ("item" + (++autoId));
7677 var td = this.nextBlock();
7679 var ti = new Roo.Toolbar.Item(td.firstChild);
7682 this.fields.add(field);
7693 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7694 this.el.child('div').hide();
7702 this.el.child('div').show();
7706 nextBlock : function(){
7707 var td = document.createElement("td");
7708 this.tr.appendChild(td);
7713 destroy : function(){
7714 if(this.items){ // rendered?
7715 Roo.destroy.apply(Roo, this.items.items);
7717 if(this.fields){ // rendered?
7718 Roo.destroy.apply(Roo, this.fields.items);
7720 Roo.Element.uncache(this.el, this.tr);
7725 * @class Roo.Toolbar.Item
7726 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7728 * Creates a new Item
7729 * @param {HTMLElement} el
7731 Roo.Toolbar.Item = function(el){
7733 if (typeof (el.xtype) != 'undefined') {
7738 this.el = Roo.getDom(el);
7739 this.id = Roo.id(this.el);
7740 this.hidden = false;
7745 * Fires when the button is rendered
7746 * @param {Button} this
7750 Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7752 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7753 //Roo.Toolbar.Item.prototype = {
7756 * Get this item's HTML Element
7757 * @return {HTMLElement}
7764 render : function(td){
7767 td.appendChild(this.el);
7769 this.fireEvent('render', this);
7773 * Removes and destroys this item.
7775 destroy : function(){
7776 this.td.parentNode.removeChild(this.td);
7783 this.hidden = false;
7784 this.td.style.display = "";
7792 this.td.style.display = "none";
7796 * Convenience function for boolean show/hide.
7797 * @param {Boolean} visible true to show/false to hide
7799 setVisible: function(visible){
7808 * Try to focus this item.
7811 Roo.fly(this.el).focus();
7815 * Disables this item.
7817 disable : function(){
7818 Roo.fly(this.td).addClass("x-item-disabled");
7819 this.disabled = true;
7820 this.el.disabled = true;
7824 * Enables this item.
7826 enable : function(){
7827 Roo.fly(this.td).removeClass("x-item-disabled");
7828 this.disabled = false;
7829 this.el.disabled = false;
7835 * @class Roo.Toolbar.Separator
7836 * @extends Roo.Toolbar.Item
7837 * A simple toolbar separator class
7839 * Creates a new Separator
7841 Roo.Toolbar.Separator = function(cfg){
7843 var s = document.createElement("span");
7844 s.className = "ytb-sep";
7849 Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7851 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7853 disable:Roo.emptyFn,
7858 * @class Roo.Toolbar.Spacer
7859 * @extends Roo.Toolbar.Item
7860 * A simple element that adds extra horizontal space to a toolbar.
7862 * Creates a new Spacer
7864 Roo.Toolbar.Spacer = function(cfg){
7865 var s = document.createElement("div");
7866 s.className = "ytb-spacer";
7870 Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7872 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7874 disable:Roo.emptyFn,
7879 * @class Roo.Toolbar.Fill
7880 * @extends Roo.Toolbar.Spacer
7881 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7883 * Creates a new Spacer
7885 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7887 render : function(td){
7888 td.style.width = '100%';
7889 Roo.Toolbar.Fill.superclass.render.call(this, td);
7894 * @class Roo.Toolbar.TextItem
7895 * @extends Roo.Toolbar.Item
7896 * A simple class that renders text directly into a toolbar.
7898 * Creates a new TextItem
7899 * @param {String} text
7901 Roo.Toolbar.TextItem = function(cfg){
7902 var text = cfg || "";
7903 if (typeof(cfg) == 'object') {
7904 text = cfg.text || "";
7908 var s = document.createElement("span");
7909 s.className = "ytb-text";
7915 Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg || s);
7917 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7921 disable:Roo.emptyFn,
7926 * @class Roo.Toolbar.Button
7927 * @extends Roo.Button
7928 * A button that renders into a toolbar.
7930 * Creates a new Button
7931 * @param {Object} config A standard {@link Roo.Button} config object
7933 Roo.Toolbar.Button = function(config){
7934 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7936 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7937 render : function(td){
7939 Roo.Toolbar.Button.superclass.render.call(this, td);
7943 * Removes and destroys this button
7945 destroy : function(){
7946 Roo.Toolbar.Button.superclass.destroy.call(this);
7947 this.td.parentNode.removeChild(this.td);
7954 this.hidden = false;
7955 this.td.style.display = "";
7963 this.td.style.display = "none";
7967 * Disables this item
7969 disable : function(){
7970 Roo.fly(this.td).addClass("x-item-disabled");
7971 this.disabled = true;
7977 enable : function(){
7978 Roo.fly(this.td).removeClass("x-item-disabled");
7979 this.disabled = false;
7983 Roo.ToolbarButton = Roo.Toolbar.Button;
7986 * @class Roo.Toolbar.SplitButton
7987 * @extends Roo.SplitButton
7988 * A menu button that renders into a toolbar.
7990 * Creates a new SplitButton
7991 * @param {Object} config A standard {@link Roo.SplitButton} config object
7993 Roo.Toolbar.SplitButton = function(config){
7994 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7996 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7997 render : function(td){
7999 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8003 * Removes and destroys this button
8005 destroy : function(){
8006 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8007 this.td.parentNode.removeChild(this.td);
8014 this.hidden = false;
8015 this.td.style.display = "";
8023 this.td.style.display = "none";
8028 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8030 * Ext JS Library 1.1.1
8031 * Copyright(c) 2006-2007, Ext JS, LLC.
8033 * Originally Released Under LGPL - original licence link has changed is not relivant.
8036 * <script type="text/javascript">
8040 * @class Roo.PagingToolbar
8041 * @extends Roo.Toolbar
8042 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8044 * Create a new PagingToolbar
8045 * @param {Object} config The config object
8047 Roo.PagingToolbar = function(el, ds, config)
8049 // old args format still supported... - xtype is prefered..
8050 if (typeof(el) == 'object' && el.xtype) {
8051 // created from xtype...
8054 el = config.container;
8058 items = config.items;
8062 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8065 this.renderButtons(this.el);
8068 // supprot items array.
8070 Roo.each(items, function(e) {
8071 this.add(Roo.factory(e));
8076 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8078 * @cfg {Roo.data.Store} dataSource
8079 * The underlying data store providing the paged data
8082 * @cfg {String/HTMLElement/Element} container
8083 * container The id or element that will contain the toolbar
8086 * @cfg {Boolean} displayInfo
8087 * True to display the displayMsg (defaults to false)
8090 * @cfg {Number} pageSize
8091 * The number of records to display per page (defaults to 20)
8095 * @cfg {String} displayMsg
8096 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8098 displayMsg : 'Displaying {0} - {1} of {2}',
8100 * @cfg {String} emptyMsg
8101 * The message to display when no records are found (defaults to "No data to display")
8103 emptyMsg : 'No data to display',
8105 * Customizable piece of the default paging text (defaults to "Page")
8108 beforePageText : "Page",
8110 * Customizable piece of the default paging text (defaults to "of %0")
8113 afterPageText : "of {0}",
8115 * Customizable piece of the default paging text (defaults to "First Page")
8118 firstText : "First Page",
8120 * Customizable piece of the default paging text (defaults to "Previous Page")
8123 prevText : "Previous Page",
8125 * Customizable piece of the default paging text (defaults to "Next Page")
8128 nextText : "Next Page",
8130 * Customizable piece of the default paging text (defaults to "Last Page")
8133 lastText : "Last Page",
8135 * Customizable piece of the default paging text (defaults to "Refresh")
8138 refreshText : "Refresh",
8141 renderButtons : function(el){
8142 Roo.PagingToolbar.superclass.render.call(this, el);
8143 this.first = this.addButton({
8144 tooltip: this.firstText,
8145 cls: "x-btn-icon x-grid-page-first",
8147 handler: this.onClick.createDelegate(this, ["first"])
8149 this.prev = this.addButton({
8150 tooltip: this.prevText,
8151 cls: "x-btn-icon x-grid-page-prev",
8153 handler: this.onClick.createDelegate(this, ["prev"])
8155 //this.addSeparator();
8156 this.add(this.beforePageText);
8157 this.field = Roo.get(this.addDom({
8162 cls: "x-grid-page-number"
8164 this.field.on("keydown", this.onPagingKeydown, this);
8165 this.field.on("focus", function(){this.dom.select();});
8166 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8167 this.field.setHeight(18);
8168 //this.addSeparator();
8169 this.next = this.addButton({
8170 tooltip: this.nextText,
8171 cls: "x-btn-icon x-grid-page-next",
8173 handler: this.onClick.createDelegate(this, ["next"])
8175 this.last = this.addButton({
8176 tooltip: this.lastText,
8177 cls: "x-btn-icon x-grid-page-last",
8179 handler: this.onClick.createDelegate(this, ["last"])
8181 //this.addSeparator();
8182 this.loading = this.addButton({
8183 tooltip: this.refreshText,
8184 cls: "x-btn-icon x-grid-loading",
8185 handler: this.onClick.createDelegate(this, ["refresh"])
8188 if(this.displayInfo){
8189 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8194 updateInfo : function(){
8196 var count = this.ds.getCount();
8197 var msg = count == 0 ?
8201 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
8203 this.displayEl.update(msg);
8208 onLoad : function(ds, r, o){
8209 this.cursor = o.params ? o.params.start : 0;
8210 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8212 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8213 this.field.dom.value = ap;
8214 this.first.setDisabled(ap == 1);
8215 this.prev.setDisabled(ap == 1);
8216 this.next.setDisabled(ap == ps);
8217 this.last.setDisabled(ap == ps);
8218 this.loading.enable();
8223 getPageData : function(){
8224 var total = this.ds.getTotalCount();
8227 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8228 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8233 onLoadError : function(){
8234 this.loading.enable();
8238 onPagingKeydown : function(e){
8240 var d = this.getPageData();
8242 var v = this.field.dom.value, pageNum;
8243 if(!v || isNaN(pageNum = parseInt(v, 10))){
8244 this.field.dom.value = d.activePage;
8247 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8248 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8251 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))
8253 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8254 this.field.dom.value = pageNum;
8255 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8258 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8260 var v = this.field.dom.value, pageNum;
8261 var increment = (e.shiftKey) ? 10 : 1;
8262 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8265 if(!v || isNaN(pageNum = parseInt(v, 10))) {
8266 this.field.dom.value = d.activePage;
8269 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8271 this.field.dom.value = parseInt(v, 10) + increment;
8272 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8273 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8280 beforeLoad : function(){
8282 this.loading.disable();
8287 onClick : function(which){
8291 ds.load({params:{start: 0, limit: this.pageSize}});
8294 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8297 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8300 var total = ds.getTotalCount();
8301 var extra = total % this.pageSize;
8302 var lastStart = extra ? (total - extra) : total-this.pageSize;
8303 ds.load({params:{start: lastStart, limit: this.pageSize}});
8306 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8312 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8313 * @param {Roo.data.Store} store The data store to unbind
8315 unbind : function(ds){
8316 ds.un("beforeload", this.beforeLoad, this);
8317 ds.un("load", this.onLoad, this);
8318 ds.un("loadexception", this.onLoadError, this);
8319 ds.un("remove", this.updateInfo, this);
8320 ds.un("add", this.updateInfo, this);
8321 this.ds = undefined;
8325 * Binds the paging toolbar to the specified {@link Roo.data.Store}
8326 * @param {Roo.data.Store} store The data store to bind
8328 bind : function(ds){
8329 ds.on("beforeload", this.beforeLoad, this);
8330 ds.on("load", this.onLoad, this);
8331 ds.on("loadexception", this.onLoadError, this);
8332 ds.on("remove", this.updateInfo, this);
8333 ds.on("add", this.updateInfo, this);
8338 * Ext JS Library 1.1.1
8339 * Copyright(c) 2006-2007, Ext JS, LLC.
8341 * Originally Released Under LGPL - original licence link has changed is not relivant.
8344 * <script type="text/javascript">
8348 * @class Roo.Resizable
8349 * @extends Roo.util.Observable
8350 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8351 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8352 * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
8353 * the element will be wrapped for you automatically.</p>
8354 * <p>Here is the list of valid resize handles:</p>
8357 ------ -------------------
8366 'hd' horizontal drag
8369 * <p>Here's an example showing the creation of a typical Resizable:</p>
8371 var resizer = new Roo.Resizable("element-id", {
8379 resizer.on("resize", myHandler);
8381 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8382 * resizer.east.setDisplayed(false);</p>
8383 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8384 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8385 * resize operation's new size (defaults to [0, 0])
8386 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8387 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8388 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8389 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8390 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8391 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8392 * @cfg {Number} width The width of the element in pixels (defaults to null)
8393 * @cfg {Number} height The height of the element in pixels (defaults to null)
8394 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8395 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8396 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8397 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8398 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
8399 * in favor of the handles config option (defaults to false)
8400 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8401 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8402 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8403 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8404 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8405 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8406 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8407 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8408 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8409 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8410 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8412 * Create a new resizable component
8413 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8414 * @param {Object} config configuration options
8416 Roo.Resizable = function(el, config)
8418 this.el = Roo.get(el);
8420 if(config && config.wrap){
8421 config.resizeChild = this.el;
8422 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8423 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8424 this.el.setStyle("overflow", "hidden");
8425 this.el.setPositioning(config.resizeChild.getPositioning());
8426 config.resizeChild.clearPositioning();
8427 if(!config.width || !config.height){
8428 var csize = config.resizeChild.getSize();
8429 this.el.setSize(csize.width, csize.height);
8431 if(config.pinned && !config.adjustments){
8432 config.adjustments = "auto";
8436 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8437 this.proxy.unselectable();
8438 this.proxy.enableDisplayMode('block');
8440 Roo.apply(this, config);
8443 this.disableTrackOver = true;
8444 this.el.addClass("x-resizable-pinned");
8446 // if the element isn't positioned, make it relative
8447 var position = this.el.getStyle("position");
8448 if(position != "absolute" && position != "fixed"){
8449 this.el.setStyle("position", "relative");
8451 if(!this.handles){ // no handles passed, must be legacy style
8452 this.handles = 's,e,se';
8453 if(this.multiDirectional){
8454 this.handles += ',n,w';
8457 if(this.handles == "all"){
8458 this.handles = "n s e w ne nw se sw";
8460 var hs = this.handles.split(/\s*?[,;]\s*?| /);
8461 var ps = Roo.Resizable.positions;
8462 for(var i = 0, len = hs.length; i < len; i++){
8463 if(hs[i] && ps[hs[i]]){
8464 var pos = ps[hs[i]];
8465 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8469 this.corner = this.southeast;
8471 // updateBox = the box can move..
8472 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8473 this.updateBox = true;
8476 this.activeHandle = null;
8478 if(this.resizeChild){
8479 if(typeof this.resizeChild == "boolean"){
8480 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8482 this.resizeChild = Roo.get(this.resizeChild, true);
8486 if(this.adjustments == "auto"){
8487 var rc = this.resizeChild;
8488 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8489 if(rc && (hw || hn)){
8490 rc.position("relative");
8491 rc.setLeft(hw ? hw.el.getWidth() : 0);
8492 rc.setTop(hn ? hn.el.getHeight() : 0);
8494 this.adjustments = [
8495 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8496 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8501 this.dd = this.dynamic ?
8502 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8503 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8509 * @event beforeresize
8510 * Fired before resize is allowed. Set enabled to false to cancel resize.
8511 * @param {Roo.Resizable} this
8512 * @param {Roo.EventObject} e The mousedown event
8514 "beforeresize" : true,
8518 * @param {Roo.Resizable} this
8519 * @param {Number} x The new x position
8520 * @param {Number} y The new y position
8521 * @param {Number} w The new w width
8522 * @param {Number} h The new h hight
8523 * @param {Roo.EventObject} e The mouseup event
8528 * Fired after a resize.
8529 * @param {Roo.Resizable} this
8530 * @param {Number} width The new width
8531 * @param {Number} height The new height
8532 * @param {Roo.EventObject} e The mouseup event
8537 if(this.width !== null && this.height !== null){
8538 this.resizeTo(this.width, this.height);
8540 this.updateChildSize();
8543 this.el.dom.style.zoom = 1;
8545 Roo.Resizable.superclass.constructor.call(this);
8548 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8549 resizeChild : false,
8550 adjustments : [0, 0],
8560 multiDirectional : false,
8561 disableTrackOver : false,
8562 easing : 'easeOutStrong',
8564 heightIncrement : 0,
8568 preserveRatio : false,
8575 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8577 constrainTo: undefined,
8579 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8581 resizeRegion: undefined,
8585 * Perform a manual resize
8586 * @param {Number} width
8587 * @param {Number} height
8589 resizeTo : function(width, height){
8590 this.el.setSize(width, height);
8591 this.updateChildSize();
8592 this.fireEvent("resize", this, width, height, null);
8596 startSizing : function(e, handle){
8597 this.fireEvent("beforeresize", this, e);
8598 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8601 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
8602 this.overlay.unselectable();
8603 this.overlay.enableDisplayMode("block");
8604 this.overlay.on("mousemove", this.onMouseMove, this);
8605 this.overlay.on("mouseup", this.onMouseUp, this);
8607 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8609 this.resizing = true;
8610 this.startBox = this.el.getBox();
8611 this.startPoint = e.getXY();
8612 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8613 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8615 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8616 this.overlay.show();
8618 if(this.constrainTo) {
8619 var ct = Roo.get(this.constrainTo);
8620 this.resizeRegion = ct.getRegion().adjust(
8621 ct.getFrameWidth('t'),
8622 ct.getFrameWidth('l'),
8623 -ct.getFrameWidth('b'),
8624 -ct.getFrameWidth('r')
8628 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8630 this.proxy.setBox(this.startBox);
8632 this.proxy.setStyle('visibility', 'visible');
8638 onMouseDown : function(handle, e){
8641 this.activeHandle = handle;
8642 this.startSizing(e, handle);
8647 onMouseUp : function(e){
8648 var size = this.resizeElement();
8649 this.resizing = false;
8651 this.overlay.hide();
8653 this.fireEvent("resize", this, size.width, size.height, e);
8657 updateChildSize : function(){
8659 if(this.resizeChild){
8661 var child = this.resizeChild;
8662 var adj = this.adjustments;
8663 if(el.dom.offsetWidth){
8664 var b = el.getSize(true);
8665 child.setSize(b.width+adj[0], b.height+adj[1]);
8667 // Second call here for IE
8668 // The first call enables instant resizing and
8669 // the second call corrects scroll bars if they
8672 setTimeout(function(){
8673 if(el.dom.offsetWidth){
8674 var b = el.getSize(true);
8675 child.setSize(b.width+adj[0], b.height+adj[1]);
8683 snap : function(value, inc, min){
8684 if(!inc || !value) {
8687 var newValue = value;
8688 var m = value % inc;
8691 newValue = value + (inc-m);
8693 newValue = value - m;
8696 return Math.max(min, newValue);
8700 resizeElement : function(){
8701 var box = this.proxy.getBox();
8703 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8705 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8707 this.updateChildSize();
8715 constrain : function(v, diff, m, mx){
8718 }else if(v - diff > mx){
8725 onMouseMove : function(e){
8728 try{// try catch so if something goes wrong the user doesn't get hung
8730 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8734 //var curXY = this.startPoint;
8735 var curSize = this.curSize || this.startBox;
8736 var x = this.startBox.x, y = this.startBox.y;
8738 var w = curSize.width, h = curSize.height;
8740 var mw = this.minWidth, mh = this.minHeight;
8741 var mxw = this.maxWidth, mxh = this.maxHeight;
8742 var wi = this.widthIncrement;
8743 var hi = this.heightIncrement;
8745 var eventXY = e.getXY();
8746 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8747 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8749 var pos = this.activeHandle.position;
8754 w = Math.min(Math.max(mw, w), mxw);
8759 h = Math.min(Math.max(mh, h), mxh);
8764 w = Math.min(Math.max(mw, w), mxw);
8765 h = Math.min(Math.max(mh, h), mxh);
8768 diffY = this.constrain(h, diffY, mh, mxh);
8775 var adiffX = Math.abs(diffX);
8776 var sub = (adiffX % wi); // how much
8777 if (sub > (wi/2)) { // far enough to snap
8778 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8780 // remove difference..
8781 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8785 x = Math.max(this.minX, x);
8788 diffX = this.constrain(w, diffX, mw, mxw);
8794 w = Math.min(Math.max(mw, w), mxw);
8795 diffY = this.constrain(h, diffY, mh, mxh);
8800 diffX = this.constrain(w, diffX, mw, mxw);
8801 diffY = this.constrain(h, diffY, mh, mxh);
8808 diffX = this.constrain(w, diffX, mw, mxw);
8810 h = Math.min(Math.max(mh, h), mxh);
8816 var sw = this.snap(w, wi, mw);
8817 var sh = this.snap(h, hi, mh);
8818 if(sw != w || sh != h){
8841 if(this.preserveRatio){
8846 h = Math.min(Math.max(mh, h), mxh);
8851 w = Math.min(Math.max(mw, w), mxw);
8856 w = Math.min(Math.max(mw, w), mxw);
8862 w = Math.min(Math.max(mw, w), mxw);
8868 h = Math.min(Math.max(mh, h), mxh);
8876 h = Math.min(Math.max(mh, h), mxh);
8886 h = Math.min(Math.max(mh, h), mxh);
8894 if (pos == 'hdrag') {
8897 this.proxy.setBounds(x, y, w, h);
8899 this.resizeElement();
8903 this.fireEvent("resizing", this, x, y, w, h, e);
8907 handleOver : function(){
8909 this.el.addClass("x-resizable-over");
8914 handleOut : function(){
8916 this.el.removeClass("x-resizable-over");
8921 * Returns the element this component is bound to.
8922 * @return {Roo.Element}
8929 * Returns the resizeChild element (or null).
8930 * @return {Roo.Element}
8932 getResizeChild : function(){
8933 return this.resizeChild;
8935 groupHandler : function()
8940 * Destroys this resizable. If the element was wrapped and
8941 * removeEl is not true then the element remains.
8942 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8944 destroy : function(removeEl){
8945 this.proxy.remove();
8947 this.overlay.removeAllListeners();
8948 this.overlay.remove();
8950 var ps = Roo.Resizable.positions;
8952 if(typeof ps[k] != "function" && this[ps[k]]){
8953 var h = this[ps[k]];
8954 h.el.removeAllListeners();
8966 // hash to map config positions to true positions
8967 Roo.Resizable.positions = {
8968 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
8973 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8975 // only initialize the template if resizable is used
8976 var tpl = Roo.DomHelper.createTemplate(
8977 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8980 Roo.Resizable.Handle.prototype.tpl = tpl;
8982 this.position = pos;
8984 // show north drag fro topdra
8985 var handlepos = pos == 'hdrag' ? 'north' : pos;
8987 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8988 if (pos == 'hdrag') {
8989 this.el.setStyle('cursor', 'pointer');
8991 this.el.unselectable();
8993 this.el.setOpacity(0);
8995 this.el.on("mousedown", this.onMouseDown, this);
8996 if(!disableTrackOver){
8997 this.el.on("mouseover", this.onMouseOver, this);
8998 this.el.on("mouseout", this.onMouseOut, this);
9003 Roo.Resizable.Handle.prototype = {
9004 afterResize : function(rz){
9009 onMouseDown : function(e){
9010 this.rz.onMouseDown(this, e);
9013 onMouseOver : function(e){
9014 this.rz.handleOver(this, e);
9017 onMouseOut : function(e){
9018 this.rz.handleOut(this, e);
9022 * Ext JS Library 1.1.1
9023 * Copyright(c) 2006-2007, Ext JS, LLC.
9025 * Originally Released Under LGPL - original licence link has changed is not relivant.
9028 * <script type="text/javascript">
9033 * @extends Roo.Component
9034 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9036 * Create a new Editor
9037 * @param {Roo.form.Field} field The Field object (or descendant)
9038 * @param {Object} config The config object
9040 Roo.Editor = function(field, config){
9041 Roo.Editor.superclass.constructor.call(this, config);
9045 * @event beforestartedit
9046 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
9047 * false from the handler of this event.
9048 * @param {Editor} this
9049 * @param {Roo.Element} boundEl The underlying element bound to this editor
9050 * @param {Mixed} value The field value being set
9052 "beforestartedit" : true,
9055 * Fires when this editor is displayed
9056 * @param {Roo.Element} boundEl The underlying element bound to this editor
9057 * @param {Mixed} value The starting field value
9061 * @event beforecomplete
9062 * Fires after a change has been made to the field, but before the change is reflected in the underlying
9063 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
9064 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9065 * event will not fire since no edit actually occurred.
9066 * @param {Editor} this
9067 * @param {Mixed} value The current field value
9068 * @param {Mixed} startValue The original field value
9070 "beforecomplete" : true,
9073 * Fires after editing is complete and any changed value has been written to the underlying field.
9074 * @param {Editor} this
9075 * @param {Mixed} value The current field value
9076 * @param {Mixed} startValue The original field value
9081 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9082 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9083 * @param {Roo.form.Field} this
9084 * @param {Roo.EventObject} e The event object
9090 Roo.extend(Roo.Editor, Roo.Component, {
9092 * @cfg {Boolean/String} autosize
9093 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9094 * or "height" to adopt the height only (defaults to false)
9097 * @cfg {Boolean} revertInvalid
9098 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9099 * validation fails (defaults to true)
9102 * @cfg {Boolean} ignoreNoChange
9103 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9104 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
9105 * will never be ignored.
9108 * @cfg {Boolean} hideEl
9109 * False to keep the bound element visible while the editor is displayed (defaults to true)
9112 * @cfg {Mixed} value
9113 * The data value of the underlying field (defaults to "")
9117 * @cfg {String} alignment
9118 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9122 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9123 * for bottom-right shadow (defaults to "frame")
9127 * @cfg {Boolean} constrain True to constrain the editor to the viewport
9131 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9133 completeOnEnter : false,
9135 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9137 cancelOnEsc : false,
9139 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9144 onRender : function(ct, position){
9145 this.el = new Roo.Layer({
9146 shadow: this.shadow,
9152 constrain: this.constrain
9154 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9155 if(this.field.msgTarget != 'title'){
9156 this.field.msgTarget = 'qtip';
9158 this.field.render(this.el);
9160 this.field.el.dom.setAttribute('autocomplete', 'off');
9162 this.field.on("specialkey", this.onSpecialKey, this);
9163 if(this.swallowKeys){
9164 this.field.el.swallowEvent(['keydown','keypress']);
9167 this.field.on("blur", this.onBlur, this);
9168 if(this.field.grow){
9169 this.field.on("autosize", this.el.sync, this.el, {delay:1});
9173 onSpecialKey : function(field, e)
9175 //Roo.log('editor onSpecialKey');
9176 if(this.completeOnEnter && e.getKey() == e.ENTER){
9178 this.completeEdit();
9181 // do not fire special key otherwise it might hide close the editor...
9182 if(e.getKey() == e.ENTER){
9185 if(this.cancelOnEsc && e.getKey() == e.ESC){
9189 this.fireEvent('specialkey', field, e);
9194 * Starts the editing process and shows the editor.
9195 * @param {String/HTMLElement/Element} el The element to edit
9196 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9197 * to the innerHTML of el.
9199 startEdit : function(el, value){
9201 this.completeEdit();
9203 this.boundEl = Roo.get(el);
9204 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9206 this.render(this.parentEl || document.body);
9208 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9211 this.startValue = v;
9212 this.field.setValue(v);
9214 var sz = this.boundEl.getSize();
9215 switch(this.autoSize){
9217 this.setSize(sz.width, "");
9220 this.setSize("", sz.height);
9223 this.setSize(sz.width, sz.height);
9226 this.el.alignTo(this.boundEl, this.alignment);
9227 this.editing = true;
9229 Roo.QuickTips.disable();
9235 * Sets the height and width of this editor.
9236 * @param {Number} width The new width
9237 * @param {Number} height The new height
9239 setSize : function(w, h){
9240 this.field.setSize(w, h);
9247 * Realigns the editor to the bound field based on the current alignment config value.
9249 realign : function(){
9250 this.el.alignTo(this.boundEl, this.alignment);
9254 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9255 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9257 completeEdit : function(remainVisible){
9261 var v = this.getValue();
9262 if(this.revertInvalid !== false && !this.field.isValid()){
9263 v = this.startValue;
9264 this.cancelEdit(true);
9266 if(String(v) === String(this.startValue) && this.ignoreNoChange){
9267 this.editing = false;
9271 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9272 this.editing = false;
9273 if(this.updateEl && this.boundEl){
9274 this.boundEl.update(v);
9276 if(remainVisible !== true){
9279 this.fireEvent("complete", this, v, this.startValue);
9284 onShow : function(){
9286 if(this.hideEl !== false){
9287 this.boundEl.hide();
9290 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9291 this.fixIEFocus = true;
9292 this.deferredFocus.defer(50, this);
9296 this.fireEvent("startedit", this.boundEl, this.startValue);
9299 deferredFocus : function(){
9306 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
9307 * reverted to the original starting value.
9308 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9309 * cancel (defaults to false)
9311 cancelEdit : function(remainVisible){
9313 this.setValue(this.startValue);
9314 if(remainVisible !== true){
9321 onBlur : function(){
9322 if(this.allowBlur !== true && this.editing){
9323 this.completeEdit();
9328 onHide : function(){
9330 this.completeEdit();
9334 if(this.field.collapse){
9335 this.field.collapse();
9338 if(this.hideEl !== false){
9339 this.boundEl.show();
9342 Roo.QuickTips.enable();
9347 * Sets the data value of the editor
9348 * @param {Mixed} value Any valid value supported by the underlying field
9350 setValue : function(v){
9351 this.field.setValue(v);
9355 * Gets the data value of the editor
9356 * @return {Mixed} The data value
9358 getValue : function(){
9359 return this.field.getValue();
9363 * Ext JS Library 1.1.1
9364 * Copyright(c) 2006-2007, Ext JS, LLC.
9366 * Originally Released Under LGPL - original licence link has changed is not relivant.
9369 * <script type="text/javascript">
9373 * @class Roo.BasicDialog
9374 * @extends Roo.util.Observable
9375 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
9377 var dlg = new Roo.BasicDialog("my-dlg", {
9386 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9387 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
9388 dlg.addButton('Cancel', dlg.hide, dlg);
9391 <b>A Dialog should always be a direct child of the body element.</b>
9392 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9393 * @cfg {String} title Default text to display in the title bar (defaults to null)
9394 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9395 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9396 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9397 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9398 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9399 * (defaults to null with no animation)
9400 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9401 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9402 * property for valid values (defaults to 'all')
9403 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9404 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9405 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9406 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9407 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9408 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9409 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9410 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9411 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9412 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9413 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9414 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9415 * draggable = true (defaults to false)
9416 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9417 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9418 * shadow (defaults to false)
9419 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9420 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9421 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9422 * @cfg {Array} buttons Array of buttons
9423 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9425 * Create a new BasicDialog.
9426 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9427 * @param {Object} config Configuration options
9429 Roo.BasicDialog = function(el, config){
9430 this.el = Roo.get(el);
9431 var dh = Roo.DomHelper;
9432 if(!this.el && config && config.autoCreate){
9433 if(typeof config.autoCreate == "object"){
9434 if(!config.autoCreate.id){
9435 config.autoCreate.id = el;
9437 this.el = dh.append(document.body,
9438 config.autoCreate, true);
9440 this.el = dh.append(document.body,
9441 {tag: "div", id: el, style:'visibility:hidden;'}, true);
9445 el.setDisplayed(true);
9446 el.hide = this.hideAction;
9448 el.addClass("x-dlg");
9450 Roo.apply(this, config);
9452 this.proxy = el.createProxy("x-dlg-proxy");
9453 this.proxy.hide = this.hideAction;
9454 this.proxy.setOpacity(.5);
9458 el.setWidth(config.width);
9461 el.setHeight(config.height);
9463 this.size = el.getSize();
9464 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9465 this.xy = [config.x,config.y];
9467 this.xy = el.getCenterXY(true);
9469 /** The header element @type Roo.Element */
9470 this.header = el.child("> .x-dlg-hd");
9471 /** The body element @type Roo.Element */
9472 this.body = el.child("> .x-dlg-bd");
9473 /** The footer element @type Roo.Element */
9474 this.footer = el.child("> .x-dlg-ft");
9477 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
9480 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9483 this.header.unselectable();
9485 this.header.update(this.title);
9487 // this element allows the dialog to be focused for keyboard event
9488 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9489 this.focusEl.swallowEvent("click", true);
9491 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9493 // wrap the body and footer for special rendering
9494 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9496 this.bwrap.dom.appendChild(this.footer.dom);
9499 this.bg = this.el.createChild({
9500 tag: "div", cls:"x-dlg-bg",
9501 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
9503 this.centerBg = this.bg.child("div.x-dlg-bg-center");
9506 if(this.autoScroll !== false && !this.autoTabs){
9507 this.body.setStyle("overflow", "auto");
9510 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9512 if(this.closable !== false){
9513 this.el.addClass("x-dlg-closable");
9514 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9515 this.close.on("click", this.closeClick, this);
9516 this.close.addClassOnOver("x-dlg-close-over");
9518 if(this.collapsible !== false){
9519 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9520 this.collapseBtn.on("click", this.collapseClick, this);
9521 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9522 this.header.on("dblclick", this.collapseClick, this);
9524 if(this.resizable !== false){
9525 this.el.addClass("x-dlg-resizable");
9526 this.resizer = new Roo.Resizable(el, {
9527 minWidth: this.minWidth || 80,
9528 minHeight:this.minHeight || 80,
9529 handles: this.resizeHandles || "all",
9532 this.resizer.on("beforeresize", this.beforeResize, this);
9533 this.resizer.on("resize", this.onResize, this);
9535 if(this.draggable !== false){
9536 el.addClass("x-dlg-draggable");
9537 if (!this.proxyDrag) {
9538 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9541 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9543 dd.setHandleElId(this.header.id);
9544 dd.endDrag = this.endMove.createDelegate(this);
9545 dd.startDrag = this.startMove.createDelegate(this);
9546 dd.onDrag = this.onDrag.createDelegate(this);
9551 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9552 this.mask.enableDisplayMode("block");
9554 this.el.addClass("x-dlg-modal");
9557 this.shadow = new Roo.Shadow({
9558 mode : typeof this.shadow == "string" ? this.shadow : "sides",
9559 offset : this.shadowOffset
9562 this.shadowOffset = 0;
9564 if(Roo.useShims && this.shim !== false){
9565 this.shim = this.el.createShim();
9566 this.shim.hide = this.hideAction;
9575 var bts= this.buttons;
9577 Roo.each(bts, function(b) {
9586 * Fires when a key is pressed
9587 * @param {Roo.BasicDialog} this
9588 * @param {Roo.EventObject} e
9593 * Fires when this dialog is moved by the user.
9594 * @param {Roo.BasicDialog} this
9595 * @param {Number} x The new page X
9596 * @param {Number} y The new page Y
9601 * Fires when this dialog is resized by the user.
9602 * @param {Roo.BasicDialog} this
9603 * @param {Number} width The new width
9604 * @param {Number} height The new height
9609 * Fires before this dialog is hidden.
9610 * @param {Roo.BasicDialog} this
9612 "beforehide" : true,
9615 * Fires when this dialog is hidden.
9616 * @param {Roo.BasicDialog} this
9621 * Fires before this dialog is shown.
9622 * @param {Roo.BasicDialog} this
9624 "beforeshow" : true,
9627 * Fires when this dialog is shown.
9628 * @param {Roo.BasicDialog} this
9632 el.on("keydown", this.onKeyDown, this);
9633 el.on("mousedown", this.toFront, this);
9634 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9636 Roo.DialogManager.register(this);
9637 Roo.BasicDialog.superclass.constructor.call(this);
9640 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9641 shadowOffset: Roo.isIE ? 6 : 5,
9645 defaultButton: null,
9646 buttonAlign: "right",
9651 * Sets the dialog title text
9652 * @param {String} text The title text to display
9653 * @return {Roo.BasicDialog} this
9655 setTitle : function(text){
9656 this.header.update(text);
9661 closeClick : function(){
9666 collapseClick : function(){
9667 this[this.collapsed ? "expand" : "collapse"]();
9671 * Collapses the dialog to its minimized state (only the title bar is visible).
9672 * Equivalent to the user clicking the collapse dialog button.
9674 collapse : function(){
9675 if(!this.collapsed){
9676 this.collapsed = true;
9677 this.el.addClass("x-dlg-collapsed");
9678 this.restoreHeight = this.el.getHeight();
9679 this.resizeTo(this.el.getWidth(), this.header.getHeight());
9684 * Expands a collapsed dialog back to its normal state. Equivalent to the user
9685 * clicking the expand dialog button.
9687 expand : function(){
9689 this.collapsed = false;
9690 this.el.removeClass("x-dlg-collapsed");
9691 this.resizeTo(this.el.getWidth(), this.restoreHeight);
9696 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9697 * @return {Roo.TabPanel} The tabs component
9699 initTabs : function(){
9700 var tabs = this.getTabs();
9701 while(tabs.getTab(0)){
9704 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9706 tabs.addTab(Roo.id(dom), dom.title);
9714 beforeResize : function(){
9715 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9719 onResize : function(){
9721 this.syncBodyHeight();
9722 this.adjustAssets();
9724 this.fireEvent("resize", this, this.size.width, this.size.height);
9728 onKeyDown : function(e){
9729 if(this.isVisible()){
9730 this.fireEvent("keydown", this, e);
9735 * Resizes the dialog.
9736 * @param {Number} width
9737 * @param {Number} height
9738 * @return {Roo.BasicDialog} this
9740 resizeTo : function(width, height){
9741 this.el.setSize(width, height);
9742 this.size = {width: width, height: height};
9743 this.syncBodyHeight();
9744 if(this.fixedcenter){
9747 if(this.isVisible()){
9749 this.adjustAssets();
9751 this.fireEvent("resize", this, width, height);
9757 * Resizes the dialog to fit the specified content size.
9758 * @param {Number} width
9759 * @param {Number} height
9760 * @return {Roo.BasicDialog} this
9762 setContentSize : function(w, h){
9763 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9764 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9765 //if(!this.el.isBorderBox()){
9766 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9767 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9770 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9771 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9773 this.resizeTo(w, h);
9778 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
9779 * executed in response to a particular key being pressed while the dialog is active.
9780 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9781 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9782 * @param {Function} fn The function to call
9783 * @param {Object} scope (optional) The scope of the function
9784 * @return {Roo.BasicDialog} this
9786 addKeyListener : function(key, fn, scope){
9787 var keyCode, shift, ctrl, alt;
9788 if(typeof key == "object" && !(key instanceof Array)){
9789 keyCode = key["key"];
9790 shift = key["shift"];
9796 var handler = function(dlg, e){
9797 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
9799 if(keyCode instanceof Array){
9800 for(var i = 0, len = keyCode.length; i < len; i++){
9801 if(keyCode[i] == k){
9802 fn.call(scope || window, dlg, k, e);
9808 fn.call(scope || window, dlg, k, e);
9813 this.on("keydown", handler);
9818 * Returns the TabPanel component (creates it if it doesn't exist).
9819 * Note: If you wish to simply check for the existence of tabs without creating them,
9820 * check for a null 'tabs' property.
9821 * @return {Roo.TabPanel} The tabs component
9823 getTabs : function(){
9825 this.el.addClass("x-dlg-auto-tabs");
9826 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9827 this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9833 * Adds a button to the footer section of the dialog.
9834 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9835 * object or a valid Roo.DomHelper element config
9836 * @param {Function} handler The function called when the button is clicked
9837 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9838 * @return {Roo.Button} The new button
9840 addButton : function(config, handler, scope){
9841 var dh = Roo.DomHelper;
9843 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9845 if(!this.btnContainer){
9846 var tb = this.footer.createChild({
9848 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9849 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9851 this.btnContainer = tb.firstChild.firstChild.firstChild;
9856 minWidth: this.minButtonWidth,
9859 if(typeof config == "string"){
9860 bconfig.text = config;
9863 bconfig.dhconfig = config;
9865 Roo.apply(bconfig, config);
9869 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9870 bconfig.position = Math.max(0, bconfig.position);
9871 fc = this.btnContainer.childNodes[bconfig.position];
9874 var btn = new Roo.Button(
9876 this.btnContainer.insertBefore(document.createElement("td"),fc)
9877 : this.btnContainer.appendChild(document.createElement("td")),
9878 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
9881 this.syncBodyHeight();
9884 * Array of all the buttons that have been added to this dialog via addButton
9889 this.buttons.push(btn);
9894 * Sets the default button to be focused when the dialog is displayed.
9895 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9896 * @return {Roo.BasicDialog} this
9898 setDefaultButton : function(btn){
9899 this.defaultButton = btn;
9904 getHeaderFooterHeight : function(safe){
9907 height += this.header.getHeight();
9910 var fm = this.footer.getMargins();
9911 height += (this.footer.getHeight()+fm.top+fm.bottom);
9913 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9914 height += this.centerBg.getPadding("tb");
9919 syncBodyHeight : function()
9921 var bd = this.body, // the text
9922 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9924 var height = this.size.height - this.getHeaderFooterHeight(false);
9925 bd.setHeight(height-bd.getMargins("tb"));
9926 var hh = this.header.getHeight();
9927 var h = this.size.height-hh;
9930 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9931 bw.setHeight(h-cb.getPadding("tb"));
9933 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9934 bd.setWidth(bw.getWidth(true));
9936 this.tabs.syncHeight();
9938 this.tabs.el.repaint();
9944 * Restores the previous state of the dialog if Roo.state is configured.
9945 * @return {Roo.BasicDialog} this
9947 restoreState : function(){
9948 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9949 if(box && box.width){
9950 this.xy = [box.x, box.y];
9951 this.resizeTo(box.width, box.height);
9957 beforeShow : function(){
9959 if(this.fixedcenter){
9960 this.xy = this.el.getCenterXY(true);
9963 Roo.get(document.body).addClass("x-body-masked");
9964 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9971 animShow : function(){
9972 var b = Roo.get(this.animateTarget).getBox();
9973 this.proxy.setSize(b.width, b.height);
9974 this.proxy.setLocation(b.x, b.y);
9976 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9977 true, .35, this.showEl.createDelegate(this));
9982 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9983 * @return {Roo.BasicDialog} this
9985 show : function(animateTarget){
9986 if (this.fireEvent("beforeshow", this) === false){
9989 if(this.syncHeightBeforeShow){
9990 this.syncBodyHeight();
9991 }else if(this.firstShow){
9992 this.firstShow = false;
9993 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9995 this.animateTarget = animateTarget || this.animateTarget;
9996 if(!this.el.isVisible()){
9998 if(this.animateTarget && Roo.get(this.animateTarget)){
10008 showEl : function(){
10010 this.el.setXY(this.xy);
10012 this.adjustAssets(true);
10015 // IE peekaboo bug - fix found by Dave Fenwick
10019 this.fireEvent("show", this);
10023 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
10024 * dialog itself will receive focus.
10026 focus : function(){
10027 if(this.defaultButton){
10028 this.defaultButton.focus();
10030 this.focusEl.focus();
10035 constrainXY : function(){
10036 if(this.constraintoviewport !== false){
10037 if(!this.viewSize){
10038 if(this.container){
10039 var s = this.container.getSize();
10040 this.viewSize = [s.width, s.height];
10042 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10045 var s = Roo.get(this.container||document).getScroll();
10047 var x = this.xy[0], y = this.xy[1];
10048 var w = this.size.width, h = this.size.height;
10049 var vw = this.viewSize[0], vh = this.viewSize[1];
10050 // only move it if it needs it
10052 // first validate right/bottom
10053 if(x + w > vw+s.left){
10057 if(y + h > vh+s.top){
10061 // then make sure top/left isn't negative
10073 if(this.isVisible()){
10074 this.el.setLocation(x, y);
10075 this.adjustAssets();
10082 onDrag : function(){
10083 if(!this.proxyDrag){
10084 this.xy = this.el.getXY();
10085 this.adjustAssets();
10090 adjustAssets : function(doShow){
10091 var x = this.xy[0], y = this.xy[1];
10092 var w = this.size.width, h = this.size.height;
10093 if(doShow === true){
10095 this.shadow.show(this.el);
10101 if(this.shadow && this.shadow.isVisible()){
10102 this.shadow.show(this.el);
10104 if(this.shim && this.shim.isVisible()){
10105 this.shim.setBounds(x, y, w, h);
10110 adjustViewport : function(w, h){
10112 w = Roo.lib.Dom.getViewWidth();
10113 h = Roo.lib.Dom.getViewHeight();
10116 this.viewSize = [w, h];
10117 if(this.modal && this.mask.isVisible()){
10118 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10119 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10121 if(this.isVisible()){
10122 this.constrainXY();
10127 * Destroys this dialog and all its supporting elements (including any tabs, shim,
10128 * shadow, proxy, mask, etc.) Also removes all event listeners.
10129 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10131 destroy : function(removeEl){
10132 if(this.isVisible()){
10133 this.animateTarget = null;
10136 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10138 this.tabs.destroy(removeEl);
10151 for(var i = 0, len = this.buttons.length; i < len; i++){
10152 this.buttons[i].destroy();
10155 this.el.removeAllListeners();
10156 if(removeEl === true){
10157 this.el.update("");
10160 Roo.DialogManager.unregister(this);
10164 startMove : function(){
10165 if(this.proxyDrag){
10168 if(this.constraintoviewport !== false){
10169 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10174 endMove : function(){
10175 if(!this.proxyDrag){
10176 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10178 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10181 this.refreshSize();
10182 this.adjustAssets();
10184 this.fireEvent("move", this, this.xy[0], this.xy[1]);
10188 * Brings this dialog to the front of any other visible dialogs
10189 * @return {Roo.BasicDialog} this
10191 toFront : function(){
10192 Roo.DialogManager.bringToFront(this);
10197 * Sends this dialog to the back (under) of any other visible dialogs
10198 * @return {Roo.BasicDialog} this
10200 toBack : function(){
10201 Roo.DialogManager.sendToBack(this);
10206 * Centers this dialog in the viewport
10207 * @return {Roo.BasicDialog} this
10209 center : function(){
10210 var xy = this.el.getCenterXY(true);
10211 this.moveTo(xy[0], xy[1]);
10216 * Moves the dialog's top-left corner to the specified point
10217 * @param {Number} x
10218 * @param {Number} y
10219 * @return {Roo.BasicDialog} this
10221 moveTo : function(x, y){
10223 if(this.isVisible()){
10224 this.el.setXY(this.xy);
10225 this.adjustAssets();
10231 * Aligns the dialog to the specified element
10232 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10233 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10234 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10235 * @return {Roo.BasicDialog} this
10237 alignTo : function(element, position, offsets){
10238 this.xy = this.el.getAlignToXY(element, position, offsets);
10239 if(this.isVisible()){
10240 this.el.setXY(this.xy);
10241 this.adjustAssets();
10247 * Anchors an element to another element and realigns it when the window is resized.
10248 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10249 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10250 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10251 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10252 * is a number, it is used as the buffer delay (defaults to 50ms).
10253 * @return {Roo.BasicDialog} this
10255 anchorTo : function(el, alignment, offsets, monitorScroll){
10256 var action = function(){
10257 this.alignTo(el, alignment, offsets);
10259 Roo.EventManager.onWindowResize(action, this);
10260 var tm = typeof monitorScroll;
10261 if(tm != 'undefined'){
10262 Roo.EventManager.on(window, 'scroll', action, this,
10263 {buffer: tm == 'number' ? monitorScroll : 50});
10270 * Returns true if the dialog is visible
10271 * @return {Boolean}
10273 isVisible : function(){
10274 return this.el.isVisible();
10278 animHide : function(callback){
10279 var b = Roo.get(this.animateTarget).getBox();
10281 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10283 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10284 this.hideEl.createDelegate(this, [callback]));
10288 * Hides the dialog.
10289 * @param {Function} callback (optional) Function to call when the dialog is hidden
10290 * @return {Roo.BasicDialog} this
10292 hide : function(callback){
10293 if (this.fireEvent("beforehide", this) === false){
10297 this.shadow.hide();
10302 // sometimes animateTarget seems to get set.. causing problems...
10303 // this just double checks..
10304 if(this.animateTarget && Roo.get(this.animateTarget)) {
10305 this.animHide(callback);
10308 this.hideEl(callback);
10314 hideEl : function(callback){
10318 Roo.get(document.body).removeClass("x-body-masked");
10320 this.fireEvent("hide", this);
10321 if(typeof callback == "function"){
10327 hideAction : function(){
10328 this.setLeft("-10000px");
10329 this.setTop("-10000px");
10330 this.setStyle("visibility", "hidden");
10334 refreshSize : function(){
10335 this.size = this.el.getSize();
10336 this.xy = this.el.getXY();
10337 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10341 // z-index is managed by the DialogManager and may be overwritten at any time
10342 setZIndex : function(index){
10344 this.mask.setStyle("z-index", index);
10347 this.shim.setStyle("z-index", ++index);
10350 this.shadow.setZIndex(++index);
10352 this.el.setStyle("z-index", ++index);
10354 this.proxy.setStyle("z-index", ++index);
10357 this.resizer.proxy.setStyle("z-index", ++index);
10360 this.lastZIndex = index;
10364 * Returns the element for this dialog
10365 * @return {Roo.Element} The underlying dialog Element
10367 getEl : function(){
10373 * @class Roo.DialogManager
10374 * Provides global access to BasicDialogs that have been created and
10375 * support for z-indexing (layering) multiple open dialogs.
10377 Roo.DialogManager = function(){
10379 var accessList = [];
10383 var sortDialogs = function(d1, d2){
10384 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10388 var orderDialogs = function(){
10389 accessList.sort(sortDialogs);
10390 var seed = Roo.DialogManager.zseed;
10391 for(var i = 0, len = accessList.length; i < len; i++){
10392 var dlg = accessList[i];
10394 dlg.setZIndex(seed + (i*10));
10401 * The starting z-index for BasicDialogs (defaults to 9000)
10402 * @type Number The z-index value
10407 register : function(dlg){
10408 list[dlg.id] = dlg;
10409 accessList.push(dlg);
10413 unregister : function(dlg){
10414 delete list[dlg.id];
10417 if(!accessList.indexOf){
10418 for( i = 0, len = accessList.length; i < len; i++){
10419 if(accessList[i] == dlg){
10420 accessList.splice(i, 1);
10425 i = accessList.indexOf(dlg);
10427 accessList.splice(i, 1);
10433 * Gets a registered dialog by id
10434 * @param {String/Object} id The id of the dialog or a dialog
10435 * @return {Roo.BasicDialog} this
10437 get : function(id){
10438 return typeof id == "object" ? id : list[id];
10442 * Brings the specified dialog to the front
10443 * @param {String/Object} dlg The id of the dialog or a dialog
10444 * @return {Roo.BasicDialog} this
10446 bringToFront : function(dlg){
10447 dlg = this.get(dlg);
10450 dlg._lastAccess = new Date().getTime();
10457 * Sends the specified dialog to the back
10458 * @param {String/Object} dlg The id of the dialog or a dialog
10459 * @return {Roo.BasicDialog} this
10461 sendToBack : function(dlg){
10462 dlg = this.get(dlg);
10463 dlg._lastAccess = -(new Date().getTime());
10469 * Hides all dialogs
10471 hideAll : function(){
10472 for(var id in list){
10473 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10482 * @class Roo.LayoutDialog
10483 * @extends Roo.BasicDialog
10484 * Dialog which provides adjustments for working with a layout in a Dialog.
10485 * Add your necessary layout config options to the dialog's config.<br>
10486 * Example usage (including a nested layout):
10489 dialog = new Roo.LayoutDialog("download-dlg", {
10498 // layout config merges with the dialog config
10500 tabPosition: "top",
10501 alwaysShowTabs: true
10504 dialog.addKeyListener(27, dialog.hide, dialog);
10505 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10506 dialog.addButton("Build It!", this.getDownload, this);
10508 // we can even add nested layouts
10509 var innerLayout = new Roo.BorderLayout("dl-inner", {
10519 innerLayout.beginUpdate();
10520 innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10521 innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10522 innerLayout.endUpdate(true);
10524 var layout = dialog.getLayout();
10525 layout.beginUpdate();
10526 layout.add("center", new Roo.ContentPanel("standard-panel",
10527 {title: "Download the Source", fitToFrame:true}));
10528 layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10529 {title: "Build your own roo.js"}));
10530 layout.getRegion("center").showPanel(sp);
10531 layout.endUpdate();
10535 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10536 * @param {Object} config configuration options
10538 Roo.LayoutDialog = function(el, cfg){
10541 if (typeof(cfg) == 'undefined') {
10542 config = Roo.apply({}, el);
10543 // not sure why we use documentElement here.. - it should always be body.
10544 // IE7 borks horribly if we use documentElement.
10545 // webkit also does not like documentElement - it creates a body element...
10546 el = Roo.get( document.body || document.documentElement ).createChild();
10547 //config.autoCreate = true;
10551 config.autoTabs = false;
10552 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10553 this.body.setStyle({overflow:"hidden", position:"relative"});
10554 this.layout = new Roo.BorderLayout(this.body.dom, config);
10555 this.layout.monitorWindowResize = false;
10556 this.el.addClass("x-dlg-auto-layout");
10557 // fix case when center region overwrites center function
10558 this.center = Roo.BasicDialog.prototype.center;
10559 this.on("show", this.layout.layout, this.layout, true);
10560 if (config.items) {
10561 var xitems = config.items;
10562 delete config.items;
10563 Roo.each(xitems, this.addxtype, this);
10568 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10570 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10573 endUpdate : function(){
10574 this.layout.endUpdate();
10578 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10581 beginUpdate : function(){
10582 this.layout.beginUpdate();
10586 * Get the BorderLayout for this dialog
10587 * @return {Roo.BorderLayout}
10589 getLayout : function(){
10590 return this.layout;
10593 showEl : function(){
10594 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10596 this.layout.layout();
10601 // Use the syncHeightBeforeShow config option to control this automatically
10602 syncBodyHeight : function(){
10603 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10604 if(this.layout){this.layout.layout();}
10608 * Add an xtype element (actually adds to the layout.)
10609 * @return {Object} xdata xtype object data.
10612 addxtype : function(c) {
10613 return this.layout.addxtype(c);
10617 * Ext JS Library 1.1.1
10618 * Copyright(c) 2006-2007, Ext JS, LLC.
10620 * Originally Released Under LGPL - original licence link has changed is not relivant.
10623 * <script type="text/javascript">
10627 * @class Roo.MessageBox
10628 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
10632 Roo.Msg.alert('Status', 'Changes saved successfully.');
10634 // Prompt for user data:
10635 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10637 // process text value...
10641 // Show a dialog using config options:
10643 title:'Save Changes?',
10644 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10645 buttons: Roo.Msg.YESNOCANCEL,
10652 Roo.MessageBox = function(){
10653 var dlg, opt, mask, waitTimer;
10654 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10655 var buttons, activeTextEl, bwidth;
10658 var handleButton = function(button){
10660 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10664 var handleHide = function(){
10665 if(opt && opt.cls){
10666 dlg.el.removeClass(opt.cls);
10669 Roo.TaskMgr.stop(waitTimer);
10675 var updateButtons = function(b){
10678 buttons["ok"].hide();
10679 buttons["cancel"].hide();
10680 buttons["yes"].hide();
10681 buttons["no"].hide();
10682 dlg.footer.dom.style.display = 'none';
10685 dlg.footer.dom.style.display = '';
10686 for(var k in buttons){
10687 if(typeof buttons[k] != "function"){
10690 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10691 width += buttons[k].el.getWidth()+15;
10701 var handleEsc = function(d, k, e){
10702 if(opt && opt.closable !== false){
10712 * Returns a reference to the underlying {@link Roo.BasicDialog} element
10713 * @return {Roo.BasicDialog} The BasicDialog element
10715 getDialog : function(){
10717 dlg = new Roo.BasicDialog("x-msg-box", {
10722 constraintoviewport:false,
10724 collapsible : false,
10727 width:400, height:100,
10728 buttonAlign:"center",
10729 closeClick : function(){
10730 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10731 handleButton("no");
10733 handleButton("cancel");
10737 dlg.on("hide", handleHide);
10739 dlg.addKeyListener(27, handleEsc);
10741 var bt = this.buttonText;
10742 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10743 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10744 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10745 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10746 bodyEl = dlg.body.createChild({
10748 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
10750 msgEl = bodyEl.dom.firstChild;
10751 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10752 textboxEl.enableDisplayMode();
10753 textboxEl.addKeyListener([10,13], function(){
10754 if(dlg.isVisible() && opt && opt.buttons){
10755 if(opt.buttons.ok){
10756 handleButton("ok");
10757 }else if(opt.buttons.yes){
10758 handleButton("yes");
10762 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10763 textareaEl.enableDisplayMode();
10764 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10765 progressEl.enableDisplayMode();
10766 var pf = progressEl.dom.firstChild;
10768 pp = Roo.get(pf.firstChild);
10769 pp.setHeight(pf.offsetHeight);
10777 * Updates the message box body text
10778 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10779 * the XHTML-compliant non-breaking space character '&#160;')
10780 * @return {Roo.MessageBox} This message box
10782 updateText : function(text){
10783 if(!dlg.isVisible() && !opt.width){
10784 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10786 msgEl.innerHTML = text || ' ';
10788 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10789 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10791 Math.min(opt.width || cw , this.maxWidth),
10792 Math.max(opt.minWidth || this.minWidth, bwidth)
10795 activeTextEl.setWidth(w);
10797 if(dlg.isVisible()){
10798 dlg.fixedcenter = false;
10800 // to big, make it scroll. = But as usual stupid IE does not support
10803 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10804 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10805 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10807 bodyEl.dom.style.height = '';
10808 bodyEl.dom.style.overflowY = '';
10811 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10813 bodyEl.dom.style.overflowX = '';
10816 dlg.setContentSize(w, bodyEl.getHeight());
10817 if(dlg.isVisible()){
10818 dlg.fixedcenter = true;
10824 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
10825 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10826 * @param {Number} value Any number between 0 and 1 (e.g., .5)
10827 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10828 * @return {Roo.MessageBox} This message box
10830 updateProgress : function(value, text){
10832 this.updateText(text);
10834 if (pp) { // weird bug on my firefox - for some reason this is not defined
10835 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10841 * Returns true if the message box is currently displayed
10842 * @return {Boolean} True if the message box is visible, else false
10844 isVisible : function(){
10845 return dlg && dlg.isVisible();
10849 * Hides the message box if it is displayed
10852 if(this.isVisible()){
10858 * Displays a new message box, or reinitializes an existing message box, based on the config options
10859 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10860 * The following config object properties are supported:
10862 Property Type Description
10863 ---------- --------------- ------------------------------------------------------------------------------------
10864 animEl String/Element An id or Element from which the message box should animate as it opens and
10865 closes (defaults to undefined)
10866 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10867 cancel:'Bar'}), or false to not show any buttons (defaults to false)
10868 closable Boolean False to hide the top-right close button (defaults to true). Note that
10869 progress and wait dialogs will ignore this property and always hide the
10870 close button as they can only be closed programmatically.
10871 cls String A custom CSS class to apply to the message box element
10872 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
10873 displayed (defaults to 75)
10874 fn Function A callback function to execute after closing the dialog. The arguments to the
10875 function will be btn (the name of the button that was clicked, if applicable,
10876 e.g. "ok"), and text (the value of the active text field, if applicable).
10877 Progress and wait dialogs will ignore this option since they do not respond to
10878 user actions and can only be closed programmatically, so any required function
10879 should be called by the same code after it closes the dialog.
10880 icon String A CSS class that provides a background image to be used as an icon for
10881 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10882 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
10883 minWidth Number The minimum width in pixels of the message box (defaults to 100)
10884 modal Boolean False to allow user interaction with the page while the message box is
10885 displayed (defaults to true)
10886 msg String A string that will replace the existing message box body text (defaults
10887 to the XHTML-compliant non-breaking space character ' ')
10888 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
10889 progress Boolean True to display a progress bar (defaults to false)
10890 progressText String The text to display inside the progress bar if progress = true (defaults to '')
10891 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
10892 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
10893 title String The title text
10894 value String The string value to set into the active textbox element if displayed
10895 wait Boolean True to display a progress bar (defaults to false)
10896 width Number The width of the dialog in pixels
10903 msg: 'Please enter your address:',
10905 buttons: Roo.MessageBox.OKCANCEL,
10908 animEl: 'addAddressBtn'
10911 * @param {Object} config Configuration options
10912 * @return {Roo.MessageBox} This message box
10914 show : function(options)
10917 // this causes nightmares if you show one dialog after another
10918 // especially on callbacks..
10920 if(this.isVisible()){
10923 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10924 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
10925 Roo.log("New Dialog Message:" + options.msg )
10926 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10927 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10930 var d = this.getDialog();
10932 d.setTitle(opt.title || " ");
10933 d.close.setDisplayed(opt.closable !== false);
10934 activeTextEl = textboxEl;
10935 opt.prompt = opt.prompt || (opt.multiline ? true : false);
10940 textareaEl.setHeight(typeof opt.multiline == "number" ?
10941 opt.multiline : this.defaultTextHeight);
10942 activeTextEl = textareaEl;
10951 progressEl.setDisplayed(opt.progress === true);
10952 this.updateProgress(0);
10953 activeTextEl.dom.value = opt.value || "";
10955 dlg.setDefaultButton(activeTextEl);
10957 var bs = opt.buttons;
10960 db = buttons["ok"];
10961 }else if(bs && bs.yes){
10962 db = buttons["yes"];
10964 dlg.setDefaultButton(db);
10966 bwidth = updateButtons(opt.buttons);
10967 this.updateText(opt.msg);
10969 d.el.addClass(opt.cls);
10971 d.proxyDrag = opt.proxyDrag === true;
10972 d.modal = opt.modal !== false;
10973 d.mask = opt.modal !== false ? mask : false;
10974 if(!d.isVisible()){
10975 // force it to the end of the z-index stack so it gets a cursor in FF
10976 document.body.appendChild(dlg.el.dom);
10977 d.animateTarget = null;
10978 d.show(options.animEl);
10984 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
10985 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10986 * and closing the message box when the process is complete.
10987 * @param {String} title The title bar text
10988 * @param {String} msg The message box body text
10989 * @return {Roo.MessageBox} This message box
10991 progress : function(title, msg){
10998 minWidth: this.minProgressWidth,
11005 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11006 * If a callback function is passed it will be called after the user clicks the button, and the
11007 * id of the button that was clicked will be passed as the only parameter to the callback
11008 * (could also be the top-right close button).
11009 * @param {String} title The title bar text
11010 * @param {String} msg The message box body text
11011 * @param {Function} fn (optional) The callback function invoked after the message box is closed
11012 * @param {Object} scope (optional) The scope of the callback function
11013 * @return {Roo.MessageBox} This message box
11015 alert : function(title, msg, fn, scope){
11028 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
11029 * interaction while waiting for a long-running process to complete that does not have defined intervals.
11030 * You are responsible for closing the message box when the process is complete.
11031 * @param {String} msg The message box body text
11032 * @param {String} title (optional) The title bar text
11033 * @return {Roo.MessageBox} This message box
11035 wait : function(msg, title){
11046 waitTimer = Roo.TaskMgr.start({
11048 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11056 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11057 * If a callback function is passed it will be called after the user clicks either button, and the id of the
11058 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11059 * @param {String} title The title bar text
11060 * @param {String} msg The message box body text
11061 * @param {Function} fn (optional) The callback function invoked after the message box is closed
11062 * @param {Object} scope (optional) The scope of the callback function
11063 * @return {Roo.MessageBox} This message box
11065 confirm : function(title, msg, fn, scope){
11069 buttons: this.YESNO,
11078 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11079 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
11080 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11081 * (could also be the top-right close button) and the text that was entered will be passed as the two
11082 * parameters to the callback.
11083 * @param {String} title The title bar text
11084 * @param {String} msg The message box body text
11085 * @param {Function} fn (optional) The callback function invoked after the message box is closed
11086 * @param {Object} scope (optional) The scope of the callback function
11087 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11088 * property, or the height in pixels to create the textbox (defaults to false / single-line)
11089 * @return {Roo.MessageBox} This message box
11091 prompt : function(title, msg, fn, scope, multiline){
11095 buttons: this.OKCANCEL,
11100 multiline: multiline,
11107 * Button config that displays a single OK button
11112 * Button config that displays Yes and No buttons
11115 YESNO : {yes:true, no:true},
11117 * Button config that displays OK and Cancel buttons
11120 OKCANCEL : {ok:true, cancel:true},
11122 * Button config that displays Yes, No and Cancel buttons
11125 YESNOCANCEL : {yes:true, no:true, cancel:true},
11128 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11131 defaultTextHeight : 75,
11133 * The maximum width in pixels of the message box (defaults to 600)
11138 * The minimum width in pixels of the message box (defaults to 100)
11143 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
11144 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11147 minProgressWidth : 250,
11149 * An object containing the default button text strings that can be overriden for localized language support.
11150 * Supported properties are: ok, cancel, yes and no.
11151 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11164 * Shorthand for {@link Roo.MessageBox}
11166 Roo.Msg = Roo.MessageBox;/*
11168 * Ext JS Library 1.1.1
11169 * Copyright(c) 2006-2007, Ext JS, LLC.
11171 * Originally Released Under LGPL - original licence link has changed is not relivant.
11174 * <script type="text/javascript">
11177 * @class Roo.QuickTips
11178 * Provides attractive and customizable tooltips for any element.
11181 Roo.QuickTips = function(){
11182 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11183 var ce, bd, xy, dd;
11184 var visible = false, disabled = true, inited = false;
11185 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11187 var onOver = function(e){
11191 var t = e.getTarget();
11192 if(!t || t.nodeType !== 1 || t == document || t == document.body){
11195 if(ce && t == ce.el){
11196 clearTimeout(hideProc);
11199 if(t && tagEls[t.id]){
11200 tagEls[t.id].el = t;
11201 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11204 var ttp, et = Roo.fly(t);
11205 var ns = cfg.namespace;
11206 if(tm.interceptTitles && t.title){
11209 t.removeAttribute("title");
11210 e.preventDefault();
11212 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11215 showProc = show.defer(tm.showDelay, tm, [{
11218 width: et.getAttributeNS(ns, cfg.width),
11219 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11220 title: et.getAttributeNS(ns, cfg.title),
11221 cls: et.getAttributeNS(ns, cfg.cls)
11226 var onOut = function(e){
11227 clearTimeout(showProc);
11228 var t = e.getTarget();
11229 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11230 hideProc = setTimeout(hide, tm.hideDelay);
11234 var onMove = function(e){
11240 if(tm.trackMouse && ce){
11245 var onDown = function(e){
11246 clearTimeout(showProc);
11247 clearTimeout(hideProc);
11249 if(tm.hideOnClick){
11252 tm.enable.defer(100, tm);
11257 var getPad = function(){
11258 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11261 var show = function(o){
11265 clearTimeout(dismissProc);
11267 if(removeCls){ // in case manually hidden
11268 el.removeClass(removeCls);
11272 el.addClass(ce.cls);
11273 removeCls = ce.cls;
11276 tipTitle.update(ce.title);
11279 tipTitle.update('');
11282 el.dom.style.width = tm.maxWidth+'px';
11283 //tipBody.dom.style.width = '';
11284 tipBodyText.update(o.text);
11285 var p = getPad(), w = ce.width;
11287 var td = tipBodyText.dom;
11288 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11289 if(aw > tm.maxWidth){
11291 }else if(aw < tm.minWidth){
11297 //tipBody.setWidth(w);
11298 el.setWidth(parseInt(w, 10) + p);
11299 if(ce.autoHide === false){
11300 close.setDisplayed(true);
11305 close.setDisplayed(false);
11311 el.avoidY = xy[1]-18;
11316 el.setStyle("visibility", "visible");
11317 el.fadeIn({callback: afterShow});
11323 var afterShow = function(){
11327 if(tm.autoDismiss && ce.autoHide !== false){
11328 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11333 var hide = function(noanim){
11334 clearTimeout(dismissProc);
11335 clearTimeout(hideProc);
11337 if(el.isVisible()){
11339 if(noanim !== true && tm.animate){
11340 el.fadeOut({callback: afterHide});
11347 var afterHide = function(){
11350 el.removeClass(removeCls);
11357 * @cfg {Number} minWidth
11358 * The minimum width of the quick tip (defaults to 40)
11362 * @cfg {Number} maxWidth
11363 * The maximum width of the quick tip (defaults to 300)
11367 * @cfg {Boolean} interceptTitles
11368 * True to automatically use the element's DOM title value if available (defaults to false)
11370 interceptTitles : false,
11372 * @cfg {Boolean} trackMouse
11373 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11375 trackMouse : false,
11377 * @cfg {Boolean} hideOnClick
11378 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11380 hideOnClick : true,
11382 * @cfg {Number} showDelay
11383 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11387 * @cfg {Number} hideDelay
11388 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11392 * @cfg {Boolean} autoHide
11393 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11394 * Used in conjunction with hideDelay.
11399 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11400 * (defaults to true). Used in conjunction with autoDismissDelay.
11402 autoDismiss : true,
11405 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11407 autoDismissDelay : 5000,
11409 * @cfg {Boolean} animate
11410 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11415 * @cfg {String} title
11416 * Title text to display (defaults to ''). This can be any valid HTML markup.
11420 * @cfg {String} text
11421 * Body text to display (defaults to ''). This can be any valid HTML markup.
11425 * @cfg {String} cls
11426 * A CSS class to apply to the base quick tip element (defaults to '').
11430 * @cfg {Number} width
11431 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
11432 * minWidth or maxWidth.
11437 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
11438 * or display QuickTips in a page.
11441 tm = Roo.QuickTips;
11442 cfg = tm.tagConfig;
11444 if(!Roo.isReady){ // allow calling of init() before onReady
11445 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11448 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11449 el.fxDefaults = {stopFx: true};
11450 // maximum custom styling
11451 //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
11452 el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');
11453 tipTitle = el.child('h3');
11454 tipTitle.enableDisplayMode("block");
11455 tipBody = el.child('div.x-tip-bd');
11456 tipBodyText = el.child('div.x-tip-bd-inner');
11457 //bdLeft = el.child('div.x-tip-bd-left');
11458 //bdRight = el.child('div.x-tip-bd-right');
11459 close = el.child('div.x-tip-close');
11460 close.enableDisplayMode("block");
11461 close.on("click", hide);
11462 var d = Roo.get(document);
11463 d.on("mousedown", onDown);
11464 d.on("mouseover", onOver);
11465 d.on("mouseout", onOut);
11466 d.on("mousemove", onMove);
11467 esc = d.addKeyListener(27, hide);
11470 dd = el.initDD("default", null, {
11471 onDrag : function(){
11475 dd.setHandleElId(tipTitle.id);
11484 * Configures a new quick tip instance and assigns it to a target element. The following config options
11487 Property Type Description
11488 ---------- --------------------- ------------------------------------------------------------------------
11489 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
11491 * @param {Object} config The config object
11493 register : function(config){
11494 var cs = config instanceof Array ? config : arguments;
11495 for(var i = 0, len = cs.length; i < len; i++) {
11497 var target = c.target;
11499 if(target instanceof Array){
11500 for(var j = 0, jlen = target.length; j < jlen; j++){
11501 tagEls[target[j]] = c;
11504 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11511 * Removes this quick tip from its element and destroys it.
11512 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11514 unregister : function(el){
11515 delete tagEls[Roo.id(el)];
11519 * Enable this quick tip.
11521 enable : function(){
11522 if(inited && disabled){
11524 if(locks.length < 1){
11531 * Disable this quick tip.
11533 disable : function(){
11535 clearTimeout(showProc);
11536 clearTimeout(hideProc);
11537 clearTimeout(dismissProc);
11545 * Returns true if the quick tip is enabled, else false.
11547 isEnabled : function(){
11553 namespace : "roo", // was ext?? this may break..
11554 alt_namespace : "ext",
11555 attribute : "qtip",
11565 // backwards compat
11566 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11568 * Ext JS Library 1.1.1
11569 * Copyright(c) 2006-2007, Ext JS, LLC.
11571 * Originally Released Under LGPL - original licence link has changed is not relivant.
11574 * <script type="text/javascript">
11579 * @class Roo.tree.TreePanel
11580 * @extends Roo.data.Tree
11582 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11583 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11584 * @cfg {Boolean} enableDD true to enable drag and drop
11585 * @cfg {Boolean} enableDrag true to enable just drag
11586 * @cfg {Boolean} enableDrop true to enable just drop
11587 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11588 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11589 * @cfg {String} ddGroup The DD group this TreePanel belongs to
11590 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11591 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11592 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11593 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11594 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11595 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11596 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11597 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11598 * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11599 * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11600 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11601 * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
11602 * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
11605 * @param {String/HTMLElement/Element} el The container element
11606 * @param {Object} config
11608 Roo.tree.TreePanel = function(el, config){
11610 var loader = false;
11612 root = config.root;
11613 delete config.root;
11615 if (config.loader) {
11616 loader = config.loader;
11617 delete config.loader;
11620 Roo.apply(this, config);
11621 Roo.tree.TreePanel.superclass.constructor.call(this);
11622 this.el = Roo.get(el);
11623 this.el.addClass('x-tree');
11624 //console.log(root);
11626 this.setRootNode( Roo.factory(root, Roo.tree));
11629 this.loader = Roo.factory(loader, Roo.tree);
11632 * Read-only. The id of the container element becomes this TreePanel's id.
11634 this.id = this.el.id;
11637 * @event beforeload
11638 * Fires before a node is loaded, return false to cancel
11639 * @param {Node} node The node being loaded
11641 "beforeload" : true,
11644 * Fires when a node is loaded
11645 * @param {Node} node The node that was loaded
11649 * @event textchange
11650 * Fires when the text for a node is changed
11651 * @param {Node} node The node
11652 * @param {String} text The new text
11653 * @param {String} oldText The old text
11655 "textchange" : true,
11657 * @event beforeexpand
11658 * Fires before a node is expanded, return false to cancel.
11659 * @param {Node} node The node
11660 * @param {Boolean} deep
11661 * @param {Boolean} anim
11663 "beforeexpand" : true,
11665 * @event beforecollapse
11666 * Fires before a node is collapsed, return false to cancel.
11667 * @param {Node} node The node
11668 * @param {Boolean} deep
11669 * @param {Boolean} anim
11671 "beforecollapse" : true,
11674 * Fires when a node is expanded
11675 * @param {Node} node The node
11679 * @event disabledchange
11680 * Fires when the disabled status of a node changes
11681 * @param {Node} node The node
11682 * @param {Boolean} disabled
11684 "disabledchange" : true,
11687 * Fires when a node is collapsed
11688 * @param {Node} node The node
11692 * @event beforeclick
11693 * Fires before click processing on a node. Return false to cancel the default action.
11694 * @param {Node} node The node
11695 * @param {Roo.EventObject} e The event object
11697 "beforeclick":true,
11699 * @event checkchange
11700 * Fires when a node with a checkbox's checked property changes
11701 * @param {Node} this This node
11702 * @param {Boolean} checked
11704 "checkchange":true,
11707 * Fires when a node is clicked
11708 * @param {Node} node The node
11709 * @param {Roo.EventObject} e The event object
11714 * Fires when a node is double clicked
11715 * @param {Node} node The node
11716 * @param {Roo.EventObject} e The event object
11720 * @event contextmenu
11721 * Fires when a node is right clicked
11722 * @param {Node} node The node
11723 * @param {Roo.EventObject} e The event object
11725 "contextmenu":true,
11727 * @event beforechildrenrendered
11728 * Fires right before the child nodes for a node are rendered
11729 * @param {Node} node The node
11731 "beforechildrenrendered":true,
11734 * Fires when a node starts being dragged
11735 * @param {Roo.tree.TreePanel} this
11736 * @param {Roo.tree.TreeNode} node
11737 * @param {event} e The raw browser event
11739 "startdrag" : true,
11742 * Fires when a drag operation is complete
11743 * @param {Roo.tree.TreePanel} this
11744 * @param {Roo.tree.TreeNode} node
11745 * @param {event} e The raw browser event
11750 * Fires when a dragged node is dropped on a valid DD target
11751 * @param {Roo.tree.TreePanel} this
11752 * @param {Roo.tree.TreeNode} node
11753 * @param {DD} dd The dd it was dropped on
11754 * @param {event} e The raw browser event
11758 * @event beforenodedrop
11759 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11760 * passed to handlers has the following properties:<br />
11761 * <ul style="padding:5px;padding-left:16px;">
11762 * <li>tree - The TreePanel</li>
11763 * <li>target - The node being targeted for the drop</li>
11764 * <li>data - The drag data from the drag source</li>
11765 * <li>point - The point of the drop - append, above or below</li>
11766 * <li>source - The drag source</li>
11767 * <li>rawEvent - Raw mouse event</li>
11768 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11769 * to be inserted by setting them on this object.</li>
11770 * <li>cancel - Set this to true to cancel the drop.</li>
11772 * @param {Object} dropEvent
11774 "beforenodedrop" : true,
11777 * Fires after a DD object is dropped on a node in this tree. The dropEvent
11778 * passed to handlers has the following properties:<br />
11779 * <ul style="padding:5px;padding-left:16px;">
11780 * <li>tree - The TreePanel</li>
11781 * <li>target - The node being targeted for the drop</li>
11782 * <li>data - The drag data from the drag source</li>
11783 * <li>point - The point of the drop - append, above or below</li>
11784 * <li>source - The drag source</li>
11785 * <li>rawEvent - Raw mouse event</li>
11786 * <li>dropNode - Dropped node(s).</li>
11788 * @param {Object} dropEvent
11792 * @event nodedragover
11793 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11794 * passed to handlers has the following properties:<br />
11795 * <ul style="padding:5px;padding-left:16px;">
11796 * <li>tree - The TreePanel</li>
11797 * <li>target - The node being targeted for the drop</li>
11798 * <li>data - The drag data from the drag source</li>
11799 * <li>point - The point of the drop - append, above or below</li>
11800 * <li>source - The drag source</li>
11801 * <li>rawEvent - Raw mouse event</li>
11802 * <li>dropNode - Drop node(s) provided by the source.</li>
11803 * <li>cancel - Set this to true to signal drop not allowed.</li>
11805 * @param {Object} dragOverEvent
11807 "nodedragover" : true
11810 if(this.singleExpand){
11811 this.on("beforeexpand", this.restrictExpand, this);
11814 this.editor.tree = this;
11815 this.editor = Roo.factory(this.editor, Roo.tree);
11818 if (this.selModel) {
11819 this.selModel = Roo.factory(this.selModel, Roo.tree);
11823 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11824 rootVisible : true,
11825 animate: Roo.enableFx,
11828 hlDrop : Roo.enableFx,
11832 rendererTip: false,
11834 restrictExpand : function(node){
11835 var p = node.parentNode;
11837 if(p.expandedChild && p.expandedChild.parentNode == p){
11838 p.expandedChild.collapse();
11840 p.expandedChild = node;
11844 // private override
11845 setRootNode : function(node){
11846 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11847 if(!this.rootVisible){
11848 node.ui = new Roo.tree.RootTreeNodeUI(node);
11854 * Returns the container element for this TreePanel
11856 getEl : function(){
11861 * Returns the default TreeLoader for this TreePanel
11863 getLoader : function(){
11864 return this.loader;
11870 expandAll : function(){
11871 this.root.expand(true);
11875 * Collapse all nodes
11877 collapseAll : function(){
11878 this.root.collapse(true);
11882 * Returns the selection model used by this TreePanel
11884 getSelectionModel : function(){
11885 if(!this.selModel){
11886 this.selModel = new Roo.tree.DefaultSelectionModel();
11888 return this.selModel;
11892 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11893 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11894 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11897 getChecked : function(a, startNode){
11898 startNode = startNode || this.root;
11900 var f = function(){
11901 if(this.attributes.checked){
11902 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11905 startNode.cascade(f);
11910 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11911 * @param {String} path
11912 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11913 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11914 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11916 expandPath : function(path, attr, callback){
11917 attr = attr || "id";
11918 var keys = path.split(this.pathSeparator);
11919 var curNode = this.root;
11920 if(curNode.attributes[attr] != keys[1]){ // invalid root
11922 callback(false, null);
11927 var f = function(){
11928 if(++index == keys.length){
11930 callback(true, curNode);
11934 var c = curNode.findChild(attr, keys[index]);
11937 callback(false, curNode);
11942 c.expand(false, false, f);
11944 curNode.expand(false, false, f);
11948 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11949 * @param {String} path
11950 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11951 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11952 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11954 selectPath : function(path, attr, callback){
11955 attr = attr || "id";
11956 var keys = path.split(this.pathSeparator);
11957 var v = keys.pop();
11958 if(keys.length > 0){
11959 var f = function(success, node){
11960 if(success && node){
11961 var n = node.findChild(attr, v);
11967 }else if(callback){
11968 callback(false, n);
11972 callback(false, n);
11976 this.expandPath(keys.join(this.pathSeparator), attr, f);
11978 this.root.select();
11980 callback(true, this.root);
11985 getTreeEl : function(){
11990 * Trigger rendering of this TreePanel
11992 render : function(){
11993 if (this.innerCt) {
11994 return this; // stop it rendering more than once!!
11997 this.innerCt = this.el.createChild({tag:"ul",
11998 cls:"x-tree-root-ct " +
11999 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12001 if(this.containerScroll){
12002 Roo.dd.ScrollManager.register(this.el);
12004 if((this.enableDD || this.enableDrop) && !this.dropZone){
12006 * The dropZone used by this tree if drop is enabled
12007 * @type Roo.tree.TreeDropZone
12009 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12010 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12013 if((this.enableDD || this.enableDrag) && !this.dragZone){
12015 * The dragZone used by this tree if drag is enabled
12016 * @type Roo.tree.TreeDragZone
12018 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12019 ddGroup: this.ddGroup || "TreeDD",
12020 scroll: this.ddScroll
12023 this.getSelectionModel().init(this);
12025 Roo.log("ROOT not set in tree");
12028 this.root.render();
12029 if(!this.rootVisible){
12030 this.root.renderChildren();
12036 * Ext JS Library 1.1.1
12037 * Copyright(c) 2006-2007, Ext JS, LLC.
12039 * Originally Released Under LGPL - original licence link has changed is not relivant.
12042 * <script type="text/javascript">
12047 * @class Roo.tree.DefaultSelectionModel
12048 * @extends Roo.util.Observable
12049 * The default single selection for a TreePanel.
12050 * @param {Object} cfg Configuration
12052 Roo.tree.DefaultSelectionModel = function(cfg){
12053 this.selNode = null;
12059 * @event selectionchange
12060 * Fires when the selected node changes
12061 * @param {DefaultSelectionModel} this
12062 * @param {TreeNode} node the new selection
12064 "selectionchange" : true,
12067 * @event beforeselect
12068 * Fires before the selected node changes, return false to cancel the change
12069 * @param {DefaultSelectionModel} this
12070 * @param {TreeNode} node the new selection
12071 * @param {TreeNode} node the old selection
12073 "beforeselect" : true
12076 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12079 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12080 init : function(tree){
12082 tree.getTreeEl().on("keydown", this.onKeyDown, this);
12083 tree.on("click", this.onNodeClick, this);
12086 onNodeClick : function(node, e){
12087 if (e.ctrlKey && this.selNode == node) {
12088 this.unselect(node);
12096 * @param {TreeNode} node The node to select
12097 * @return {TreeNode} The selected node
12099 select : function(node){
12100 var last = this.selNode;
12101 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12103 last.ui.onSelectedChange(false);
12105 this.selNode = node;
12106 node.ui.onSelectedChange(true);
12107 this.fireEvent("selectionchange", this, node, last);
12114 * @param {TreeNode} node The node to unselect
12116 unselect : function(node){
12117 if(this.selNode == node){
12118 this.clearSelections();
12123 * Clear all selections
12125 clearSelections : function(){
12126 var n = this.selNode;
12128 n.ui.onSelectedChange(false);
12129 this.selNode = null;
12130 this.fireEvent("selectionchange", this, null);
12136 * Get the selected node
12137 * @return {TreeNode} The selected node
12139 getSelectedNode : function(){
12140 return this.selNode;
12144 * Returns true if the node is selected
12145 * @param {TreeNode} node The node to check
12146 * @return {Boolean}
12148 isSelected : function(node){
12149 return this.selNode == node;
12153 * Selects the node above the selected node in the tree, intelligently walking the nodes
12154 * @return TreeNode The new selection
12156 selectPrevious : function(){
12157 var s = this.selNode || this.lastSelNode;
12161 var ps = s.previousSibling;
12163 if(!ps.isExpanded() || ps.childNodes.length < 1){
12164 return this.select(ps);
12166 var lc = ps.lastChild;
12167 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12170 return this.select(lc);
12172 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12173 return this.select(s.parentNode);
12179 * Selects the node above the selected node in the tree, intelligently walking the nodes
12180 * @return TreeNode The new selection
12182 selectNext : function(){
12183 var s = this.selNode || this.lastSelNode;
12187 if(s.firstChild && s.isExpanded()){
12188 return this.select(s.firstChild);
12189 }else if(s.nextSibling){
12190 return this.select(s.nextSibling);
12191 }else if(s.parentNode){
12193 s.parentNode.bubble(function(){
12194 if(this.nextSibling){
12195 newS = this.getOwnerTree().selModel.select(this.nextSibling);
12204 onKeyDown : function(e){
12205 var s = this.selNode || this.lastSelNode;
12206 // undesirable, but required
12211 var k = e.getKey();
12219 this.selectPrevious();
12222 e.preventDefault();
12223 if(s.hasChildNodes()){
12224 if(!s.isExpanded()){
12226 }else if(s.firstChild){
12227 this.select(s.firstChild, e);
12232 e.preventDefault();
12233 if(s.hasChildNodes() && s.isExpanded()){
12235 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12236 this.select(s.parentNode, e);
12244 * @class Roo.tree.MultiSelectionModel
12245 * @extends Roo.util.Observable
12246 * Multi selection for a TreePanel.
12247 * @param {Object} cfg Configuration
12249 Roo.tree.MultiSelectionModel = function(){
12250 this.selNodes = [];
12254 * @event selectionchange
12255 * Fires when the selected nodes change
12256 * @param {MultiSelectionModel} this
12257 * @param {Array} nodes Array of the selected nodes
12259 "selectionchange" : true
12261 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12265 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12266 init : function(tree){
12268 tree.getTreeEl().on("keydown", this.onKeyDown, this);
12269 tree.on("click", this.onNodeClick, this);
12272 onNodeClick : function(node, e){
12273 this.select(node, e, e.ctrlKey);
12278 * @param {TreeNode} node The node to select
12279 * @param {EventObject} e (optional) An event associated with the selection
12280 * @param {Boolean} keepExisting True to retain existing selections
12281 * @return {TreeNode} The selected node
12283 select : function(node, e, keepExisting){
12284 if(keepExisting !== true){
12285 this.clearSelections(true);
12287 if(this.isSelected(node)){
12288 this.lastSelNode = node;
12291 this.selNodes.push(node);
12292 this.selMap[node.id] = node;
12293 this.lastSelNode = node;
12294 node.ui.onSelectedChange(true);
12295 this.fireEvent("selectionchange", this, this.selNodes);
12301 * @param {TreeNode} node The node to unselect
12303 unselect : function(node){
12304 if(this.selMap[node.id]){
12305 node.ui.onSelectedChange(false);
12306 var sn = this.selNodes;
12309 index = sn.indexOf(node);
12311 for(var i = 0, len = sn.length; i < len; i++){
12319 this.selNodes.splice(index, 1);
12321 delete this.selMap[node.id];
12322 this.fireEvent("selectionchange", this, this.selNodes);
12327 * Clear all selections
12329 clearSelections : function(suppressEvent){
12330 var sn = this.selNodes;
12332 for(var i = 0, len = sn.length; i < len; i++){
12333 sn[i].ui.onSelectedChange(false);
12335 this.selNodes = [];
12337 if(suppressEvent !== true){
12338 this.fireEvent("selectionchange", this, this.selNodes);
12344 * Returns true if the node is selected
12345 * @param {TreeNode} node The node to check
12346 * @return {Boolean}
12348 isSelected : function(node){
12349 return this.selMap[node.id] ? true : false;
12353 * Returns an array of the selected nodes
12356 getSelectedNodes : function(){
12357 return this.selNodes;
12360 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12362 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12364 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12367 * Ext JS Library 1.1.1
12368 * Copyright(c) 2006-2007, Ext JS, LLC.
12370 * Originally Released Under LGPL - original licence link has changed is not relivant.
12373 * <script type="text/javascript">
12377 * @class Roo.tree.TreeNode
12378 * @extends Roo.data.Node
12379 * @cfg {String} text The text for this node
12380 * @cfg {Boolean} expanded true to start the node expanded
12381 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12382 * @cfg {Boolean} allowDrop false if this node cannot be drop on
12383 * @cfg {Boolean} disabled true to start the node disabled
12384 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12385 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
12386 * @cfg {String} cls A css class to be added to the node
12387 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12388 * @cfg {String} href URL of the link used for the node (defaults to #)
12389 * @cfg {String} hrefTarget target frame for the link
12390 * @cfg {String} qtip An Ext QuickTip for the node
12391 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12392 * @cfg {Boolean} singleClickExpand True for single click expand on this node
12393 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12394 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12395 * (defaults to undefined with no checkbox rendered)
12397 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12399 Roo.tree.TreeNode = function(attributes){
12400 attributes = attributes || {};
12401 if(typeof attributes == "string"){
12402 attributes = {text: attributes};
12404 this.childrenRendered = false;
12405 this.rendered = false;
12406 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12407 this.expanded = attributes.expanded === true;
12408 this.isTarget = attributes.isTarget !== false;
12409 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12410 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12413 * Read-only. The text for this node. To change it use setText().
12416 this.text = attributes.text;
12418 * True if this node is disabled.
12421 this.disabled = attributes.disabled === true;
12425 * @event textchange
12426 * Fires when the text for this node is changed
12427 * @param {Node} this This node
12428 * @param {String} text The new text
12429 * @param {String} oldText The old text
12431 "textchange" : true,
12433 * @event beforeexpand
12434 * Fires before this node is expanded, return false to cancel.
12435 * @param {Node} this This node
12436 * @param {Boolean} deep
12437 * @param {Boolean} anim
12439 "beforeexpand" : true,
12441 * @event beforecollapse
12442 * Fires before this node is collapsed, return false to cancel.
12443 * @param {Node} this This node
12444 * @param {Boolean} deep
12445 * @param {Boolean} anim
12447 "beforecollapse" : true,
12450 * Fires when this node is expanded
12451 * @param {Node} this This node
12455 * @event disabledchange
12456 * Fires when the disabled status of this node changes
12457 * @param {Node} this This node
12458 * @param {Boolean} disabled
12460 "disabledchange" : true,
12463 * Fires when this node is collapsed
12464 * @param {Node} this This node
12468 * @event beforeclick
12469 * Fires before click processing. Return false to cancel the default action.
12470 * @param {Node} this This node
12471 * @param {Roo.EventObject} e The event object
12473 "beforeclick":true,
12475 * @event checkchange
12476 * Fires when a node with a checkbox's checked property changes
12477 * @param {Node} this This node
12478 * @param {Boolean} checked
12480 "checkchange":true,
12483 * Fires when this node is clicked
12484 * @param {Node} this This node
12485 * @param {Roo.EventObject} e The event object
12490 * Fires when this node is double clicked
12491 * @param {Node} this This node
12492 * @param {Roo.EventObject} e The event object
12496 * @event contextmenu
12497 * Fires when this node is right clicked
12498 * @param {Node} this This node
12499 * @param {Roo.EventObject} e The event object
12501 "contextmenu":true,
12503 * @event beforechildrenrendered
12504 * Fires right before the child nodes for this node are rendered
12505 * @param {Node} this This node
12507 "beforechildrenrendered":true
12510 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12513 * Read-only. The UI for this node
12516 this.ui = new uiClass(this);
12518 // finally support items[]
12519 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12524 Roo.each(this.attributes.items, function(c) {
12525 this.appendChild(Roo.factory(c,Roo.Tree));
12527 delete this.attributes.items;
12532 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12533 preventHScroll: true,
12535 * Returns true if this node is expanded
12536 * @return {Boolean}
12538 isExpanded : function(){
12539 return this.expanded;
12543 * Returns the UI object for this node
12544 * @return {TreeNodeUI}
12546 getUI : function(){
12550 // private override
12551 setFirstChild : function(node){
12552 var of = this.firstChild;
12553 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12554 if(this.childrenRendered && of && node != of){
12555 of.renderIndent(true, true);
12558 this.renderIndent(true, true);
12562 // private override
12563 setLastChild : function(node){
12564 var ol = this.lastChild;
12565 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12566 if(this.childrenRendered && ol && node != ol){
12567 ol.renderIndent(true, true);
12570 this.renderIndent(true, true);
12574 // these methods are overridden to provide lazy rendering support
12575 // private override
12576 appendChild : function()
12578 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12579 if(node && this.childrenRendered){
12582 this.ui.updateExpandIcon();
12586 // private override
12587 removeChild : function(node){
12588 this.ownerTree.getSelectionModel().unselect(node);
12589 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12590 // if it's been rendered remove dom node
12591 if(this.childrenRendered){
12594 if(this.childNodes.length < 1){
12595 this.collapse(false, false);
12597 this.ui.updateExpandIcon();
12599 if(!this.firstChild) {
12600 this.childrenRendered = false;
12605 // private override
12606 insertBefore : function(node, refNode){
12607 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12608 if(newNode && refNode && this.childrenRendered){
12611 this.ui.updateExpandIcon();
12616 * Sets the text for this node
12617 * @param {String} text
12619 setText : function(text){
12620 var oldText = this.text;
12622 this.attributes.text = text;
12623 if(this.rendered){ // event without subscribing
12624 this.ui.onTextChange(this, text, oldText);
12626 this.fireEvent("textchange", this, text, oldText);
12630 * Triggers selection of this node
12632 select : function(){
12633 this.getOwnerTree().getSelectionModel().select(this);
12637 * Triggers deselection of this node
12639 unselect : function(){
12640 this.getOwnerTree().getSelectionModel().unselect(this);
12644 * Returns true if this node is selected
12645 * @return {Boolean}
12647 isSelected : function(){
12648 return this.getOwnerTree().getSelectionModel().isSelected(this);
12652 * Expand this node.
12653 * @param {Boolean} deep (optional) True to expand all children as well
12654 * @param {Boolean} anim (optional) false to cancel the default animation
12655 * @param {Function} callback (optional) A callback to be called when
12656 * expanding this node completes (does not wait for deep expand to complete).
12657 * Called with 1 parameter, this node.
12659 expand : function(deep, anim, callback){
12660 if(!this.expanded){
12661 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12664 if(!this.childrenRendered){
12665 this.renderChildren();
12667 this.expanded = true;
12668 if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
12669 this.ui.animExpand(function(){
12670 this.fireEvent("expand", this);
12671 if(typeof callback == "function"){
12675 this.expandChildNodes(true);
12677 }.createDelegate(this));
12681 this.fireEvent("expand", this);
12682 if(typeof callback == "function"){
12687 if(typeof callback == "function"){
12692 this.expandChildNodes(true);
12696 isHiddenRoot : function(){
12697 return this.isRoot && !this.getOwnerTree().rootVisible;
12701 * Collapse this node.
12702 * @param {Boolean} deep (optional) True to collapse all children as well
12703 * @param {Boolean} anim (optional) false to cancel the default animation
12705 collapse : function(deep, anim){
12706 if(this.expanded && !this.isHiddenRoot()){
12707 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12710 this.expanded = false;
12711 if((this.getOwnerTree().animate && anim !== false) || anim){
12712 this.ui.animCollapse(function(){
12713 this.fireEvent("collapse", this);
12715 this.collapseChildNodes(true);
12717 }.createDelegate(this));
12720 this.ui.collapse();
12721 this.fireEvent("collapse", this);
12725 var cs = this.childNodes;
12726 for(var i = 0, len = cs.length; i < len; i++) {
12727 cs[i].collapse(true, false);
12733 delayedExpand : function(delay){
12734 if(!this.expandProcId){
12735 this.expandProcId = this.expand.defer(delay, this);
12740 cancelExpand : function(){
12741 if(this.expandProcId){
12742 clearTimeout(this.expandProcId);
12744 this.expandProcId = false;
12748 * Toggles expanded/collapsed state of the node
12750 toggle : function(){
12759 * Ensures all parent nodes are expanded
12761 ensureVisible : function(callback){
12762 var tree = this.getOwnerTree();
12763 tree.expandPath(this.parentNode.getPath(), false, function(){
12764 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12765 Roo.callback(callback);
12766 }.createDelegate(this));
12770 * Expand all child nodes
12771 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12773 expandChildNodes : function(deep){
12774 var cs = this.childNodes;
12775 for(var i = 0, len = cs.length; i < len; i++) {
12776 cs[i].expand(deep);
12781 * Collapse all child nodes
12782 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12784 collapseChildNodes : function(deep){
12785 var cs = this.childNodes;
12786 for(var i = 0, len = cs.length; i < len; i++) {
12787 cs[i].collapse(deep);
12792 * Disables this node
12794 disable : function(){
12795 this.disabled = true;
12797 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12798 this.ui.onDisableChange(this, true);
12800 this.fireEvent("disabledchange", this, true);
12804 * Enables this node
12806 enable : function(){
12807 this.disabled = false;
12808 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12809 this.ui.onDisableChange(this, false);
12811 this.fireEvent("disabledchange", this, false);
12815 renderChildren : function(suppressEvent){
12816 if(suppressEvent !== false){
12817 this.fireEvent("beforechildrenrendered", this);
12819 var cs = this.childNodes;
12820 for(var i = 0, len = cs.length; i < len; i++){
12821 cs[i].render(true);
12823 this.childrenRendered = true;
12827 sort : function(fn, scope){
12828 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12829 if(this.childrenRendered){
12830 var cs = this.childNodes;
12831 for(var i = 0, len = cs.length; i < len; i++){
12832 cs[i].render(true);
12838 render : function(bulkRender){
12839 this.ui.render(bulkRender);
12840 if(!this.rendered){
12841 this.rendered = true;
12843 this.expanded = false;
12844 this.expand(false, false);
12850 renderIndent : function(deep, refresh){
12852 this.ui.childIndent = null;
12854 this.ui.renderIndent();
12855 if(deep === true && this.childrenRendered){
12856 var cs = this.childNodes;
12857 for(var i = 0, len = cs.length; i < len; i++){
12858 cs[i].renderIndent(true, refresh);
12864 * Ext JS Library 1.1.1
12865 * Copyright(c) 2006-2007, Ext JS, LLC.
12867 * Originally Released Under LGPL - original licence link has changed is not relivant.
12870 * <script type="text/javascript">
12874 * @class Roo.tree.AsyncTreeNode
12875 * @extends Roo.tree.TreeNode
12876 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12878 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12880 Roo.tree.AsyncTreeNode = function(config){
12881 this.loaded = false;
12882 this.loading = false;
12883 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12885 * @event beforeload
12886 * Fires before this node is loaded, return false to cancel
12887 * @param {Node} this This node
12889 this.addEvents({'beforeload':true, 'load': true});
12892 * Fires when this node is loaded
12893 * @param {Node} this This node
12896 * The loader used by this node (defaults to using the tree's defined loader)
12901 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12902 expand : function(deep, anim, callback){
12903 if(this.loading){ // if an async load is already running, waiting til it's done
12905 var f = function(){
12906 if(!this.loading){ // done loading
12907 clearInterval(timer);
12908 this.expand(deep, anim, callback);
12910 }.createDelegate(this);
12911 timer = setInterval(f, 200);
12915 if(this.fireEvent("beforeload", this) === false){
12918 this.loading = true;
12919 this.ui.beforeLoad(this);
12920 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12922 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12926 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12930 * Returns true if this node is currently loading
12931 * @return {Boolean}
12933 isLoading : function(){
12934 return this.loading;
12937 loadComplete : function(deep, anim, callback){
12938 this.loading = false;
12939 this.loaded = true;
12940 this.ui.afterLoad(this);
12941 this.fireEvent("load", this);
12942 this.expand(deep, anim, callback);
12946 * Returns true if this node has been loaded
12947 * @return {Boolean}
12949 isLoaded : function(){
12950 return this.loaded;
12953 hasChildNodes : function(){
12954 if(!this.isLeaf() && !this.loaded){
12957 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12962 * Trigger a reload for this node
12963 * @param {Function} callback
12965 reload : function(callback){
12966 this.collapse(false, false);
12967 while(this.firstChild){
12968 this.removeChild(this.firstChild);
12970 this.childrenRendered = false;
12971 this.loaded = false;
12972 if(this.isHiddenRoot()){
12973 this.expanded = false;
12975 this.expand(false, false, callback);
12979 * Ext JS Library 1.1.1
12980 * Copyright(c) 2006-2007, Ext JS, LLC.
12982 * Originally Released Under LGPL - original licence link has changed is not relivant.
12985 * <script type="text/javascript">
12989 * @class Roo.tree.TreeNodeUI
12991 * @param {Object} node The node to render
12992 * The TreeNode UI implementation is separate from the
12993 * tree implementation. Unless you are customizing the tree UI,
12994 * you should never have to use this directly.
12996 Roo.tree.TreeNodeUI = function(node){
12998 this.rendered = false;
12999 this.animating = false;
13000 this.emptyIcon = Roo.BLANK_IMAGE_URL;
13003 Roo.tree.TreeNodeUI.prototype = {
13004 removeChild : function(node){
13006 this.ctNode.removeChild(node.ui.getEl());
13010 beforeLoad : function(){
13011 this.addClass("x-tree-node-loading");
13014 afterLoad : function(){
13015 this.removeClass("x-tree-node-loading");
13018 onTextChange : function(node, text, oldText){
13020 this.textNode.innerHTML = text;
13024 onDisableChange : function(node, state){
13025 this.disabled = state;
13027 this.addClass("x-tree-node-disabled");
13029 this.removeClass("x-tree-node-disabled");
13033 onSelectedChange : function(state){
13036 this.addClass("x-tree-selected");
13039 this.removeClass("x-tree-selected");
13043 onMove : function(tree, node, oldParent, newParent, index, refNode){
13044 this.childIndent = null;
13046 var targetNode = newParent.ui.getContainer();
13047 if(!targetNode){//target not rendered
13048 this.holder = document.createElement("div");
13049 this.holder.appendChild(this.wrap);
13052 var insertBefore = refNode ? refNode.ui.getEl() : null;
13054 targetNode.insertBefore(this.wrap, insertBefore);
13056 targetNode.appendChild(this.wrap);
13058 this.node.renderIndent(true);
13062 addClass : function(cls){
13064 Roo.fly(this.elNode).addClass(cls);
13068 removeClass : function(cls){
13070 Roo.fly(this.elNode).removeClass(cls);
13074 remove : function(){
13076 this.holder = document.createElement("div");
13077 this.holder.appendChild(this.wrap);
13081 fireEvent : function(){
13082 return this.node.fireEvent.apply(this.node, arguments);
13085 initEvents : function(){
13086 this.node.on("move", this.onMove, this);
13087 var E = Roo.EventManager;
13088 var a = this.anchor;
13090 var el = Roo.fly(a, '_treeui');
13092 if(Roo.isOpera){ // opera render bug ignores the CSS
13093 el.setStyle("text-decoration", "none");
13096 el.on("click", this.onClick, this);
13097 el.on("dblclick", this.onDblClick, this);
13100 Roo.EventManager.on(this.checkbox,
13101 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13104 el.on("contextmenu", this.onContextMenu, this);
13106 var icon = Roo.fly(this.iconNode);
13107 icon.on("click", this.onClick, this);
13108 icon.on("dblclick", this.onDblClick, this);
13109 icon.on("contextmenu", this.onContextMenu, this);
13110 E.on(this.ecNode, "click", this.ecClick, this, true);
13112 if(this.node.disabled){
13113 this.addClass("x-tree-node-disabled");
13115 if(this.node.hidden){
13116 this.addClass("x-tree-node-disabled");
13118 var ot = this.node.getOwnerTree();
13119 var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
13120 if(dd && (!this.node.isRoot || ot.rootVisible)){
13121 Roo.dd.Registry.register(this.elNode, {
13123 handles: this.getDDHandles(),
13129 getDDHandles : function(){
13130 return [this.iconNode, this.textNode];
13135 this.wrap.style.display = "none";
13141 this.wrap.style.display = "";
13145 onContextMenu : function(e){
13146 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13147 e.preventDefault();
13149 this.fireEvent("contextmenu", this.node, e);
13153 onClick : function(e){
13158 if(this.fireEvent("beforeclick", this.node, e) !== false){
13159 if(!this.disabled && this.node.attributes.href){
13160 this.fireEvent("click", this.node, e);
13163 e.preventDefault();
13168 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13169 this.node.toggle();
13172 this.fireEvent("click", this.node, e);
13178 onDblClick : function(e){
13179 e.preventDefault();
13184 this.toggleCheck();
13186 if(!this.animating && this.node.hasChildNodes()){
13187 this.node.toggle();
13189 this.fireEvent("dblclick", this.node, e);
13192 onCheckChange : function(){
13193 var checked = this.checkbox.checked;
13194 this.node.attributes.checked = checked;
13195 this.fireEvent('checkchange', this.node, checked);
13198 ecClick : function(e){
13199 if(!this.animating && this.node.hasChildNodes()){
13200 this.node.toggle();
13204 startDrop : function(){
13205 this.dropping = true;
13208 // delayed drop so the click event doesn't get fired on a drop
13209 endDrop : function(){
13210 setTimeout(function(){
13211 this.dropping = false;
13212 }.createDelegate(this), 50);
13215 expand : function(){
13216 this.updateExpandIcon();
13217 this.ctNode.style.display = "";
13220 focus : function(){
13221 if(!this.node.preventHScroll){
13222 try{this.anchor.focus();
13224 }else if(!Roo.isIE){
13226 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13227 var l = noscroll.scrollLeft;
13228 this.anchor.focus();
13229 noscroll.scrollLeft = l;
13234 toggleCheck : function(value){
13235 var cb = this.checkbox;
13237 cb.checked = (value === undefined ? !cb.checked : value);
13243 this.anchor.blur();
13247 animExpand : function(callback){
13248 var ct = Roo.get(this.ctNode);
13250 if(!this.node.hasChildNodes()){
13251 this.updateExpandIcon();
13252 this.ctNode.style.display = "";
13253 Roo.callback(callback);
13256 this.animating = true;
13257 this.updateExpandIcon();
13260 callback : function(){
13261 this.animating = false;
13262 Roo.callback(callback);
13265 duration: this.node.ownerTree.duration || .25
13269 highlight : function(){
13270 var tree = this.node.getOwnerTree();
13271 Roo.fly(this.wrap).highlight(
13272 tree.hlColor || "C3DAF9",
13273 {endColor: tree.hlBaseColor}
13277 collapse : function(){
13278 this.updateExpandIcon();
13279 this.ctNode.style.display = "none";
13282 animCollapse : function(callback){
13283 var ct = Roo.get(this.ctNode);
13284 ct.enableDisplayMode('block');
13287 this.animating = true;
13288 this.updateExpandIcon();
13291 callback : function(){
13292 this.animating = false;
13293 Roo.callback(callback);
13296 duration: this.node.ownerTree.duration || .25
13300 getContainer : function(){
13301 return this.ctNode;
13304 getEl : function(){
13308 appendDDGhost : function(ghostNode){
13309 ghostNode.appendChild(this.elNode.cloneNode(true));
13312 getDDRepairXY : function(){
13313 return Roo.lib.Dom.getXY(this.iconNode);
13316 onRender : function(){
13320 render : function(bulkRender){
13321 var n = this.node, a = n.attributes;
13322 var targetNode = n.parentNode ?
13323 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13325 if(!this.rendered){
13326 this.rendered = true;
13328 this.renderElements(n, a, targetNode, bulkRender);
13331 if(this.textNode.setAttributeNS){
13332 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13334 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13337 this.textNode.setAttribute("ext:qtip", a.qtip);
13339 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13342 }else if(a.qtipCfg){
13343 a.qtipCfg.target = Roo.id(this.textNode);
13344 Roo.QuickTips.register(a.qtipCfg);
13347 if(!this.node.expanded){
13348 this.updateExpandIcon();
13351 if(bulkRender === true) {
13352 targetNode.appendChild(this.wrap);
13357 renderElements : function(n, a, targetNode, bulkRender)
13359 // add some indent caching, this helps performance when rendering a large tree
13360 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13361 var t = n.getOwnerTree();
13362 var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13363 if (typeof(n.attributes.html) != 'undefined') {
13364 txt = n.attributes.html;
13366 var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
13367 var cb = typeof a.checked == 'boolean';
13368 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13369 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13370 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13371 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13372 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13373 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13374 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13375 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
13376 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13377 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13380 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13381 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13382 n.nextSibling.ui.getEl(), buf.join(""));
13384 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13387 this.elNode = this.wrap.childNodes[0];
13388 this.ctNode = this.wrap.childNodes[1];
13389 var cs = this.elNode.childNodes;
13390 this.indentNode = cs[0];
13391 this.ecNode = cs[1];
13392 this.iconNode = cs[2];
13395 this.checkbox = cs[3];
13398 this.anchor = cs[index];
13399 this.textNode = cs[index].firstChild;
13402 getAnchor : function(){
13403 return this.anchor;
13406 getTextEl : function(){
13407 return this.textNode;
13410 getIconEl : function(){
13411 return this.iconNode;
13414 isChecked : function(){
13415 return this.checkbox ? this.checkbox.checked : false;
13418 updateExpandIcon : function(){
13420 var n = this.node, c1, c2;
13421 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13422 var hasChild = n.hasChildNodes();
13426 c1 = "x-tree-node-collapsed";
13427 c2 = "x-tree-node-expanded";
13430 c1 = "x-tree-node-expanded";
13431 c2 = "x-tree-node-collapsed";
13434 this.removeClass("x-tree-node-leaf");
13435 this.wasLeaf = false;
13437 if(this.c1 != c1 || this.c2 != c2){
13438 Roo.fly(this.elNode).replaceClass(c1, c2);
13439 this.c1 = c1; this.c2 = c2;
13442 // this changes non-leafs into leafs if they have no children.
13443 // it's not very rational behaviour..
13445 if(!this.wasLeaf && this.node.leaf){
13446 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13449 this.wasLeaf = true;
13452 var ecc = "x-tree-ec-icon "+cls;
13453 if(this.ecc != ecc){
13454 this.ecNode.className = ecc;
13460 getChildIndent : function(){
13461 if(!this.childIndent){
13465 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13467 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13469 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13474 this.childIndent = buf.join("");
13476 return this.childIndent;
13479 renderIndent : function(){
13482 var p = this.node.parentNode;
13484 indent = p.ui.getChildIndent();
13486 if(this.indentMarkup != indent){ // don't rerender if not required
13487 this.indentNode.innerHTML = indent;
13488 this.indentMarkup = indent;
13490 this.updateExpandIcon();
13495 Roo.tree.RootTreeNodeUI = function(){
13496 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13498 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13499 render : function(){
13500 if(!this.rendered){
13501 var targetNode = this.node.ownerTree.innerCt.dom;
13502 this.node.expanded = true;
13503 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13504 this.wrap = this.ctNode = targetNode.firstChild;
13507 collapse : function(){
13509 expand : function(){
13513 * Ext JS Library 1.1.1
13514 * Copyright(c) 2006-2007, Ext JS, LLC.
13516 * Originally Released Under LGPL - original licence link has changed is not relivant.
13519 * <script type="text/javascript">
13522 * @class Roo.tree.TreeLoader
13523 * @extends Roo.util.Observable
13524 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13525 * nodes from a specified URL. The response must be a javascript Array definition
13526 * who's elements are node definition objects. eg:
13531 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13532 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13539 * The old style respose with just an array is still supported, but not recommended.
13542 * A server request is sent, and child nodes are loaded only when a node is expanded.
13543 * The loading node's id is passed to the server under the parameter name "node" to
13544 * enable the server to produce the correct child nodes.
13546 * To pass extra parameters, an event handler may be attached to the "beforeload"
13547 * event, and the parameters specified in the TreeLoader's baseParams property:
13549 myTreeLoader.on("beforeload", function(treeLoader, node) {
13550 this.baseParams.category = node.attributes.category;
13553 * This would pass an HTTP parameter called "category" to the server containing
13554 * the value of the Node's "category" attribute.
13556 * Creates a new Treeloader.
13557 * @param {Object} config A config object containing config properties.
13559 Roo.tree.TreeLoader = function(config){
13560 this.baseParams = {};
13561 this.requestMethod = "POST";
13562 Roo.apply(this, config);
13567 * @event beforeload
13568 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13569 * @param {Object} This TreeLoader object.
13570 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13571 * @param {Object} callback The callback function specified in the {@link #load} call.
13576 * Fires when the node has been successfuly loaded.
13577 * @param {Object} This TreeLoader object.
13578 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13579 * @param {Object} response The response object containing the data from the server.
13583 * @event loadexception
13584 * Fires if the network request failed.
13585 * @param {Object} This TreeLoader object.
13586 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13587 * @param {Object} response The response object containing the data from the server.
13589 loadexception : true,
13592 * Fires before a node is created, enabling you to return custom Node types
13593 * @param {Object} This TreeLoader object.
13594 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13599 Roo.tree.TreeLoader.superclass.constructor.call(this);
13602 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13604 * @cfg {String} dataUrl The URL from which to request a Json string which
13605 * specifies an array of node definition object representing the child nodes
13609 * @cfg {String} requestMethod either GET or POST
13610 * defaults to POST (due to BC)
13614 * @cfg {Object} baseParams (optional) An object containing properties which
13615 * specify HTTP parameters to be passed to each request for child nodes.
13618 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13619 * created by this loader. If the attributes sent by the server have an attribute in this object,
13620 * they take priority.
13623 * @cfg {Object} uiProviders (optional) An object containing properties which
13625 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13626 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13627 * <i>uiProvider</i> attribute of a returned child node is a string rather
13628 * than a reference to a TreeNodeUI implementation, this that string value
13629 * is used as a property name in the uiProviders object. You can define the provider named
13630 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13635 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13636 * child nodes before loading.
13638 clearOnLoad : true,
13641 * @cfg {String} root (optional) Default to false. Use this to read data from an object
13642 * property on loading, rather than expecting an array. (eg. more compatible to a standard
13643 * Grid query { data : [ .....] }
13648 * @cfg {String} queryParam (optional)
13649 * Name of the query as it will be passed on the querystring (defaults to 'node')
13650 * eg. the request will be ?node=[id]
13657 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13658 * This is called automatically when a node is expanded, but may be used to reload
13659 * a node (or append new children if the {@link #clearOnLoad} option is false.)
13660 * @param {Roo.tree.TreeNode} node
13661 * @param {Function} callback
13663 load : function(node, callback){
13664 if(this.clearOnLoad){
13665 while(node.firstChild){
13666 node.removeChild(node.firstChild);
13669 if(node.attributes.children){ // preloaded json children
13670 var cs = node.attributes.children;
13671 for(var i = 0, len = cs.length; i < len; i++){
13672 node.appendChild(this.createNode(cs[i]));
13674 if(typeof callback == "function"){
13677 }else if(this.dataUrl){
13678 this.requestData(node, callback);
13682 getParams: function(node){
13683 var buf = [], bp = this.baseParams;
13684 for(var key in bp){
13685 if(typeof bp[key] != "function"){
13686 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13689 var n = this.queryParam === false ? 'node' : this.queryParam;
13690 buf.push(n + "=", encodeURIComponent(node.id));
13691 return buf.join("");
13694 requestData : function(node, callback){
13695 if(this.fireEvent("beforeload", this, node, callback) !== false){
13696 this.transId = Roo.Ajax.request({
13697 method:this.requestMethod,
13698 url: this.dataUrl||this.url,
13699 success: this.handleResponse,
13700 failure: this.handleFailure,
13702 argument: {callback: callback, node: node},
13703 params: this.getParams(node)
13706 // if the load is cancelled, make sure we notify
13707 // the node that we are done
13708 if(typeof callback == "function"){
13714 isLoading : function(){
13715 return this.transId ? true : false;
13718 abort : function(){
13719 if(this.isLoading()){
13720 Roo.Ajax.abort(this.transId);
13725 createNode : function(attr)
13727 // apply baseAttrs, nice idea Corey!
13728 if(this.baseAttrs){
13729 Roo.applyIf(attr, this.baseAttrs);
13731 if(this.applyLoader !== false){
13732 attr.loader = this;
13734 // uiProvider = depreciated..
13736 if(typeof(attr.uiProvider) == 'string'){
13737 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
13738 /** eval:var:attr */ eval(attr.uiProvider);
13740 if(typeof(this.uiProviders['default']) != 'undefined') {
13741 attr.uiProvider = this.uiProviders['default'];
13744 this.fireEvent('create', this, attr);
13746 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13748 new Roo.tree.TreeNode(attr) :
13749 new Roo.tree.AsyncTreeNode(attr));
13752 processResponse : function(response, node, callback)
13754 var json = response.responseText;
13757 var o = Roo.decode(json);
13759 if (this.root === false && typeof(o.success) != undefined) {
13760 this.root = 'data'; // the default behaviour for list like data..
13763 if (this.root !== false && !o.success) {
13764 // it's a failure condition.
13765 var a = response.argument;
13766 this.fireEvent("loadexception", this, a.node, response);
13767 Roo.log("Load failed - should have a handler really");
13773 if (this.root !== false) {
13777 for(var i = 0, len = o.length; i < len; i++){
13778 var n = this.createNode(o[i]);
13780 node.appendChild(n);
13783 if(typeof callback == "function"){
13784 callback(this, node);
13787 this.handleFailure(response);
13791 handleResponse : function(response){
13792 this.transId = false;
13793 var a = response.argument;
13794 this.processResponse(response, a.node, a.callback);
13795 this.fireEvent("load", this, a.node, response);
13798 handleFailure : function(response)
13800 // should handle failure better..
13801 this.transId = false;
13802 var a = response.argument;
13803 this.fireEvent("loadexception", this, a.node, response);
13804 if(typeof a.callback == "function"){
13805 a.callback(this, a.node);
13810 * Ext JS Library 1.1.1
13811 * Copyright(c) 2006-2007, Ext JS, LLC.
13813 * Originally Released Under LGPL - original licence link has changed is not relivant.
13816 * <script type="text/javascript">
13820 * @class Roo.tree.TreeFilter
13821 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13822 * @param {TreePanel} tree
13823 * @param {Object} config (optional)
13825 Roo.tree.TreeFilter = function(tree, config){
13827 this.filtered = {};
13828 Roo.apply(this, config);
13831 Roo.tree.TreeFilter.prototype = {
13838 * Filter the data by a specific attribute.
13839 * @param {String/RegExp} value Either string that the attribute value
13840 * should start with or a RegExp to test against the attribute
13841 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13842 * @param {TreeNode} startNode (optional) The node to start the filter at.
13844 filter : function(value, attr, startNode){
13845 attr = attr || "text";
13847 if(typeof value == "string"){
13848 var vlen = value.length;
13849 // auto clear empty filter
13850 if(vlen == 0 && this.clearBlank){
13854 value = value.toLowerCase();
13856 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13858 }else if(value.exec){ // regex?
13860 return value.test(n.attributes[attr]);
13863 throw 'Illegal filter type, must be string or regex';
13865 this.filterBy(f, null, startNode);
13869 * Filter by a function. The passed function will be called with each
13870 * node in the tree (or from the startNode). If the function returns true, the node is kept
13871 * otherwise it is filtered. If a node is filtered, its children are also filtered.
13872 * @param {Function} fn The filter function
13873 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13875 filterBy : function(fn, scope, startNode){
13876 startNode = startNode || this.tree.root;
13877 if(this.autoClear){
13880 var af = this.filtered, rv = this.reverse;
13881 var f = function(n){
13882 if(n == startNode){
13888 var m = fn.call(scope || n, n);
13896 startNode.cascade(f);
13899 if(typeof id != "function"){
13901 if(n && n.parentNode){
13902 n.parentNode.removeChild(n);
13910 * Clears the current filter. Note: with the "remove" option
13911 * set a filter cannot be cleared.
13913 clear : function(){
13915 var af = this.filtered;
13917 if(typeof id != "function"){
13924 this.filtered = {};
13929 * Ext JS Library 1.1.1
13930 * Copyright(c) 2006-2007, Ext JS, LLC.
13932 * Originally Released Under LGPL - original licence link has changed is not relivant.
13935 * <script type="text/javascript">
13940 * @class Roo.tree.TreeSorter
13941 * Provides sorting of nodes in a TreePanel
13943 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13944 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13945 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13946 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13947 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13948 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13950 * @param {TreePanel} tree
13951 * @param {Object} config
13953 Roo.tree.TreeSorter = function(tree, config){
13954 Roo.apply(this, config);
13955 tree.on("beforechildrenrendered", this.doSort, this);
13956 tree.on("append", this.updateSort, this);
13957 tree.on("insert", this.updateSort, this);
13959 var dsc = this.dir && this.dir.toLowerCase() == "desc";
13960 var p = this.property || "text";
13961 var sortType = this.sortType;
13962 var fs = this.folderSort;
13963 var cs = this.caseSensitive === true;
13964 var leafAttr = this.leafAttr || 'leaf';
13966 this.sortFn = function(n1, n2){
13968 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13971 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13975 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13976 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13978 return dsc ? +1 : -1;
13980 return dsc ? -1 : +1;
13987 Roo.tree.TreeSorter.prototype = {
13988 doSort : function(node){
13989 node.sort(this.sortFn);
13992 compareNodes : function(n1, n2){
13993 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13996 updateSort : function(tree, node){
13997 if(node.childrenRendered){
13998 this.doSort.defer(1, this, [node]);
14003 * Ext JS Library 1.1.1
14004 * Copyright(c) 2006-2007, Ext JS, LLC.
14006 * Originally Released Under LGPL - original licence link has changed is not relivant.
14009 * <script type="text/javascript">
14012 if(Roo.dd.DropZone){
14014 Roo.tree.TreeDropZone = function(tree, config){
14015 this.allowParentInsert = false;
14016 this.allowContainerDrop = false;
14017 this.appendOnly = false;
14018 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14020 this.lastInsertClass = "x-tree-no-status";
14021 this.dragOverData = {};
14024 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14025 ddGroup : "TreeDD",
14028 expandDelay : 1000,
14030 expandNode : function(node){
14031 if(node.hasChildNodes() && !node.isExpanded()){
14032 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14036 queueExpand : function(node){
14037 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14040 cancelExpand : function(){
14041 if(this.expandProcId){
14042 clearTimeout(this.expandProcId);
14043 this.expandProcId = false;
14047 isValidDropPoint : function(n, pt, dd, e, data){
14048 if(!n || !data){ return false; }
14049 var targetNode = n.node;
14050 var dropNode = data.node;
14051 // default drop rules
14052 if(!(targetNode && targetNode.isTarget && pt)){
14055 if(pt == "append" && targetNode.allowChildren === false){
14058 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14061 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14064 // reuse the object
14065 var overEvent = this.dragOverData;
14066 overEvent.tree = this.tree;
14067 overEvent.target = targetNode;
14068 overEvent.data = data;
14069 overEvent.point = pt;
14070 overEvent.source = dd;
14071 overEvent.rawEvent = e;
14072 overEvent.dropNode = dropNode;
14073 overEvent.cancel = false;
14074 var result = this.tree.fireEvent("nodedragover", overEvent);
14075 return overEvent.cancel === false && result !== false;
14078 getDropPoint : function(e, n, dd)
14082 return tn.allowChildren !== false ? "append" : false; // always append for root
14084 var dragEl = n.ddel;
14085 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14086 var y = Roo.lib.Event.getPageY(e);
14087 //var noAppend = tn.allowChildren === false || tn.isLeaf();
14089 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14090 var noAppend = tn.allowChildren === false;
14091 if(this.appendOnly || tn.parentNode.allowChildren === false){
14092 return noAppend ? false : "append";
14094 var noBelow = false;
14095 if(!this.allowParentInsert){
14096 noBelow = tn.hasChildNodes() && tn.isExpanded();
14098 var q = (b - t) / (noAppend ? 2 : 3);
14099 if(y >= t && y < (t + q)){
14101 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14108 onNodeEnter : function(n, dd, e, data)
14110 this.cancelExpand();
14113 onNodeOver : function(n, dd, e, data)
14116 var pt = this.getDropPoint(e, n, dd);
14119 // auto node expand check
14120 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14121 this.queueExpand(node);
14122 }else if(pt != "append"){
14123 this.cancelExpand();
14126 // set the insert point style on the target node
14127 var returnCls = this.dropNotAllowed;
14128 if(this.isValidDropPoint(n, pt, dd, e, data)){
14133 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14134 cls = "x-tree-drag-insert-above";
14135 }else if(pt == "below"){
14136 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14137 cls = "x-tree-drag-insert-below";
14139 returnCls = "x-tree-drop-ok-append";
14140 cls = "x-tree-drag-append";
14142 if(this.lastInsertClass != cls){
14143 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14144 this.lastInsertClass = cls;
14151 onNodeOut : function(n, dd, e, data){
14153 this.cancelExpand();
14154 this.removeDropIndicators(n);
14157 onNodeDrop : function(n, dd, e, data){
14158 var point = this.getDropPoint(e, n, dd);
14159 var targetNode = n.node;
14160 targetNode.ui.startDrop();
14161 if(!this.isValidDropPoint(n, point, dd, e, data)){
14162 targetNode.ui.endDrop();
14165 // first try to find the drop node
14166 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14169 target: targetNode,
14174 dropNode: dropNode,
14177 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14178 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14179 targetNode.ui.endDrop();
14182 // allow target changing
14183 targetNode = dropEvent.target;
14184 if(point == "append" && !targetNode.isExpanded()){
14185 targetNode.expand(false, null, function(){
14186 this.completeDrop(dropEvent);
14187 }.createDelegate(this));
14189 this.completeDrop(dropEvent);
14194 completeDrop : function(de){
14195 var ns = de.dropNode, p = de.point, t = de.target;
14196 if(!(ns instanceof Array)){
14200 for(var i = 0, len = ns.length; i < len; i++){
14203 t.parentNode.insertBefore(n, t);
14204 }else if(p == "below"){
14205 t.parentNode.insertBefore(n, t.nextSibling);
14211 if(this.tree.hlDrop){
14215 this.tree.fireEvent("nodedrop", de);
14218 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14219 if(this.tree.hlDrop){
14220 dropNode.ui.focus();
14221 dropNode.ui.highlight();
14223 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14226 getTree : function(){
14230 removeDropIndicators : function(n){
14233 Roo.fly(el).removeClass([
14234 "x-tree-drag-insert-above",
14235 "x-tree-drag-insert-below",
14236 "x-tree-drag-append"]);
14237 this.lastInsertClass = "_noclass";
14241 beforeDragDrop : function(target, e, id){
14242 this.cancelExpand();
14246 afterRepair : function(data){
14247 if(data && Roo.enableFx){
14248 data.node.ui.highlight();
14258 * Ext JS Library 1.1.1
14259 * Copyright(c) 2006-2007, Ext JS, LLC.
14261 * Originally Released Under LGPL - original licence link has changed is not relivant.
14264 * <script type="text/javascript">
14268 if(Roo.dd.DragZone){
14269 Roo.tree.TreeDragZone = function(tree, config){
14270 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14274 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14275 ddGroup : "TreeDD",
14277 onBeforeDrag : function(data, e){
14279 return n && n.draggable && !n.disabled;
14283 onInitDrag : function(e){
14284 var data = this.dragData;
14285 this.tree.getSelectionModel().select(data.node);
14286 this.proxy.update("");
14287 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14288 this.tree.fireEvent("startdrag", this.tree, data.node, e);
14291 getRepairXY : function(e, data){
14292 return data.node.ui.getDDRepairXY();
14295 onEndDrag : function(data, e){
14296 this.tree.fireEvent("enddrag", this.tree, data.node, e);
14301 onValidDrop : function(dd, e, id){
14302 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14306 beforeInvalidDrop : function(e, id){
14307 // this scrolls the original position back into view
14308 var sm = this.tree.getSelectionModel();
14309 sm.clearSelections();
14310 sm.select(this.dragData.node);
14315 * Ext JS Library 1.1.1
14316 * Copyright(c) 2006-2007, Ext JS, LLC.
14318 * Originally Released Under LGPL - original licence link has changed is not relivant.
14321 * <script type="text/javascript">
14324 * @class Roo.tree.TreeEditor
14325 * @extends Roo.Editor
14326 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
14327 * as the editor field.
14329 * @param {Object} config (used to be the tree panel.)
14330 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14332 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14333 * @cfg {Roo.form.TextField|Object} field The field configuration
14337 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14340 if (oldconfig) { // old style..
14341 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14344 tree = config.tree;
14345 config.field = config.field || {};
14346 config.field.xtype = 'TextField';
14347 field = Roo.factory(config.field, Roo.form);
14349 config = config || {};
14354 * @event beforenodeedit
14355 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
14356 * false from the handler of this event.
14357 * @param {Editor} this
14358 * @param {Roo.tree.Node} node
14360 "beforenodeedit" : true
14364 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14368 tree.on('beforeclick', this.beforeNodeClick, this);
14369 tree.getTreeEl().on('mousedown', this.hide, this);
14370 this.on('complete', this.updateNode, this);
14371 this.on('beforestartedit', this.fitToTree, this);
14372 this.on('startedit', this.bindScroll, this, {delay:10});
14373 this.on('specialkey', this.onSpecialKey, this);
14376 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14378 * @cfg {String} alignment
14379 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14385 * @cfg {Boolean} hideEl
14386 * True to hide the bound element while the editor is displayed (defaults to false)
14390 * @cfg {String} cls
14391 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14393 cls: "x-small-editor x-tree-editor",
14395 * @cfg {Boolean} shim
14396 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14402 * @cfg {Number} maxWidth
14403 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
14404 * the containing tree element's size, it will be automatically limited for you to the container width, taking
14405 * scroll and client offsets into account prior to each edit.
14412 fitToTree : function(ed, el){
14413 var td = this.tree.getTreeEl().dom, nd = el.dom;
14414 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
14415 td.scrollLeft = nd.offsetLeft;
14419 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14420 this.setSize(w, '');
14422 return this.fireEvent('beforenodeedit', this, this.editNode);
14427 triggerEdit : function(node){
14428 this.completeEdit();
14429 this.editNode = node;
14430 this.startEdit(node.ui.textNode, node.text);
14434 bindScroll : function(){
14435 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14439 beforeNodeClick : function(node, e){
14440 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14441 this.lastClick = new Date();
14442 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14444 this.triggerEdit(node);
14451 updateNode : function(ed, value){
14452 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14453 this.editNode.setText(value);
14457 onHide : function(){
14458 Roo.tree.TreeEditor.superclass.onHide.call(this);
14460 this.editNode.ui.focus();
14465 onSpecialKey : function(field, e){
14466 var k = e.getKey();
14470 }else if(k == e.ENTER && !e.hasModifier()){
14472 this.completeEdit();
14475 });//<Script type="text/javascript">
14478 * Ext JS Library 1.1.1
14479 * Copyright(c) 2006-2007, Ext JS, LLC.
14481 * Originally Released Under LGPL - original licence link has changed is not relivant.
14484 * <script type="text/javascript">
14488 * Not documented??? - probably should be...
14491 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14492 //focus: Roo.emptyFn, // prevent odd scrolling behavior
14494 renderElements : function(n, a, targetNode, bulkRender){
14495 //consel.log("renderElements?");
14496 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14498 var t = n.getOwnerTree();
14499 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14501 var cols = t.columns;
14502 var bw = t.borderWidth;
14504 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14505 var cb = typeof a.checked == "boolean";
14506 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14507 var colcls = 'x-t-' + tid + '-c0';
14509 '<li class="x-tree-node">',
14512 '<div class="x-tree-node-el ', a.cls,'">',
14514 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14517 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14518 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
14519 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14520 (a.icon ? ' x-tree-node-inline-icon' : ''),
14521 (a.iconCls ? ' '+a.iconCls : ''),
14522 '" unselectable="on" />',
14523 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
14524 (a.checked ? 'checked="checked" />' : ' />')) : ''),
14526 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14527 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14528 '<span unselectable="on" qtip="' + tx + '">',
14532 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14533 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14535 for(var i = 1, len = cols.length; i < len; i++){
14537 colcls = 'x-t-' + tid + '-c' +i;
14538 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14539 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14540 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14546 '<div class="x-clear"></div></div>',
14547 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14550 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14551 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14552 n.nextSibling.ui.getEl(), buf.join(""));
14554 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14556 var el = this.wrap.firstChild;
14558 this.elNode = el.firstChild;
14559 this.ranchor = el.childNodes[1];
14560 this.ctNode = this.wrap.childNodes[1];
14561 var cs = el.firstChild.childNodes;
14562 this.indentNode = cs[0];
14563 this.ecNode = cs[1];
14564 this.iconNode = cs[2];
14567 this.checkbox = cs[3];
14570 this.anchor = cs[index];
14572 this.textNode = cs[index].firstChild;
14574 //el.on("click", this.onClick, this);
14575 //el.on("dblclick", this.onDblClick, this);
14578 // console.log(this);
14580 initEvents : function(){
14581 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14584 var a = this.ranchor;
14586 var el = Roo.get(a);
14588 if(Roo.isOpera){ // opera render bug ignores the CSS
14589 el.setStyle("text-decoration", "none");
14592 el.on("click", this.onClick, this);
14593 el.on("dblclick", this.onDblClick, this);
14594 el.on("contextmenu", this.onContextMenu, this);
14598 /*onSelectedChange : function(state){
14601 this.addClass("x-tree-selected");
14604 this.removeClass("x-tree-selected");
14607 addClass : function(cls){
14609 Roo.fly(this.elRow).addClass(cls);
14615 removeClass : function(cls){
14617 Roo.fly(this.elRow).removeClass(cls);
14623 });//<Script type="text/javascript">
14627 * Ext JS Library 1.1.1
14628 * Copyright(c) 2006-2007, Ext JS, LLC.
14630 * Originally Released Under LGPL - original licence link has changed is not relivant.
14633 * <script type="text/javascript">
14638 * @class Roo.tree.ColumnTree
14639 * @extends Roo.data.TreePanel
14640 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
14641 * @cfg {int} borderWidth compined right/left border allowance
14643 * @param {String/HTMLElement/Element} el The container element
14644 * @param {Object} config
14646 Roo.tree.ColumnTree = function(el, config)
14648 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14652 * Fire this event on a container when it resizes
14653 * @param {int} w Width
14654 * @param {int} h Height
14658 this.on('resize', this.onResize, this);
14661 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14665 borderWidth: Roo.isBorderBox ? 0 : 2,
14668 render : function(){
14669 // add the header.....
14671 Roo.tree.ColumnTree.superclass.render.apply(this);
14673 this.el.addClass('x-column-tree');
14675 this.headers = this.el.createChild(
14676 {cls:'x-tree-headers'},this.innerCt.dom);
14678 var cols = this.columns, c;
14679 var totalWidth = 0;
14681 var len = cols.length;
14682 for(var i = 0; i < len; i++){
14684 totalWidth += c.width;
14685 this.headEls.push(this.headers.createChild({
14686 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14688 cls:'x-tree-hd-text',
14691 style:'width:'+(c.width-this.borderWidth)+'px;'
14694 this.headers.createChild({cls:'x-clear'});
14695 // prevent floats from wrapping when clipped
14696 this.headers.setWidth(totalWidth);
14697 //this.innerCt.setWidth(totalWidth);
14698 this.innerCt.setStyle({ overflow: 'auto' });
14699 this.onResize(this.width, this.height);
14703 onResize : function(w,h)
14708 this.innerCt.setWidth(this.width);
14709 this.innerCt.setHeight(this.height-20);
14712 var cols = this.columns, c;
14713 var totalWidth = 0;
14715 var len = cols.length;
14716 for(var i = 0; i < len; i++){
14718 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14719 // it's the expander..
14720 expEl = this.headEls[i];
14723 totalWidth += c.width;
14727 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
14729 this.headers.setWidth(w-20);
14738 * Ext JS Library 1.1.1
14739 * Copyright(c) 2006-2007, Ext JS, LLC.
14741 * Originally Released Under LGPL - original licence link has changed is not relivant.
14744 * <script type="text/javascript">
14748 * @class Roo.menu.Menu
14749 * @extends Roo.util.Observable
14750 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
14751 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14753 * Creates a new Menu
14754 * @param {Object} config Configuration options
14756 Roo.menu.Menu = function(config){
14757 Roo.apply(this, config);
14758 this.id = this.id || Roo.id();
14761 * @event beforeshow
14762 * Fires before this menu is displayed
14763 * @param {Roo.menu.Menu} this
14767 * @event beforehide
14768 * Fires before this menu is hidden
14769 * @param {Roo.menu.Menu} this
14774 * Fires after this menu is displayed
14775 * @param {Roo.menu.Menu} this
14780 * Fires after this menu is hidden
14781 * @param {Roo.menu.Menu} this
14786 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14787 * @param {Roo.menu.Menu} this
14788 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14789 * @param {Roo.EventObject} e
14794 * Fires when the mouse is hovering over this menu
14795 * @param {Roo.menu.Menu} this
14796 * @param {Roo.EventObject} e
14797 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14802 * Fires when the mouse exits this menu
14803 * @param {Roo.menu.Menu} this
14804 * @param {Roo.EventObject} e
14805 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14810 * Fires when a menu item contained in this menu is clicked
14811 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14812 * @param {Roo.EventObject} e
14816 if (this.registerMenu) {
14817 Roo.menu.MenuMgr.register(this);
14820 var mis = this.items;
14821 this.items = new Roo.util.MixedCollection();
14823 this.add.apply(this, mis);
14827 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14829 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14833 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14834 * for bottom-right shadow (defaults to "sides")
14838 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14839 * this menu (defaults to "tl-tr?")
14841 subMenuAlign : "tl-tr?",
14843 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14844 * relative to its element of origin (defaults to "tl-bl?")
14846 defaultAlign : "tl-bl?",
14848 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14850 allowOtherMenus : false,
14852 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14854 registerMenu : true,
14859 render : function(){
14863 var el = this.el = new Roo.Layer({
14865 shadow:this.shadow,
14867 parentEl: this.parentEl || document.body,
14871 this.keyNav = new Roo.menu.MenuNav(this);
14874 el.addClass("x-menu-plain");
14877 el.addClass(this.cls);
14879 // generic focus element
14880 this.focusEl = el.createChild({
14881 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14883 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14884 //disabling touch- as it's causing issues ..
14885 //ul.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
14886 ul.on('click' , this.onClick, this);
14889 ul.on("mouseover", this.onMouseOver, this);
14890 ul.on("mouseout", this.onMouseOut, this);
14891 this.items.each(function(item){
14896 var li = document.createElement("li");
14897 li.className = "x-menu-list-item";
14898 ul.dom.appendChild(li);
14899 item.render(li, this);
14906 autoWidth : function(){
14907 var el = this.el, ul = this.ul;
14911 var w = this.width;
14914 }else if(Roo.isIE){
14915 el.setWidth(this.minWidth);
14916 var t = el.dom.offsetWidth; // force recalc
14917 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14922 delayAutoWidth : function(){
14925 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14927 this.awTask.delay(20);
14932 findTargetItem : function(e){
14933 var t = e.getTarget(".x-menu-list-item", this.ul, true);
14934 if(t && t.menuItemId){
14935 return this.items.get(t.menuItemId);
14940 onClick : function(e){
14941 Roo.log("menu.onClick");
14942 var t = this.findTargetItem(e);
14947 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
14948 if(t == this.activeItem && t.shouldDeactivate(e)){
14949 this.activeItem.deactivate();
14950 delete this.activeItem;
14954 this.setActiveItem(t, true);
14962 this.fireEvent("click", this, t, e);
14966 setActiveItem : function(item, autoExpand){
14967 if(item != this.activeItem){
14968 if(this.activeItem){
14969 this.activeItem.deactivate();
14971 this.activeItem = item;
14972 item.activate(autoExpand);
14973 }else if(autoExpand){
14979 tryActivate : function(start, step){
14980 var items = this.items;
14981 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14982 var item = items.get(i);
14983 if(!item.disabled && item.canActivate){
14984 this.setActiveItem(item, false);
14992 onMouseOver : function(e){
14994 if(t = this.findTargetItem(e)){
14995 if(t.canActivate && !t.disabled){
14996 this.setActiveItem(t, true);
14999 this.fireEvent("mouseover", this, e, t);
15003 onMouseOut : function(e){
15005 if(t = this.findTargetItem(e)){
15006 if(t == this.activeItem && t.shouldDeactivate(e)){
15007 this.activeItem.deactivate();
15008 delete this.activeItem;
15011 this.fireEvent("mouseout", this, e, t);
15015 * Read-only. Returns true if the menu is currently displayed, else false.
15018 isVisible : function(){
15019 return this.el && !this.hidden;
15023 * Displays this menu relative to another element
15024 * @param {String/HTMLElement/Roo.Element} element The element to align to
15025 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15026 * the element (defaults to this.defaultAlign)
15027 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15029 show : function(el, pos, parentMenu){
15030 this.parentMenu = parentMenu;
15034 this.fireEvent("beforeshow", this);
15035 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15039 * Displays this menu at a specific xy position
15040 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15041 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15043 showAt : function(xy, parentMenu, /* private: */_e){
15044 this.parentMenu = parentMenu;
15049 this.fireEvent("beforeshow", this);
15050 xy = this.el.adjustForConstraints(xy);
15054 this.hidden = false;
15056 this.fireEvent("show", this);
15059 focus : function(){
15061 this.doFocus.defer(50, this);
15065 doFocus : function(){
15067 this.focusEl.focus();
15072 * Hides this menu and optionally all parent menus
15073 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15075 hide : function(deep){
15076 if(this.el && this.isVisible()){
15077 this.fireEvent("beforehide", this);
15078 if(this.activeItem){
15079 this.activeItem.deactivate();
15080 this.activeItem = null;
15083 this.hidden = true;
15084 this.fireEvent("hide", this);
15086 if(deep === true && this.parentMenu){
15087 this.parentMenu.hide(true);
15092 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15093 * Any of the following are valid:
15095 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15096 * <li>An HTMLElement object which will be converted to a menu item</li>
15097 * <li>A menu item config object that will be created as a new menu item</li>
15098 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15099 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15104 var menu = new Roo.menu.Menu();
15106 // Create a menu item to add by reference
15107 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15109 // Add a bunch of items at once using different methods.
15110 // Only the last item added will be returned.
15111 var item = menu.add(
15112 menuItem, // add existing item by ref
15113 'Dynamic Item', // new TextItem
15114 '-', // new separator
15115 { text: 'Config Item' } // new item by config
15118 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15119 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15122 var a = arguments, l = a.length, item;
15123 for(var i = 0; i < l; i++){
15125 if ((typeof(el) == "object") && el.xtype && el.xns) {
15126 el = Roo.factory(el, Roo.menu);
15129 if(el.render){ // some kind of Item
15130 item = this.addItem(el);
15131 }else if(typeof el == "string"){ // string
15132 if(el == "separator" || el == "-"){
15133 item = this.addSeparator();
15135 item = this.addText(el);
15137 }else if(el.tagName || el.el){ // element
15138 item = this.addElement(el);
15139 }else if(typeof el == "object"){ // must be menu item config?
15140 item = this.addMenuItem(el);
15147 * Returns this menu's underlying {@link Roo.Element} object
15148 * @return {Roo.Element} The element
15150 getEl : function(){
15158 * Adds a separator bar to the menu
15159 * @return {Roo.menu.Item} The menu item that was added
15161 addSeparator : function(){
15162 return this.addItem(new Roo.menu.Separator());
15166 * Adds an {@link Roo.Element} object to the menu
15167 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15168 * @return {Roo.menu.Item} The menu item that was added
15170 addElement : function(el){
15171 return this.addItem(new Roo.menu.BaseItem(el));
15175 * Adds an existing object based on {@link Roo.menu.Item} to the menu
15176 * @param {Roo.menu.Item} item The menu item to add
15177 * @return {Roo.menu.Item} The menu item that was added
15179 addItem : function(item){
15180 this.items.add(item);
15182 var li = document.createElement("li");
15183 li.className = "x-menu-list-item";
15184 this.ul.dom.appendChild(li);
15185 item.render(li, this);
15186 this.delayAutoWidth();
15192 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15193 * @param {Object} config A MenuItem config object
15194 * @return {Roo.menu.Item} The menu item that was added
15196 addMenuItem : function(config){
15197 if(!(config instanceof Roo.menu.Item)){
15198 if(typeof config.checked == "boolean"){ // must be check menu item config?
15199 config = new Roo.menu.CheckItem(config);
15201 config = new Roo.menu.Item(config);
15204 return this.addItem(config);
15208 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15209 * @param {String} text The text to display in the menu item
15210 * @return {Roo.menu.Item} The menu item that was added
15212 addText : function(text){
15213 return this.addItem(new Roo.menu.TextItem({ text : text }));
15217 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15218 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15219 * @param {Roo.menu.Item} item The menu item to add
15220 * @return {Roo.menu.Item} The menu item that was added
15222 insert : function(index, item){
15223 this.items.insert(index, item);
15225 var li = document.createElement("li");
15226 li.className = "x-menu-list-item";
15227 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15228 item.render(li, this);
15229 this.delayAutoWidth();
15235 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15236 * @param {Roo.menu.Item} item The menu item to remove
15238 remove : function(item){
15239 this.items.removeKey(item.id);
15244 * Removes and destroys all items in the menu
15246 removeAll : function(){
15248 while(f = this.items.first()){
15254 // MenuNav is a private utility class used internally by the Menu
15255 Roo.menu.MenuNav = function(menu){
15256 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15257 this.scope = this.menu = menu;
15260 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15261 doRelay : function(e, h){
15262 var k = e.getKey();
15263 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15264 this.menu.tryActivate(0, 1);
15267 return h.call(this.scope || this, e, this.menu);
15270 up : function(e, m){
15271 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15272 m.tryActivate(m.items.length-1, -1);
15276 down : function(e, m){
15277 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15278 m.tryActivate(0, 1);
15282 right : function(e, m){
15284 m.activeItem.expandMenu(true);
15288 left : function(e, m){
15290 if(m.parentMenu && m.parentMenu.activeItem){
15291 m.parentMenu.activeItem.activate();
15295 enter : function(e, m){
15297 e.stopPropagation();
15298 m.activeItem.onClick(e);
15299 m.fireEvent("click", this, m.activeItem);
15305 * Ext JS Library 1.1.1
15306 * Copyright(c) 2006-2007, Ext JS, LLC.
15308 * Originally Released Under LGPL - original licence link has changed is not relivant.
15311 * <script type="text/javascript">
15315 * @class Roo.menu.MenuMgr
15316 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15319 Roo.menu.MenuMgr = function(){
15320 var menus, active, groups = {}, attached = false, lastShow = new Date();
15322 // private - called when first menu is created
15325 active = new Roo.util.MixedCollection();
15326 Roo.get(document).addKeyListener(27, function(){
15327 if(active.length > 0){
15334 function hideAll(){
15335 if(active && active.length > 0){
15336 var c = active.clone();
15337 c.each(function(m){
15344 function onHide(m){
15346 if(active.length < 1){
15347 Roo.get(document).un("mousedown", onMouseDown);
15353 function onShow(m){
15354 var last = active.last();
15355 lastShow = new Date();
15358 Roo.get(document).on("mousedown", onMouseDown);
15362 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15363 m.parentMenu.activeChild = m;
15364 }else if(last && last.isVisible()){
15365 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15370 function onBeforeHide(m){
15372 m.activeChild.hide();
15374 if(m.autoHideTimer){
15375 clearTimeout(m.autoHideTimer);
15376 delete m.autoHideTimer;
15381 function onBeforeShow(m){
15382 var pm = m.parentMenu;
15383 if(!pm && !m.allowOtherMenus){
15385 }else if(pm && pm.activeChild && active != m){
15386 pm.activeChild.hide();
15391 function onMouseDown(e){
15392 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15398 function onBeforeCheck(mi, state){
15400 var g = groups[mi.group];
15401 for(var i = 0, l = g.length; i < l; i++){
15403 g[i].setChecked(false);
15412 * Hides all menus that are currently visible
15414 hideAll : function(){
15419 register : function(menu){
15423 menus[menu.id] = menu;
15424 menu.on("beforehide", onBeforeHide);
15425 menu.on("hide", onHide);
15426 menu.on("beforeshow", onBeforeShow);
15427 menu.on("show", onShow);
15428 var g = menu.group;
15429 if(g && menu.events["checkchange"]){
15433 groups[g].push(menu);
15434 menu.on("checkchange", onCheck);
15439 * Returns a {@link Roo.menu.Menu} object
15440 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15441 * be used to generate and return a new Menu instance.
15443 get : function(menu){
15444 if(typeof menu == "string"){ // menu id
15445 return menus[menu];
15446 }else if(menu.events){ // menu instance
15448 }else if(typeof menu.length == 'number'){ // array of menu items?
15449 return new Roo.menu.Menu({items:menu});
15450 }else{ // otherwise, must be a config
15451 return new Roo.menu.Menu(menu);
15456 unregister : function(menu){
15457 delete menus[menu.id];
15458 menu.un("beforehide", onBeforeHide);
15459 menu.un("hide", onHide);
15460 menu.un("beforeshow", onBeforeShow);
15461 menu.un("show", onShow);
15462 var g = menu.group;
15463 if(g && menu.events["checkchange"]){
15464 groups[g].remove(menu);
15465 menu.un("checkchange", onCheck);
15470 registerCheckable : function(menuItem){
15471 var g = menuItem.group;
15476 groups[g].push(menuItem);
15477 menuItem.on("beforecheckchange", onBeforeCheck);
15482 unregisterCheckable : function(menuItem){
15483 var g = menuItem.group;
15485 groups[g].remove(menuItem);
15486 menuItem.un("beforecheckchange", onBeforeCheck);
15492 * Ext JS Library 1.1.1
15493 * Copyright(c) 2006-2007, Ext JS, LLC.
15495 * Originally Released Under LGPL - original licence link has changed is not relivant.
15498 * <script type="text/javascript">
15503 * @class Roo.menu.BaseItem
15504 * @extends Roo.Component
15505 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
15506 * management and base configuration options shared by all menu components.
15508 * Creates a new BaseItem
15509 * @param {Object} config Configuration options
15511 Roo.menu.BaseItem = function(config){
15512 Roo.menu.BaseItem.superclass.constructor.call(this, config);
15517 * Fires when this item is clicked
15518 * @param {Roo.menu.BaseItem} this
15519 * @param {Roo.EventObject} e
15524 * Fires when this item is activated
15525 * @param {Roo.menu.BaseItem} this
15529 * @event deactivate
15530 * Fires when this item is deactivated
15531 * @param {Roo.menu.BaseItem} this
15537 this.on("click", this.handler, this.scope, true);
15541 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15543 * @cfg {Function} handler
15544 * A function that will handle the click event of this menu item (defaults to undefined)
15547 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15549 canActivate : false,
15552 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15557 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15559 activeClass : "x-menu-item-active",
15561 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15563 hideOnClick : true,
15565 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15570 ctype: "Roo.menu.BaseItem",
15573 actionMode : "container",
15576 render : function(container, parentMenu){
15577 this.parentMenu = parentMenu;
15578 Roo.menu.BaseItem.superclass.render.call(this, container);
15579 this.container.menuItemId = this.id;
15583 onRender : function(container, position){
15584 this.el = Roo.get(this.el);
15585 container.dom.appendChild(this.el.dom);
15589 onClick : function(e){
15590 if(!this.disabled && this.fireEvent("click", this, e) !== false
15591 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15592 this.handleClick(e);
15599 activate : function(){
15603 var li = this.container;
15604 li.addClass(this.activeClass);
15605 this.region = li.getRegion().adjust(2, 2, -2, -2);
15606 this.fireEvent("activate", this);
15611 deactivate : function(){
15612 this.container.removeClass(this.activeClass);
15613 this.fireEvent("deactivate", this);
15617 shouldDeactivate : function(e){
15618 return !this.region || !this.region.contains(e.getPoint());
15622 handleClick : function(e){
15623 if(this.hideOnClick){
15624 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15629 expandMenu : function(autoActivate){
15634 hideMenu : function(){
15639 * Ext JS Library 1.1.1
15640 * Copyright(c) 2006-2007, Ext JS, LLC.
15642 * Originally Released Under LGPL - original licence link has changed is not relivant.
15645 * <script type="text/javascript">
15649 * @class Roo.menu.Adapter
15650 * @extends Roo.menu.BaseItem
15651 * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
15652 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15654 * Creates a new Adapter
15655 * @param {Object} config Configuration options
15657 Roo.menu.Adapter = function(component, config){
15658 Roo.menu.Adapter.superclass.constructor.call(this, config);
15659 this.component = component;
15661 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15663 canActivate : true,
15666 onRender : function(container, position){
15667 this.component.render(container);
15668 this.el = this.component.getEl();
15672 activate : function(){
15676 this.component.focus();
15677 this.fireEvent("activate", this);
15682 deactivate : function(){
15683 this.fireEvent("deactivate", this);
15687 disable : function(){
15688 this.component.disable();
15689 Roo.menu.Adapter.superclass.disable.call(this);
15693 enable : function(){
15694 this.component.enable();
15695 Roo.menu.Adapter.superclass.enable.call(this);
15699 * Ext JS Library 1.1.1
15700 * Copyright(c) 2006-2007, Ext JS, LLC.
15702 * Originally Released Under LGPL - original licence link has changed is not relivant.
15705 * <script type="text/javascript">
15709 * @class Roo.menu.TextItem
15710 * @extends Roo.menu.BaseItem
15711 * Adds a static text string to a menu, usually used as either a heading or group separator.
15712 * Note: old style constructor with text is still supported.
15715 * Creates a new TextItem
15716 * @param {Object} cfg Configuration
15718 Roo.menu.TextItem = function(cfg){
15719 if (typeof(cfg) == 'string') {
15722 Roo.apply(this,cfg);
15725 Roo.menu.TextItem.superclass.constructor.call(this);
15728 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15730 * @cfg {Boolean} text Text to show on item.
15735 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15737 hideOnClick : false,
15739 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15741 itemCls : "x-menu-text",
15744 onRender : function(){
15745 var s = document.createElement("span");
15746 s.className = this.itemCls;
15747 s.innerHTML = this.text;
15749 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15753 * Ext JS Library 1.1.1
15754 * Copyright(c) 2006-2007, Ext JS, LLC.
15756 * Originally Released Under LGPL - original licence link has changed is not relivant.
15759 * <script type="text/javascript">
15763 * @class Roo.menu.Separator
15764 * @extends Roo.menu.BaseItem
15765 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15766 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15768 * @param {Object} config Configuration options
15770 Roo.menu.Separator = function(config){
15771 Roo.menu.Separator.superclass.constructor.call(this, config);
15774 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15776 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15778 itemCls : "x-menu-sep",
15780 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15782 hideOnClick : false,
15785 onRender : function(li){
15786 var s = document.createElement("span");
15787 s.className = this.itemCls;
15788 s.innerHTML = " ";
15790 li.addClass("x-menu-sep-li");
15791 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15795 * Ext JS Library 1.1.1
15796 * Copyright(c) 2006-2007, Ext JS, LLC.
15798 * Originally Released Under LGPL - original licence link has changed is not relivant.
15801 * <script type="text/javascript">
15804 * @class Roo.menu.Item
15805 * @extends Roo.menu.BaseItem
15806 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15807 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15808 * activation and click handling.
15810 * Creates a new Item
15811 * @param {Object} config Configuration options
15813 Roo.menu.Item = function(config){
15814 Roo.menu.Item.superclass.constructor.call(this, config);
15816 this.menu = Roo.menu.MenuMgr.get(this.menu);
15819 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15822 * @cfg {String} text
15823 * The text to show on the menu item.
15827 * @cfg {String} HTML to render in menu
15828 * The text to show on the menu item (HTML version).
15832 * @cfg {String} icon
15833 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15837 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15839 itemCls : "x-menu-item",
15841 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15843 canActivate : true,
15845 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15848 // doc'd in BaseItem
15852 ctype: "Roo.menu.Item",
15855 onRender : function(container, position){
15856 var el = document.createElement("a");
15857 el.hideFocus = true;
15858 el.unselectable = "on";
15859 el.href = this.href || "#";
15860 if(this.hrefTarget){
15861 el.target = this.hrefTarget;
15863 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
15865 var html = this.html.length ? this.html : String.format('{0}',this.text);
15867 el.innerHTML = String.format(
15868 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15869 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15871 Roo.menu.Item.superclass.onRender.call(this, container, position);
15875 * Sets the text to display in this menu item
15876 * @param {String} text The text to display
15877 * @param {Boolean} isHTML true to indicate text is pure html.
15879 setText : function(text, isHTML){
15887 var html = this.html.length ? this.html : String.format('{0}',this.text);
15889 this.el.update(String.format(
15890 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15891 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15892 this.parentMenu.autoWidth();
15897 handleClick : function(e){
15898 if(!this.href){ // if no link defined, stop the event automatically
15901 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15905 activate : function(autoExpand){
15906 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15916 shouldDeactivate : function(e){
15917 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15918 if(this.menu && this.menu.isVisible()){
15919 return !this.menu.getEl().getRegion().contains(e.getPoint());
15927 deactivate : function(){
15928 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15933 expandMenu : function(autoActivate){
15934 if(!this.disabled && this.menu){
15935 clearTimeout(this.hideTimer);
15936 delete this.hideTimer;
15937 if(!this.menu.isVisible() && !this.showTimer){
15938 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15939 }else if (this.menu.isVisible() && autoActivate){
15940 this.menu.tryActivate(0, 1);
15946 deferExpand : function(autoActivate){
15947 delete this.showTimer;
15948 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15950 this.menu.tryActivate(0, 1);
15955 hideMenu : function(){
15956 clearTimeout(this.showTimer);
15957 delete this.showTimer;
15958 if(!this.hideTimer && this.menu && this.menu.isVisible()){
15959 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15964 deferHide : function(){
15965 delete this.hideTimer;
15970 * Ext JS Library 1.1.1
15971 * Copyright(c) 2006-2007, Ext JS, LLC.
15973 * Originally Released Under LGPL - original licence link has changed is not relivant.
15976 * <script type="text/javascript">
15980 * @class Roo.menu.CheckItem
15981 * @extends Roo.menu.Item
15982 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15984 * Creates a new CheckItem
15985 * @param {Object} config Configuration options
15987 Roo.menu.CheckItem = function(config){
15988 Roo.menu.CheckItem.superclass.constructor.call(this, config);
15991 * @event beforecheckchange
15992 * Fires before the checked value is set, providing an opportunity to cancel if needed
15993 * @param {Roo.menu.CheckItem} this
15994 * @param {Boolean} checked The new checked value that will be set
15996 "beforecheckchange" : true,
15998 * @event checkchange
15999 * Fires after the checked value has been set
16000 * @param {Roo.menu.CheckItem} this
16001 * @param {Boolean} checked The checked value that was set
16003 "checkchange" : true
16005 if(this.checkHandler){
16006 this.on('checkchange', this.checkHandler, this.scope);
16009 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16011 * @cfg {String} group
16012 * All check items with the same group name will automatically be grouped into a single-select
16013 * radio button group (defaults to '')
16016 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16018 itemCls : "x-menu-item x-menu-check-item",
16020 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16022 groupClass : "x-menu-group-item",
16025 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
16026 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16027 * initialized with checked = true will be rendered as checked.
16032 ctype: "Roo.menu.CheckItem",
16035 onRender : function(c){
16036 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16038 this.el.addClass(this.groupClass);
16040 Roo.menu.MenuMgr.registerCheckable(this);
16042 this.checked = false;
16043 this.setChecked(true, true);
16048 destroy : function(){
16050 Roo.menu.MenuMgr.unregisterCheckable(this);
16052 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16056 * Set the checked state of this item
16057 * @param {Boolean} checked The new checked value
16058 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16060 setChecked : function(state, suppressEvent){
16061 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16062 if(this.container){
16063 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16065 this.checked = state;
16066 if(suppressEvent !== true){
16067 this.fireEvent("checkchange", this, state);
16073 handleClick : function(e){
16074 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16075 this.setChecked(!this.checked);
16077 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16081 * Ext JS Library 1.1.1
16082 * Copyright(c) 2006-2007, Ext JS, LLC.
16084 * Originally Released Under LGPL - original licence link has changed is not relivant.
16087 * <script type="text/javascript">
16091 * @class Roo.menu.DateItem
16092 * @extends Roo.menu.Adapter
16093 * A menu item that wraps the {@link Roo.DatPicker} component.
16095 * Creates a new DateItem
16096 * @param {Object} config Configuration options
16098 Roo.menu.DateItem = function(config){
16099 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16100 /** The Roo.DatePicker object @type Roo.DatePicker */
16101 this.picker = this.component;
16102 this.addEvents({select: true});
16104 this.picker.on("render", function(picker){
16105 picker.getEl().swallowEvent("click");
16106 picker.container.addClass("x-menu-date-item");
16109 this.picker.on("select", this.onSelect, this);
16112 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16114 onSelect : function(picker, date){
16115 this.fireEvent("select", this, date, picker);
16116 Roo.menu.DateItem.superclass.handleClick.call(this);
16120 * Ext JS Library 1.1.1
16121 * Copyright(c) 2006-2007, Ext JS, LLC.
16123 * Originally Released Under LGPL - original licence link has changed is not relivant.
16126 * <script type="text/javascript">
16130 * @class Roo.menu.ColorItem
16131 * @extends Roo.menu.Adapter
16132 * A menu item that wraps the {@link Roo.ColorPalette} component.
16134 * Creates a new ColorItem
16135 * @param {Object} config Configuration options
16137 Roo.menu.ColorItem = function(config){
16138 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16139 /** The Roo.ColorPalette object @type Roo.ColorPalette */
16140 this.palette = this.component;
16141 this.relayEvents(this.palette, ["select"]);
16142 if(this.selectHandler){
16143 this.on('select', this.selectHandler, this.scope);
16146 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16148 * Ext JS Library 1.1.1
16149 * Copyright(c) 2006-2007, Ext JS, LLC.
16151 * Originally Released Under LGPL - original licence link has changed is not relivant.
16154 * <script type="text/javascript">
16159 * @class Roo.menu.DateMenu
16160 * @extends Roo.menu.Menu
16161 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16163 * Creates a new DateMenu
16164 * @param {Object} config Configuration options
16166 Roo.menu.DateMenu = function(config){
16167 Roo.menu.DateMenu.superclass.constructor.call(this, config);
16169 var di = new Roo.menu.DateItem(config);
16172 * The {@link Roo.DatePicker} instance for this DateMenu
16175 this.picker = di.picker;
16178 * @param {DatePicker} picker
16179 * @param {Date} date
16181 this.relayEvents(di, ["select"]);
16182 this.on('beforeshow', function(){
16184 this.picker.hideMonthPicker(false);
16188 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16192 * Ext JS Library 1.1.1
16193 * Copyright(c) 2006-2007, Ext JS, LLC.
16195 * Originally Released Under LGPL - original licence link has changed is not relivant.
16198 * <script type="text/javascript">
16203 * @class Roo.menu.ColorMenu
16204 * @extends Roo.menu.Menu
16205 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16207 * Creates a new ColorMenu
16208 * @param {Object} config Configuration options
16210 Roo.menu.ColorMenu = function(config){
16211 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16213 var ci = new Roo.menu.ColorItem(config);
16216 * The {@link Roo.ColorPalette} instance for this ColorMenu
16217 * @type ColorPalette
16219 this.palette = ci.palette;
16222 * @param {ColorPalette} palette
16223 * @param {String} color
16225 this.relayEvents(ci, ["select"]);
16227 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16229 * Ext JS Library 1.1.1
16230 * Copyright(c) 2006-2007, Ext JS, LLC.
16232 * Originally Released Under LGPL - original licence link has changed is not relivant.
16235 * <script type="text/javascript">
16239 * @class Roo.form.Field
16240 * @extends Roo.BoxComponent
16241 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16243 * Creates a new Field
16244 * @param {Object} config Configuration options
16246 Roo.form.Field = function(config){
16247 Roo.form.Field.superclass.constructor.call(this, config);
16250 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
16252 * @cfg {String} fieldLabel Label to use when rendering a form.
16255 * @cfg {String} qtip Mouse over tip
16259 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16261 invalidClass : "x-form-invalid",
16263 * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
16265 invalidText : "The value in this field is invalid",
16267 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16269 focusClass : "x-form-focus",
16271 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16272 automatic validation (defaults to "keyup").
16274 validationEvent : "keyup",
16276 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16278 validateOnBlur : true,
16280 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16282 validationDelay : 250,
16284 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16285 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16287 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16289 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16291 fieldClass : "x-form-field",
16293 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
16296 ----------- ----------------------------------------------------------------------
16297 qtip Display a quick tip when the user hovers over the field
16298 title Display a default browser title attribute popup
16299 under Add a block div beneath the field containing the error text
16300 side Add an error icon to the right of the field with a popup on hover
16301 [element id] Add the error text directly to the innerHTML of the specified element
16304 msgTarget : 'qtip',
16306 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16311 * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
16316 * @cfg {Boolean} disabled True to disable the field (defaults to false).
16321 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16323 inputType : undefined,
16326 * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
16328 tabIndex : undefined,
16331 isFormField : true,
16336 * @property {Roo.Element} fieldEl
16337 * Element Containing the rendered Field (with label etc.)
16340 * @cfg {Mixed} value A value to initialize this field with.
16345 * @cfg {String} name The field's HTML name attribute.
16348 * @cfg {String} cls A CSS class to apply to the field's underlying element.
16351 loadedValue : false,
16355 initComponent : function(){
16356 Roo.form.Field.superclass.initComponent.call(this);
16360 * Fires when this field receives input focus.
16361 * @param {Roo.form.Field} this
16366 * Fires when this field loses input focus.
16367 * @param {Roo.form.Field} this
16371 * @event specialkey
16372 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
16373 * {@link Roo.EventObject#getKey} to determine which key was pressed.
16374 * @param {Roo.form.Field} this
16375 * @param {Roo.EventObject} e The event object
16380 * Fires just before the field blurs if the field value has changed.
16381 * @param {Roo.form.Field} this
16382 * @param {Mixed} newValue The new value
16383 * @param {Mixed} oldValue The original value
16388 * Fires after the field has been marked as invalid.
16389 * @param {Roo.form.Field} this
16390 * @param {String} msg The validation message
16395 * Fires after the field has been validated with no errors.
16396 * @param {Roo.form.Field} this
16401 * Fires after the key up
16402 * @param {Roo.form.Field} this
16403 * @param {Roo.EventObject} e The event Object
16410 * Returns the name attribute of the field if available
16411 * @return {String} name The field name
16413 getName: function(){
16414 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16418 onRender : function(ct, position){
16419 Roo.form.Field.superclass.onRender.call(this, ct, position);
16421 var cfg = this.getAutoCreate();
16423 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16425 if (!cfg.name.length) {
16428 if(this.inputType){
16429 cfg.type = this.inputType;
16431 this.el = ct.createChild(cfg, position);
16433 var type = this.el.dom.type;
16435 if(type == 'password'){
16438 this.el.addClass('x-form-'+type);
16441 this.el.dom.readOnly = true;
16443 if(this.tabIndex !== undefined){
16444 this.el.dom.setAttribute('tabIndex', this.tabIndex);
16447 this.el.addClass([this.fieldClass, this.cls]);
16452 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16453 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16454 * @return {Roo.form.Field} this
16456 applyTo : function(target){
16457 this.allowDomMove = false;
16458 this.el = Roo.get(target);
16459 this.render(this.el.dom.parentNode);
16464 initValue : function(){
16465 if(this.value !== undefined){
16466 this.setValue(this.value);
16467 }else if(this.el.dom.value.length > 0){
16468 this.setValue(this.el.dom.value);
16473 * Returns true if this field has been changed since it was originally loaded and is not disabled.
16474 * DEPRICATED - it never worked well - use hasChanged/resetHasChanged.
16476 isDirty : function() {
16477 if(this.disabled) {
16480 return String(this.getValue()) !== String(this.originalValue);
16484 * stores the current value in loadedValue
16486 resetHasChanged : function()
16488 this.loadedValue = String(this.getValue());
16491 * checks the current value against the 'loaded' value.
16492 * Note - will return false if 'resetHasChanged' has not been called first.
16494 hasChanged : function()
16496 if(this.disabled || this.readOnly) {
16499 return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16505 afterRender : function(){
16506 Roo.form.Field.superclass.afterRender.call(this);
16511 fireKey : function(e){
16512 //Roo.log('field ' + e.getKey());
16513 if(e.isNavKeyPress()){
16514 this.fireEvent("specialkey", this, e);
16519 * Resets the current field value to the originally loaded value and clears any validation messages
16521 reset : function(){
16522 this.setValue(this.resetValue);
16523 this.clearInvalid();
16527 initEvents : function(){
16528 // safari killled keypress - so keydown is now used..
16529 this.el.on("keydown" , this.fireKey, this);
16530 this.el.on("focus", this.onFocus, this);
16531 this.el.on("blur", this.onBlur, this);
16532 this.el.relayEvent('keyup', this);
16534 // reference to original value for reset
16535 this.originalValue = this.getValue();
16536 this.resetValue = this.getValue();
16540 onFocus : function(){
16541 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16542 this.el.addClass(this.focusClass);
16544 if(!this.hasFocus){
16545 this.hasFocus = true;
16546 this.startValue = this.getValue();
16547 this.fireEvent("focus", this);
16551 beforeBlur : Roo.emptyFn,
16554 onBlur : function(){
16556 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16557 this.el.removeClass(this.focusClass);
16559 this.hasFocus = false;
16560 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16563 var v = this.getValue();
16564 if(String(v) !== String(this.startValue)){
16565 this.fireEvent('change', this, v, this.startValue);
16567 this.fireEvent("blur", this);
16571 * Returns whether or not the field value is currently valid
16572 * @param {Boolean} preventMark True to disable marking the field invalid
16573 * @return {Boolean} True if the value is valid, else false
16575 isValid : function(preventMark){
16579 var restore = this.preventMark;
16580 this.preventMark = preventMark === true;
16581 var v = this.validateValue(this.processValue(this.getRawValue()));
16582 this.preventMark = restore;
16587 * Validates the field value
16588 * @return {Boolean} True if the value is valid, else false
16590 validate : function(){
16591 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16592 this.clearInvalid();
16598 processValue : function(value){
16603 // Subclasses should provide the validation implementation by overriding this
16604 validateValue : function(value){
16609 * Mark this field as invalid
16610 * @param {String} msg The validation message
16612 markInvalid : function(msg){
16613 if(!this.rendered || this.preventMark){ // not rendered
16617 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16619 obj.el.addClass(this.invalidClass);
16620 msg = msg || this.invalidText;
16621 switch(this.msgTarget){
16623 obj.el.dom.qtip = msg;
16624 obj.el.dom.qclass = 'x-form-invalid-tip';
16625 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16626 Roo.QuickTips.enable();
16630 this.el.dom.title = msg;
16634 var elp = this.el.findParent('.x-form-element', 5, true);
16635 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16636 this.errorEl.setWidth(elp.getWidth(true)-20);
16638 this.errorEl.update(msg);
16639 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16642 if(!this.errorIcon){
16643 var elp = this.el.findParent('.x-form-element', 5, true);
16644 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16646 this.alignErrorIcon();
16647 this.errorIcon.dom.qtip = msg;
16648 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16649 this.errorIcon.show();
16650 this.on('resize', this.alignErrorIcon, this);
16653 var t = Roo.getDom(this.msgTarget);
16655 t.style.display = this.msgDisplay;
16658 this.fireEvent('invalid', this, msg);
16662 alignErrorIcon : function(){
16663 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16667 * Clear any invalid styles/messages for this field
16669 clearInvalid : function(){
16670 if(!this.rendered || this.preventMark){ // not rendered
16673 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16675 obj.el.removeClass(this.invalidClass);
16676 switch(this.msgTarget){
16678 obj.el.dom.qtip = '';
16681 this.el.dom.title = '';
16685 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16689 if(this.errorIcon){
16690 this.errorIcon.dom.qtip = '';
16691 this.errorIcon.hide();
16692 this.un('resize', this.alignErrorIcon, this);
16696 var t = Roo.getDom(this.msgTarget);
16698 t.style.display = 'none';
16701 this.fireEvent('valid', this);
16705 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
16706 * @return {Mixed} value The field value
16708 getRawValue : function(){
16709 var v = this.el.getValue();
16715 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16716 * @return {Mixed} value The field value
16718 getValue : function(){
16719 var v = this.el.getValue();
16725 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
16726 * @param {Mixed} value The value to set
16728 setRawValue : function(v){
16729 return this.el.dom.value = (v === null || v === undefined ? '' : v);
16733 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
16734 * @param {Mixed} value The value to set
16736 setValue : function(v){
16739 this.el.dom.value = (v === null || v === undefined ? '' : v);
16744 adjustSize : function(w, h){
16745 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16746 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16750 adjustWidth : function(tag, w){
16751 tag = tag.toLowerCase();
16752 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16753 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16754 if(tag == 'input'){
16757 if(tag == 'textarea'){
16760 }else if(Roo.isOpera){
16761 if(tag == 'input'){
16764 if(tag == 'textarea'){
16774 // anything other than normal should be considered experimental
16775 Roo.form.Field.msgFx = {
16777 show: function(msgEl, f){
16778 msgEl.setDisplayed('block');
16781 hide : function(msgEl, f){
16782 msgEl.setDisplayed(false).update('');
16787 show: function(msgEl, f){
16788 msgEl.slideIn('t', {stopFx:true});
16791 hide : function(msgEl, f){
16792 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16797 show: function(msgEl, f){
16798 msgEl.fixDisplay();
16799 msgEl.alignTo(f.el, 'tl-tr');
16800 msgEl.slideIn('l', {stopFx:true});
16803 hide : function(msgEl, f){
16804 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16809 * Ext JS Library 1.1.1
16810 * Copyright(c) 2006-2007, Ext JS, LLC.
16812 * Originally Released Under LGPL - original licence link has changed is not relivant.
16815 * <script type="text/javascript">
16820 * @class Roo.form.TextField
16821 * @extends Roo.form.Field
16822 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
16823 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16825 * Creates a new TextField
16826 * @param {Object} config Configuration options
16828 Roo.form.TextField = function(config){
16829 Roo.form.TextField.superclass.constructor.call(this, config);
16833 * Fires when the autosize function is triggered. The field may or may not have actually changed size
16834 * according to the default logic, but this event provides a hook for the developer to apply additional
16835 * logic at runtime to resize the field if needed.
16836 * @param {Roo.form.Field} this This text field
16837 * @param {Number} width The new field width
16843 Roo.extend(Roo.form.TextField, Roo.form.Field, {
16845 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16849 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16853 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16857 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16861 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16865 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16867 disableKeyFilter : false,
16869 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16873 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16877 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16879 maxLength : Number.MAX_VALUE,
16881 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16883 minLengthText : "The minimum length for this field is {0}",
16885 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16887 maxLengthText : "The maximum length for this field is {0}",
16889 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16891 selectOnFocus : false,
16893 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16895 blankText : "This field is required",
16897 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16898 * If available, this function will be called only after the basic validators all return true, and will be passed the
16899 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16903 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16904 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16905 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
16909 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16913 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16919 initEvents : function()
16921 if (this.emptyText) {
16922 this.el.attr('placeholder', this.emptyText);
16925 Roo.form.TextField.superclass.initEvents.call(this);
16926 if(this.validationEvent == 'keyup'){
16927 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16928 this.el.on('keyup', this.filterValidation, this);
16930 else if(this.validationEvent !== false){
16931 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16934 if(this.selectOnFocus){
16935 this.on("focus", this.preFocus, this);
16938 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16939 this.el.on("keypress", this.filterKeys, this);
16942 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
16943 this.el.on("click", this.autoSize, this);
16945 if(this.el.is('input[type=password]') && Roo.isSafari){
16946 this.el.on('keydown', this.SafariOnKeyDown, this);
16950 processValue : function(value){
16951 if(this.stripCharsRe){
16952 var newValue = value.replace(this.stripCharsRe, '');
16953 if(newValue !== value){
16954 this.setRawValue(newValue);
16961 filterValidation : function(e){
16962 if(!e.isNavKeyPress()){
16963 this.validationTask.delay(this.validationDelay);
16968 onKeyUp : function(e){
16969 if(!e.isNavKeyPress()){
16975 * Resets the current field value to the originally-loaded value and clears any validation messages.
16978 reset : function(){
16979 Roo.form.TextField.superclass.reset.call(this);
16985 preFocus : function(){
16987 if(this.selectOnFocus){
16988 this.el.dom.select();
16994 filterKeys : function(e){
16995 var k = e.getKey();
16996 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16999 var c = e.getCharCode(), cc = String.fromCharCode(c);
17000 if(Roo.isIE && (e.isSpecialKey() || !cc)){
17003 if(!this.maskRe.test(cc)){
17008 setValue : function(v){
17010 Roo.form.TextField.superclass.setValue.apply(this, arguments);
17016 * Validates a value according to the field's validation rules and marks the field as invalid
17017 * if the validation fails
17018 * @param {Mixed} value The value to validate
17019 * @return {Boolean} True if the value is valid, else false
17021 validateValue : function(value){
17022 if(value.length < 1) { // if it's blank
17023 if(this.allowBlank){
17024 this.clearInvalid();
17027 this.markInvalid(this.blankText);
17031 if(value.length < this.minLength){
17032 this.markInvalid(String.format(this.minLengthText, this.minLength));
17035 if(value.length > this.maxLength){
17036 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17040 var vt = Roo.form.VTypes;
17041 if(!vt[this.vtype](value, this)){
17042 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17046 if(typeof this.validator == "function"){
17047 var msg = this.validator(value);
17049 this.markInvalid(msg);
17053 if(this.regex && !this.regex.test(value)){
17054 this.markInvalid(this.regexText);
17061 * Selects text in this field
17062 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17063 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17065 selectText : function(start, end){
17066 var v = this.getRawValue();
17068 start = start === undefined ? 0 : start;
17069 end = end === undefined ? v.length : end;
17070 var d = this.el.dom;
17071 if(d.setSelectionRange){
17072 d.setSelectionRange(start, end);
17073 }else if(d.createTextRange){
17074 var range = d.createTextRange();
17075 range.moveStart("character", start);
17076 range.moveEnd("character", v.length-end);
17083 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17084 * This only takes effect if grow = true, and fires the autosize event.
17086 autoSize : function(){
17087 if(!this.grow || !this.rendered){
17091 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17094 var v = el.dom.value;
17095 var d = document.createElement('div');
17096 d.appendChild(document.createTextNode(v));
17100 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17101 this.el.setWidth(w);
17102 this.fireEvent("autosize", this, w);
17106 SafariOnKeyDown : function(event)
17108 // this is a workaround for a password hang bug on chrome/ webkit.
17110 var isSelectAll = false;
17112 if(this.el.dom.selectionEnd > 0){
17113 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17115 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17116 event.preventDefault();
17121 if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17123 event.preventDefault();
17124 // this is very hacky as keydown always get's upper case.
17126 var cc = String.fromCharCode(event.getCharCode());
17129 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
17137 * Ext JS Library 1.1.1
17138 * Copyright(c) 2006-2007, Ext JS, LLC.
17140 * Originally Released Under LGPL - original licence link has changed is not relivant.
17143 * <script type="text/javascript">
17147 * @class Roo.form.Hidden
17148 * @extends Roo.form.TextField
17149 * Simple Hidden element used on forms
17151 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17154 * Creates a new Hidden form element.
17155 * @param {Object} config Configuration options
17160 // easy hidden field...
17161 Roo.form.Hidden = function(config){
17162 Roo.form.Hidden.superclass.constructor.call(this, config);
17165 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17167 inputType: 'hidden',
17170 labelSeparator: '',
17172 itemCls : 'x-form-item-display-none'
17180 * Ext JS Library 1.1.1
17181 * Copyright(c) 2006-2007, Ext JS, LLC.
17183 * Originally Released Under LGPL - original licence link has changed is not relivant.
17186 * <script type="text/javascript">
17190 * @class Roo.form.TriggerField
17191 * @extends Roo.form.TextField
17192 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17193 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17194 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17195 * for which you can provide a custom implementation. For example:
17197 var trigger = new Roo.form.TriggerField();
17198 trigger.onTriggerClick = myTriggerFn;
17199 trigger.applyTo('my-field');
17202 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17203 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17204 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
17205 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17207 * Create a new TriggerField.
17208 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17209 * to the base TextField)
17211 Roo.form.TriggerField = function(config){
17212 this.mimicing = false;
17213 Roo.form.TriggerField.superclass.constructor.call(this, config);
17216 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
17218 * @cfg {String} triggerClass A CSS class to apply to the trigger
17221 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17222 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17224 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17226 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17230 /** @cfg {Boolean} grow @hide */
17231 /** @cfg {Number} growMin @hide */
17232 /** @cfg {Number} growMax @hide */
17238 autoSize: Roo.emptyFn,
17242 deferHeight : true,
17245 actionMode : 'wrap',
17247 onResize : function(w, h){
17248 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17249 if(typeof w == 'number'){
17250 var x = w - this.trigger.getWidth();
17251 this.el.setWidth(this.adjustWidth('input', x));
17252 this.trigger.setStyle('left', x+'px');
17257 adjustSize : Roo.BoxComponent.prototype.adjustSize,
17260 getResizeEl : function(){
17265 getPositionEl : function(){
17270 alignErrorIcon : function(){
17271 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17275 onRender : function(ct, position){
17276 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17277 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17278 this.trigger = this.wrap.createChild(this.triggerConfig ||
17279 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17280 if(this.hideTrigger){
17281 this.trigger.setDisplayed(false);
17283 this.initTrigger();
17285 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17290 initTrigger : function(){
17291 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17292 this.trigger.addClassOnOver('x-form-trigger-over');
17293 this.trigger.addClassOnClick('x-form-trigger-click');
17297 onDestroy : function(){
17299 this.trigger.removeAllListeners();
17300 this.trigger.remove();
17303 this.wrap.remove();
17305 Roo.form.TriggerField.superclass.onDestroy.call(this);
17309 onFocus : function(){
17310 Roo.form.TriggerField.superclass.onFocus.call(this);
17311 if(!this.mimicing){
17312 this.wrap.addClass('x-trigger-wrap-focus');
17313 this.mimicing = true;
17314 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17315 if(this.monitorTab){
17316 this.el.on("keydown", this.checkTab, this);
17322 checkTab : function(e){
17323 if(e.getKey() == e.TAB){
17324 this.triggerBlur();
17329 onBlur : function(){
17334 mimicBlur : function(e, t){
17335 if(!this.wrap.contains(t) && this.validateBlur()){
17336 this.triggerBlur();
17341 triggerBlur : function(){
17342 this.mimicing = false;
17343 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17344 if(this.monitorTab){
17345 this.el.un("keydown", this.checkTab, this);
17347 this.wrap.removeClass('x-trigger-wrap-focus');
17348 Roo.form.TriggerField.superclass.onBlur.call(this);
17352 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17353 validateBlur : function(e, t){
17358 onDisable : function(){
17359 Roo.form.TriggerField.superclass.onDisable.call(this);
17361 this.wrap.addClass('x-item-disabled');
17366 onEnable : function(){
17367 Roo.form.TriggerField.superclass.onEnable.call(this);
17369 this.wrap.removeClass('x-item-disabled');
17374 onShow : function(){
17375 var ae = this.getActionEl();
17378 ae.dom.style.display = '';
17379 ae.dom.style.visibility = 'visible';
17385 onHide : function(){
17386 var ae = this.getActionEl();
17387 ae.dom.style.display = 'none';
17391 * The function that should handle the trigger's click event. This method does nothing by default until overridden
17392 * by an implementing function.
17394 * @param {EventObject} e
17396 onTriggerClick : Roo.emptyFn
17399 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
17400 // to be extended by an implementing class. For an example of implementing this class, see the custom
17401 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17402 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17403 initComponent : function(){
17404 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17406 this.triggerConfig = {
17407 tag:'span', cls:'x-form-twin-triggers', cn:[
17408 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17409 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17413 getTrigger : function(index){
17414 return this.triggers[index];
17417 initTrigger : function(){
17418 var ts = this.trigger.select('.x-form-trigger', true);
17419 this.wrap.setStyle('overflow', 'hidden');
17420 var triggerField = this;
17421 ts.each(function(t, all, index){
17422 t.hide = function(){
17423 var w = triggerField.wrap.getWidth();
17424 this.dom.style.display = 'none';
17425 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17427 t.show = function(){
17428 var w = triggerField.wrap.getWidth();
17429 this.dom.style.display = '';
17430 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17432 var triggerIndex = 'Trigger'+(index+1);
17434 if(this['hide'+triggerIndex]){
17435 t.dom.style.display = 'none';
17437 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17438 t.addClassOnOver('x-form-trigger-over');
17439 t.addClassOnClick('x-form-trigger-click');
17441 this.triggers = ts.elements;
17444 onTrigger1Click : Roo.emptyFn,
17445 onTrigger2Click : Roo.emptyFn
17448 * Ext JS Library 1.1.1
17449 * Copyright(c) 2006-2007, Ext JS, LLC.
17451 * Originally Released Under LGPL - original licence link has changed is not relivant.
17454 * <script type="text/javascript">
17458 * @class Roo.form.TextArea
17459 * @extends Roo.form.TextField
17460 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
17461 * support for auto-sizing.
17463 * Creates a new TextArea
17464 * @param {Object} config Configuration options
17466 Roo.form.TextArea = function(config){
17467 Roo.form.TextArea.superclass.constructor.call(this, config);
17468 // these are provided exchanges for backwards compat
17469 // minHeight/maxHeight were replaced by growMin/growMax to be
17470 // compatible with TextField growing config values
17471 if(this.minHeight !== undefined){
17472 this.growMin = this.minHeight;
17474 if(this.maxHeight !== undefined){
17475 this.growMax = this.maxHeight;
17479 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
17481 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17485 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17489 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17490 * in the field (equivalent to setting overflow: hidden, defaults to false)
17492 preventScrollbars: false,
17494 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17495 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17499 onRender : function(ct, position){
17501 this.defaultAutoCreate = {
17503 style:"width:300px;height:60px;",
17504 autocomplete: "new-password"
17507 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17509 this.textSizeEl = Roo.DomHelper.append(document.body, {
17510 tag: "pre", cls: "x-form-grow-sizer"
17512 if(this.preventScrollbars){
17513 this.el.setStyle("overflow", "hidden");
17515 this.el.setHeight(this.growMin);
17519 onDestroy : function(){
17520 if(this.textSizeEl){
17521 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17523 Roo.form.TextArea.superclass.onDestroy.call(this);
17527 onKeyUp : function(e){
17528 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17534 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17535 * This only takes effect if grow = true, and fires the autosize event if the height changes.
17537 autoSize : function(){
17538 if(!this.grow || !this.textSizeEl){
17542 var v = el.dom.value;
17543 var ts = this.textSizeEl;
17546 ts.appendChild(document.createTextNode(v));
17549 Roo.fly(ts).setWidth(this.el.getWidth());
17551 v = "  ";
17554 v = v.replace(/\n/g, '<p> </p>');
17556 v += " \n ";
17559 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17560 if(h != this.lastHeight){
17561 this.lastHeight = h;
17562 this.el.setHeight(h);
17563 this.fireEvent("autosize", this, h);
17568 * Ext JS Library 1.1.1
17569 * Copyright(c) 2006-2007, Ext JS, LLC.
17571 * Originally Released Under LGPL - original licence link has changed is not relivant.
17574 * <script type="text/javascript">
17579 * @class Roo.form.NumberField
17580 * @extends Roo.form.TextField
17581 * Numeric text field that provides automatic keystroke filtering and numeric validation.
17583 * Creates a new NumberField
17584 * @param {Object} config Configuration options
17586 Roo.form.NumberField = function(config){
17587 Roo.form.NumberField.superclass.constructor.call(this, config);
17590 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
17592 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17594 fieldClass: "x-form-field x-form-num-field",
17596 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17598 allowDecimals : true,
17600 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17602 decimalSeparator : ".",
17604 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17606 decimalPrecision : 2,
17608 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17610 allowNegative : true,
17612 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17614 minValue : Number.NEGATIVE_INFINITY,
17616 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17618 maxValue : Number.MAX_VALUE,
17620 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17622 minText : "The minimum value for this field is {0}",
17624 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17626 maxText : "The maximum value for this field is {0}",
17628 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
17629 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17631 nanText : "{0} is not a valid number",
17634 initEvents : function(){
17635 Roo.form.NumberField.superclass.initEvents.call(this);
17636 var allowed = "0123456789";
17637 if(this.allowDecimals){
17638 allowed += this.decimalSeparator;
17640 if(this.allowNegative){
17643 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17644 var keyPress = function(e){
17645 var k = e.getKey();
17646 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17649 var c = e.getCharCode();
17650 if(allowed.indexOf(String.fromCharCode(c)) === -1){
17654 this.el.on("keypress", keyPress, this);
17658 validateValue : function(value){
17659 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17662 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17665 var num = this.parseValue(value);
17667 this.markInvalid(String.format(this.nanText, value));
17670 if(num < this.minValue){
17671 this.markInvalid(String.format(this.minText, this.minValue));
17674 if(num > this.maxValue){
17675 this.markInvalid(String.format(this.maxText, this.maxValue));
17681 getValue : function(){
17682 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17686 parseValue : function(value){
17687 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17688 return isNaN(value) ? '' : value;
17692 fixPrecision : function(value){
17693 var nan = isNaN(value);
17694 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17695 return nan ? '' : value;
17697 return parseFloat(value).toFixed(this.decimalPrecision);
17700 setValue : function(v){
17701 v = this.fixPrecision(v);
17702 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17706 decimalPrecisionFcn : function(v){
17707 return Math.floor(v);
17710 beforeBlur : function(){
17711 var v = this.parseValue(this.getRawValue());
17718 * Ext JS Library 1.1.1
17719 * Copyright(c) 2006-2007, Ext JS, LLC.
17721 * Originally Released Under LGPL - original licence link has changed is not relivant.
17724 * <script type="text/javascript">
17728 * @class Roo.form.DateField
17729 * @extends Roo.form.TriggerField
17730 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17732 * Create a new DateField
17733 * @param {Object} config
17735 Roo.form.DateField = function(config){
17736 Roo.form.DateField.superclass.constructor.call(this, config);
17742 * Fires when a date is selected
17743 * @param {Roo.form.DateField} combo This combo box
17744 * @param {Date} date The date selected
17751 if(typeof this.minValue == "string") {
17752 this.minValue = this.parseDate(this.minValue);
17754 if(typeof this.maxValue == "string") {
17755 this.maxValue = this.parseDate(this.maxValue);
17757 this.ddMatch = null;
17758 if(this.disabledDates){
17759 var dd = this.disabledDates;
17761 for(var i = 0; i < dd.length; i++){
17763 if(i != dd.length-1) {
17767 this.ddMatch = new RegExp(re + ")");
17771 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
17773 * @cfg {String} format
17774 * The default date format string which can be overriden for localization support. The format must be
17775 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17779 * @cfg {String} altFormats
17780 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17781 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17783 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17785 * @cfg {Array} disabledDays
17786 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17788 disabledDays : null,
17790 * @cfg {String} disabledDaysText
17791 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17793 disabledDaysText : "Disabled",
17795 * @cfg {Array} disabledDates
17796 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17797 * expression so they are very powerful. Some examples:
17799 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17800 * <li>["03/08", "09/16"] would disable those days for every year</li>
17801 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17802 * <li>["03/../2006"] would disable every day in March 2006</li>
17803 * <li>["^03"] would disable every day in every March</li>
17805 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17806 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17808 disabledDates : null,
17810 * @cfg {String} disabledDatesText
17811 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17813 disabledDatesText : "Disabled",
17815 * @cfg {Date/String} minValue
17816 * The minimum allowed date. Can be either a Javascript date object or a string date in a
17817 * valid format (defaults to null).
17821 * @cfg {Date/String} maxValue
17822 * The maximum allowed date. Can be either a Javascript date object or a string date in a
17823 * valid format (defaults to null).
17827 * @cfg {String} minText
17828 * The error text to display when the date in the cell is before minValue (defaults to
17829 * 'The date in this field must be after {minValue}').
17831 minText : "The date in this field must be equal to or after {0}",
17833 * @cfg {String} maxText
17834 * The error text to display when the date in the cell is after maxValue (defaults to
17835 * 'The date in this field must be before {maxValue}').
17837 maxText : "The date in this field must be equal to or before {0}",
17839 * @cfg {String} invalidText
17840 * The error text to display when the date in the field is invalid (defaults to
17841 * '{value} is not a valid date - it must be in the format {format}').
17843 invalidText : "{0} is not a valid date - it must be in the format {1}",
17845 * @cfg {String} triggerClass
17846 * An additional CSS class used to style the trigger button. The trigger will always get the
17847 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17848 * which displays a calendar icon).
17850 triggerClass : 'x-form-date-trigger',
17854 * @cfg {Boolean} useIso
17855 * if enabled, then the date field will use a hidden field to store the
17856 * real value as iso formated date. default (false)
17860 * @cfg {String/Object} autoCreate
17861 * A DomHelper element spec, or true for a default element spec (defaults to
17862 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17865 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17868 hiddenField: false,
17870 onRender : function(ct, position)
17872 Roo.form.DateField.superclass.onRender.call(this, ct, position);
17874 //this.el.dom.removeAttribute('name');
17875 Roo.log("Changing name?");
17876 this.el.dom.setAttribute('name', this.name + '____hidden___' );
17877 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17879 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17880 // prevent input submission
17881 this.hiddenName = this.name;
17888 validateValue : function(value)
17890 value = this.formatDate(value);
17891 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17892 Roo.log('super failed');
17895 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17898 var svalue = value;
17899 value = this.parseDate(value);
17901 Roo.log('parse date failed' + svalue);
17902 this.markInvalid(String.format(this.invalidText, svalue, this.format));
17905 var time = value.getTime();
17906 if(this.minValue && time < this.minValue.getTime()){
17907 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17910 if(this.maxValue && time > this.maxValue.getTime()){
17911 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17914 if(this.disabledDays){
17915 var day = value.getDay();
17916 for(var i = 0; i < this.disabledDays.length; i++) {
17917 if(day === this.disabledDays[i]){
17918 this.markInvalid(this.disabledDaysText);
17923 var fvalue = this.formatDate(value);
17924 if(this.ddMatch && this.ddMatch.test(fvalue)){
17925 this.markInvalid(String.format(this.disabledDatesText, fvalue));
17932 // Provides logic to override the default TriggerField.validateBlur which just returns true
17933 validateBlur : function(){
17934 return !this.menu || !this.menu.isVisible();
17937 getName: function()
17939 // returns hidden if it's set..
17940 if (!this.rendered) {return ''};
17941 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
17946 * Returns the current date value of the date field.
17947 * @return {Date} The date value
17949 getValue : function(){
17951 return this.hiddenField ?
17952 this.hiddenField.value :
17953 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17957 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
17958 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17959 * (the default format used is "m/d/y").
17962 //All of these calls set the same date value (May 4, 2006)
17964 //Pass a date object:
17965 var dt = new Date('5/4/06');
17966 dateField.setValue(dt);
17968 //Pass a date string (default format):
17969 dateField.setValue('5/4/06');
17971 //Pass a date string (custom format):
17972 dateField.format = 'Y-m-d';
17973 dateField.setValue('2006-5-4');
17975 * @param {String/Date} date The date or valid date string
17977 setValue : function(date){
17978 if (this.hiddenField) {
17979 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17981 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17982 // make sure the value field is always stored as a date..
17983 this.value = this.parseDate(date);
17989 parseDate : function(value){
17990 if(!value || value instanceof Date){
17993 var v = Date.parseDate(value, this.format);
17994 if (!v && this.useIso) {
17995 v = Date.parseDate(value, 'Y-m-d');
17997 if(!v && this.altFormats){
17998 if(!this.altFormatsArray){
17999 this.altFormatsArray = this.altFormats.split("|");
18001 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18002 v = Date.parseDate(value, this.altFormatsArray[i]);
18009 formatDate : function(date, fmt){
18010 return (!date || !(date instanceof Date)) ?
18011 date : date.dateFormat(fmt || this.format);
18016 select: function(m, d){
18019 this.fireEvent('select', this, d);
18021 show : function(){ // retain focus styling
18025 this.focus.defer(10, this);
18026 var ml = this.menuListeners;
18027 this.menu.un("select", ml.select, this);
18028 this.menu.un("show", ml.show, this);
18029 this.menu.un("hide", ml.hide, this);
18034 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18035 onTriggerClick : function(){
18039 if(this.menu == null){
18040 this.menu = new Roo.menu.DateMenu();
18042 Roo.apply(this.menu.picker, {
18043 showClear: this.allowBlank,
18044 minDate : this.minValue,
18045 maxDate : this.maxValue,
18046 disabledDatesRE : this.ddMatch,
18047 disabledDatesText : this.disabledDatesText,
18048 disabledDays : this.disabledDays,
18049 disabledDaysText : this.disabledDaysText,
18050 format : this.useIso ? 'Y-m-d' : this.format,
18051 minText : String.format(this.minText, this.formatDate(this.minValue)),
18052 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18054 this.menu.on(Roo.apply({}, this.menuListeners, {
18057 this.menu.picker.setValue(this.getValue() || new Date());
18058 this.menu.show(this.el, "tl-bl?");
18061 beforeBlur : function(){
18062 var v = this.parseDate(this.getRawValue());
18072 isDirty : function() {
18073 if(this.disabled) {
18077 if(typeof(this.startValue) === 'undefined'){
18081 return String(this.getValue()) !== String(this.startValue);
18086 * Ext JS Library 1.1.1
18087 * Copyright(c) 2006-2007, Ext JS, LLC.
18089 * Originally Released Under LGPL - original licence link has changed is not relivant.
18092 * <script type="text/javascript">
18096 * @class Roo.form.MonthField
18097 * @extends Roo.form.TriggerField
18098 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18100 * Create a new MonthField
18101 * @param {Object} config
18103 Roo.form.MonthField = function(config){
18105 Roo.form.MonthField.superclass.constructor.call(this, config);
18111 * Fires when a date is selected
18112 * @param {Roo.form.MonthFieeld} combo This combo box
18113 * @param {Date} date The date selected
18120 if(typeof this.minValue == "string") {
18121 this.minValue = this.parseDate(this.minValue);
18123 if(typeof this.maxValue == "string") {
18124 this.maxValue = this.parseDate(this.maxValue);
18126 this.ddMatch = null;
18127 if(this.disabledDates){
18128 var dd = this.disabledDates;
18130 for(var i = 0; i < dd.length; i++){
18132 if(i != dd.length-1) {
18136 this.ddMatch = new RegExp(re + ")");
18140 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
18142 * @cfg {String} format
18143 * The default date format string which can be overriden for localization support. The format must be
18144 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18148 * @cfg {String} altFormats
18149 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18150 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18152 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18154 * @cfg {Array} disabledDays
18155 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18157 disabledDays : [0,1,2,3,4,5,6],
18159 * @cfg {String} disabledDaysText
18160 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18162 disabledDaysText : "Disabled",
18164 * @cfg {Array} disabledDates
18165 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18166 * expression so they are very powerful. Some examples:
18168 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18169 * <li>["03/08", "09/16"] would disable those days for every year</li>
18170 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18171 * <li>["03/../2006"] would disable every day in March 2006</li>
18172 * <li>["^03"] would disable every day in every March</li>
18174 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18175 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18177 disabledDates : null,
18179 * @cfg {String} disabledDatesText
18180 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18182 disabledDatesText : "Disabled",
18184 * @cfg {Date/String} minValue
18185 * The minimum allowed date. Can be either a Javascript date object or a string date in a
18186 * valid format (defaults to null).
18190 * @cfg {Date/String} maxValue
18191 * The maximum allowed date. Can be either a Javascript date object or a string date in a
18192 * valid format (defaults to null).
18196 * @cfg {String} minText
18197 * The error text to display when the date in the cell is before minValue (defaults to
18198 * 'The date in this field must be after {minValue}').
18200 minText : "The date in this field must be equal to or after {0}",
18202 * @cfg {String} maxTextf
18203 * The error text to display when the date in the cell is after maxValue (defaults to
18204 * 'The date in this field must be before {maxValue}').
18206 maxText : "The date in this field must be equal to or before {0}",
18208 * @cfg {String} invalidText
18209 * The error text to display when the date in the field is invalid (defaults to
18210 * '{value} is not a valid date - it must be in the format {format}').
18212 invalidText : "{0} is not a valid date - it must be in the format {1}",
18214 * @cfg {String} triggerClass
18215 * An additional CSS class used to style the trigger button. The trigger will always get the
18216 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18217 * which displays a calendar icon).
18219 triggerClass : 'x-form-date-trigger',
18223 * @cfg {Boolean} useIso
18224 * if enabled, then the date field will use a hidden field to store the
18225 * real value as iso formated date. default (true)
18229 * @cfg {String/Object} autoCreate
18230 * A DomHelper element spec, or true for a default element spec (defaults to
18231 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18234 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18237 hiddenField: false,
18239 hideMonthPicker : false,
18241 onRender : function(ct, position)
18243 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18245 this.el.dom.removeAttribute('name');
18246 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18248 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18249 // prevent input submission
18250 this.hiddenName = this.name;
18257 validateValue : function(value)
18259 value = this.formatDate(value);
18260 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18263 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18266 var svalue = value;
18267 value = this.parseDate(value);
18269 this.markInvalid(String.format(this.invalidText, svalue, this.format));
18272 var time = value.getTime();
18273 if(this.minValue && time < this.minValue.getTime()){
18274 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18277 if(this.maxValue && time > this.maxValue.getTime()){
18278 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18281 /*if(this.disabledDays){
18282 var day = value.getDay();
18283 for(var i = 0; i < this.disabledDays.length; i++) {
18284 if(day === this.disabledDays[i]){
18285 this.markInvalid(this.disabledDaysText);
18291 var fvalue = this.formatDate(value);
18292 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18293 this.markInvalid(String.format(this.disabledDatesText, fvalue));
18301 // Provides logic to override the default TriggerField.validateBlur which just returns true
18302 validateBlur : function(){
18303 return !this.menu || !this.menu.isVisible();
18307 * Returns the current date value of the date field.
18308 * @return {Date} The date value
18310 getValue : function(){
18314 return this.hiddenField ?
18315 this.hiddenField.value :
18316 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18320 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
18321 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18322 * (the default format used is "m/d/y").
18325 //All of these calls set the same date value (May 4, 2006)
18327 //Pass a date object:
18328 var dt = new Date('5/4/06');
18329 monthField.setValue(dt);
18331 //Pass a date string (default format):
18332 monthField.setValue('5/4/06');
18334 //Pass a date string (custom format):
18335 monthField.format = 'Y-m-d';
18336 monthField.setValue('2006-5-4');
18338 * @param {String/Date} date The date or valid date string
18340 setValue : function(date){
18341 Roo.log('month setValue' + date);
18342 // can only be first of month..
18344 var val = this.parseDate(date);
18346 if (this.hiddenField) {
18347 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18349 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18350 this.value = this.parseDate(date);
18354 parseDate : function(value){
18355 if(!value || value instanceof Date){
18356 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18359 var v = Date.parseDate(value, this.format);
18360 if (!v && this.useIso) {
18361 v = Date.parseDate(value, 'Y-m-d');
18365 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18369 if(!v && this.altFormats){
18370 if(!this.altFormatsArray){
18371 this.altFormatsArray = this.altFormats.split("|");
18373 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18374 v = Date.parseDate(value, this.altFormatsArray[i]);
18381 formatDate : function(date, fmt){
18382 return (!date || !(date instanceof Date)) ?
18383 date : date.dateFormat(fmt || this.format);
18388 select: function(m, d){
18390 this.fireEvent('select', this, d);
18392 show : function(){ // retain focus styling
18396 this.focus.defer(10, this);
18397 var ml = this.menuListeners;
18398 this.menu.un("select", ml.select, this);
18399 this.menu.un("show", ml.show, this);
18400 this.menu.un("hide", ml.hide, this);
18404 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18405 onTriggerClick : function(){
18409 if(this.menu == null){
18410 this.menu = new Roo.menu.DateMenu();
18414 Roo.apply(this.menu.picker, {
18416 showClear: this.allowBlank,
18417 minDate : this.minValue,
18418 maxDate : this.maxValue,
18419 disabledDatesRE : this.ddMatch,
18420 disabledDatesText : this.disabledDatesText,
18422 format : this.useIso ? 'Y-m-d' : this.format,
18423 minText : String.format(this.minText, this.formatDate(this.minValue)),
18424 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18427 this.menu.on(Roo.apply({}, this.menuListeners, {
18435 // hide month picker get's called when we called by 'before hide';
18437 var ignorehide = true;
18438 p.hideMonthPicker = function(disableAnim){
18442 if(this.monthPicker){
18443 Roo.log("hideMonthPicker called");
18444 if(disableAnim === true){
18445 this.monthPicker.hide();
18447 this.monthPicker.slideOut('t', {duration:.2});
18448 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18449 p.fireEvent("select", this, this.value);
18455 Roo.log('picker set value');
18456 Roo.log(this.getValue());
18457 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18458 m.show(this.el, 'tl-bl?');
18459 ignorehide = false;
18460 // this will trigger hideMonthPicker..
18463 // hidden the day picker
18464 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18470 p.showMonthPicker.defer(100, p);
18476 beforeBlur : function(){
18477 var v = this.parseDate(this.getRawValue());
18483 /** @cfg {Boolean} grow @hide */
18484 /** @cfg {Number} growMin @hide */
18485 /** @cfg {Number} growMax @hide */
18492 * Ext JS Library 1.1.1
18493 * Copyright(c) 2006-2007, Ext JS, LLC.
18495 * Originally Released Under LGPL - original licence link has changed is not relivant.
18498 * <script type="text/javascript">
18503 * @class Roo.form.ComboBox
18504 * @extends Roo.form.TriggerField
18505 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18507 * Create a new ComboBox.
18508 * @param {Object} config Configuration options
18510 Roo.form.ComboBox = function(config){
18511 Roo.form.ComboBox.superclass.constructor.call(this, config);
18515 * Fires when the dropdown list is expanded
18516 * @param {Roo.form.ComboBox} combo This combo box
18521 * Fires when the dropdown list is collapsed
18522 * @param {Roo.form.ComboBox} combo This combo box
18526 * @event beforeselect
18527 * Fires before a list item is selected. Return false to cancel the selection.
18528 * @param {Roo.form.ComboBox} combo This combo box
18529 * @param {Roo.data.Record} record The data record returned from the underlying store
18530 * @param {Number} index The index of the selected item in the dropdown list
18532 'beforeselect' : true,
18535 * Fires when a list item is selected
18536 * @param {Roo.form.ComboBox} combo This combo box
18537 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18538 * @param {Number} index The index of the selected item in the dropdown list
18542 * @event beforequery
18543 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18544 * The event object passed has these properties:
18545 * @param {Roo.form.ComboBox} combo This combo box
18546 * @param {String} query The query
18547 * @param {Boolean} forceAll true to force "all" query
18548 * @param {Boolean} cancel true to cancel the query
18549 * @param {Object} e The query event object
18551 'beforequery': true,
18554 * Fires when the 'add' icon is pressed (add a listener to enable add button)
18555 * @param {Roo.form.ComboBox} combo This combo box
18560 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18561 * @param {Roo.form.ComboBox} combo This combo box
18562 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18568 if(this.transform){
18569 this.allowDomMove = false;
18570 var s = Roo.getDom(this.transform);
18571 if(!this.hiddenName){
18572 this.hiddenName = s.name;
18575 this.mode = 'local';
18576 var d = [], opts = s.options;
18577 for(var i = 0, len = opts.length;i < len; i++){
18579 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18581 this.value = value;
18583 d.push([value, o.text]);
18585 this.store = new Roo.data.SimpleStore({
18587 fields: ['value', 'text'],
18590 this.valueField = 'value';
18591 this.displayField = 'text';
18593 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18594 if(!this.lazyRender){
18595 this.target = true;
18596 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18597 s.parentNode.removeChild(s); // remove it
18598 this.render(this.el.parentNode);
18600 s.parentNode.removeChild(s); // remove it
18605 this.store = Roo.factory(this.store, Roo.data);
18608 this.selectedIndex = -1;
18609 if(this.mode == 'local'){
18610 if(config.queryDelay === undefined){
18611 this.queryDelay = 10;
18613 if(config.minChars === undefined){
18619 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18621 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18624 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18625 * rendering into an Roo.Editor, defaults to false)
18628 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18629 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18632 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18635 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18636 * the dropdown list (defaults to undefined, with no header element)
18640 * @cfg {String/Roo.Template} tpl The template to use to render the output
18644 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18646 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18648 listWidth: undefined,
18650 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18651 * mode = 'remote' or 'text' if mode = 'local')
18653 displayField: undefined,
18655 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18656 * mode = 'remote' or 'value' if mode = 'local').
18657 * Note: use of a valueField requires the user make a selection
18658 * in order for a value to be mapped.
18660 valueField: undefined,
18664 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18665 * field's data value (defaults to the underlying DOM element's name)
18667 hiddenName: undefined,
18669 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18673 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18675 selectedClass: 'x-combo-selected',
18677 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
18678 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18679 * which displays a downward arrow icon).
18681 triggerClass : 'x-form-arrow-trigger',
18683 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18687 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18688 * anchor positions (defaults to 'tl-bl')
18690 listAlign: 'tl-bl?',
18692 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18696 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
18697 * query specified by the allQuery config option (defaults to 'query')
18699 triggerAction: 'query',
18701 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18702 * (defaults to 4, does not apply if editable = false)
18706 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18707 * delay (typeAheadDelay) if it matches a known value (defaults to false)
18711 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18712 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18716 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18717 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
18721 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
18722 * when editable = true (defaults to false)
18724 selectOnFocus:false,
18726 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18728 queryParam: 'query',
18730 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
18731 * when mode = 'remote' (defaults to 'Loading...')
18733 loadingText: 'Loading...',
18735 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18739 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18743 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18744 * traditional select (defaults to true)
18748 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18752 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18756 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18757 * listWidth has a higher value)
18761 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18762 * allow the user to set arbitrary text into the field (defaults to false)
18764 forceSelection:false,
18766 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18767 * if typeAhead = true (defaults to 250)
18769 typeAheadDelay : 250,
18771 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18772 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18774 valueNotFoundText : undefined,
18776 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18778 blockFocus : false,
18781 * @cfg {Boolean} disableClear Disable showing of clear button.
18783 disableClear : false,
18785 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
18787 alwaysQuery : false,
18793 // element that contains real text value.. (when hidden is used..)
18796 onRender : function(ct, position){
18797 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18798 if(this.hiddenName){
18799 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
18801 this.hiddenField.value =
18802 this.hiddenValue !== undefined ? this.hiddenValue :
18803 this.value !== undefined ? this.value : '';
18805 // prevent input submission
18806 this.el.dom.removeAttribute('name');
18811 this.el.dom.setAttribute('autocomplete', 'off');
18814 var cls = 'x-combo-list';
18816 this.list = new Roo.Layer({
18817 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18820 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18821 this.list.setWidth(lw);
18822 this.list.swallowEvent('mousewheel');
18823 this.assetHeight = 0;
18826 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18827 this.assetHeight += this.header.getHeight();
18830 this.innerList = this.list.createChild({cls:cls+'-inner'});
18831 this.innerList.on('mouseover', this.onViewOver, this);
18832 this.innerList.on('mousemove', this.onViewMove, this);
18833 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18835 if(this.allowBlank && !this.pageSize && !this.disableClear){
18836 this.footer = this.list.createChild({cls:cls+'-ft'});
18837 this.pageTb = new Roo.Toolbar(this.footer);
18841 this.footer = this.list.createChild({cls:cls+'-ft'});
18842 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18843 {pageSize: this.pageSize});
18847 if (this.pageTb && this.allowBlank && !this.disableClear) {
18849 this.pageTb.add(new Roo.Toolbar.Fill(), {
18850 cls: 'x-btn-icon x-btn-clear',
18852 handler: function()
18855 _this.clearValue();
18856 _this.onSelect(false, -1);
18861 this.assetHeight += this.footer.getHeight();
18866 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18869 this.view = new Roo.View(this.innerList, this.tpl, {
18870 singleSelect:true, store: this.store, selectedClass: this.selectedClass
18873 this.view.on('click', this.onViewClick, this);
18875 this.store.on('beforeload', this.onBeforeLoad, this);
18876 this.store.on('load', this.onLoad, this);
18877 this.store.on('loadexception', this.onLoadException, this);
18879 if(this.resizable){
18880 this.resizer = new Roo.Resizable(this.list, {
18881 pinned:true, handles:'se'
18883 this.resizer.on('resize', function(r, w, h){
18884 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18885 this.listWidth = w;
18886 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18887 this.restrictHeight();
18889 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18891 if(!this.editable){
18892 this.editable = true;
18893 this.setEditable(false);
18897 if (typeof(this.events.add.listeners) != 'undefined') {
18899 this.addicon = this.wrap.createChild(
18900 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
18902 this.addicon.on('click', function(e) {
18903 this.fireEvent('add', this);
18906 if (typeof(this.events.edit.listeners) != 'undefined') {
18908 this.editicon = this.wrap.createChild(
18909 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
18910 if (this.addicon) {
18911 this.editicon.setStyle('margin-left', '40px');
18913 this.editicon.on('click', function(e) {
18915 // we fire even if inothing is selected..
18916 this.fireEvent('edit', this, this.lastData );
18926 initEvents : function(){
18927 Roo.form.ComboBox.superclass.initEvents.call(this);
18929 this.keyNav = new Roo.KeyNav(this.el, {
18930 "up" : function(e){
18931 this.inKeyMode = true;
18935 "down" : function(e){
18936 if(!this.isExpanded()){
18937 this.onTriggerClick();
18939 this.inKeyMode = true;
18944 "enter" : function(e){
18945 this.onViewClick();
18949 "esc" : function(e){
18953 "tab" : function(e){
18954 this.onViewClick(false);
18955 this.fireEvent("specialkey", this, e);
18961 doRelay : function(foo, bar, hname){
18962 if(hname == 'down' || this.scope.isExpanded()){
18963 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18970 this.queryDelay = Math.max(this.queryDelay || 10,
18971 this.mode == 'local' ? 10 : 250);
18972 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18973 if(this.typeAhead){
18974 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18976 if(this.editable !== false){
18977 this.el.on("keyup", this.onKeyUp, this);
18979 if(this.forceSelection){
18980 this.on('blur', this.doForce, this);
18984 onDestroy : function(){
18986 this.view.setStore(null);
18987 this.view.el.removeAllListeners();
18988 this.view.el.remove();
18989 this.view.purgeListeners();
18992 this.list.destroy();
18995 this.store.un('beforeload', this.onBeforeLoad, this);
18996 this.store.un('load', this.onLoad, this);
18997 this.store.un('loadexception', this.onLoadException, this);
18999 Roo.form.ComboBox.superclass.onDestroy.call(this);
19003 fireKey : function(e){
19004 if(e.isNavKeyPress() && !this.list.isVisible()){
19005 this.fireEvent("specialkey", this, e);
19010 onResize: function(w, h){
19011 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19013 if(typeof w != 'number'){
19014 // we do not handle it!?!?
19017 var tw = this.trigger.getWidth();
19018 tw += this.addicon ? this.addicon.getWidth() : 0;
19019 tw += this.editicon ? this.editicon.getWidth() : 0;
19021 this.el.setWidth( this.adjustWidth('input', x));
19023 this.trigger.setStyle('left', x+'px');
19025 if(this.list && this.listWidth === undefined){
19026 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19027 this.list.setWidth(lw);
19028 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19036 * Allow or prevent the user from directly editing the field text. If false is passed,
19037 * the user will only be able to select from the items defined in the dropdown list. This method
19038 * is the runtime equivalent of setting the 'editable' config option at config time.
19039 * @param {Boolean} value True to allow the user to directly edit the field text
19041 setEditable : function(value){
19042 if(value == this.editable){
19045 this.editable = value;
19047 this.el.dom.setAttribute('readOnly', true);
19048 this.el.on('mousedown', this.onTriggerClick, this);
19049 this.el.addClass('x-combo-noedit');
19051 this.el.dom.setAttribute('readOnly', false);
19052 this.el.un('mousedown', this.onTriggerClick, this);
19053 this.el.removeClass('x-combo-noedit');
19058 onBeforeLoad : function(){
19059 if(!this.hasFocus){
19062 this.innerList.update(this.loadingText ?
19063 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19064 this.restrictHeight();
19065 this.selectedIndex = -1;
19069 onLoad : function(){
19070 if(!this.hasFocus){
19073 if(this.store.getCount() > 0){
19075 this.restrictHeight();
19076 if(this.lastQuery == this.allQuery){
19078 this.el.dom.select();
19080 if(!this.selectByValue(this.value, true)){
19081 this.select(0, true);
19085 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19086 this.taTask.delay(this.typeAheadDelay);
19090 this.onEmptyResults();
19095 onLoadException : function()
19098 Roo.log(this.store.reader.jsonData);
19099 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19100 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19106 onTypeAhead : function(){
19107 if(this.store.getCount() > 0){
19108 var r = this.store.getAt(0);
19109 var newValue = r.data[this.displayField];
19110 var len = newValue.length;
19111 var selStart = this.getRawValue().length;
19112 if(selStart != len){
19113 this.setRawValue(newValue);
19114 this.selectText(selStart, newValue.length);
19120 onSelect : function(record, index){
19121 if(this.fireEvent('beforeselect', this, record, index) !== false){
19122 this.setFromData(index > -1 ? record.data : false);
19124 this.fireEvent('select', this, record, index);
19129 * Returns the currently selected field value or empty string if no value is set.
19130 * @return {String} value The selected value
19132 getValue : function(){
19133 if(this.valueField){
19134 return typeof this.value != 'undefined' ? this.value : '';
19136 return Roo.form.ComboBox.superclass.getValue.call(this);
19140 * Clears any text/value currently set in the field
19142 clearValue : function(){
19143 if(this.hiddenField){
19144 this.hiddenField.value = '';
19147 this.setRawValue('');
19148 this.lastSelectionText = '';
19153 * Sets the specified value into the field. If the value finds a match, the corresponding record text
19154 * will be displayed in the field. If the value does not match the data value of an existing item,
19155 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19156 * Otherwise the field will be blank (although the value will still be set).
19157 * @param {String} value The value to match
19159 setValue : function(v){
19161 if(this.valueField){
19162 var r = this.findRecord(this.valueField, v);
19164 text = r.data[this.displayField];
19165 }else if(this.valueNotFoundText !== undefined){
19166 text = this.valueNotFoundText;
19169 this.lastSelectionText = text;
19170 if(this.hiddenField){
19171 this.hiddenField.value = v;
19173 Roo.form.ComboBox.superclass.setValue.call(this, text);
19177 * @property {Object} the last set data for the element
19182 * Sets the value of the field based on a object which is related to the record format for the store.
19183 * @param {Object} value the value to set as. or false on reset?
19185 setFromData : function(o){
19186 var dv = ''; // display value
19187 var vv = ''; // value value..
19189 if (this.displayField) {
19190 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19192 // this is an error condition!!!
19193 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
19196 if(this.valueField){
19197 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19199 if(this.hiddenField){
19200 this.hiddenField.value = vv;
19202 this.lastSelectionText = dv;
19203 Roo.form.ComboBox.superclass.setValue.call(this, dv);
19207 // no hidden field.. - we store the value in 'value', but still display
19208 // display field!!!!
19209 this.lastSelectionText = dv;
19210 Roo.form.ComboBox.superclass.setValue.call(this, dv);
19216 reset : function(){
19217 // overridden so that last data is reset..
19218 this.setValue(this.resetValue);
19219 this.clearInvalid();
19220 this.lastData = false;
19222 this.view.clearSelections();
19226 findRecord : function(prop, value){
19228 if(this.store.getCount() > 0){
19229 this.store.each(function(r){
19230 if(r.data[prop] == value){
19240 getName: function()
19242 // returns hidden if it's set..
19243 if (!this.rendered) {return ''};
19244 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
19248 onViewMove : function(e, t){
19249 this.inKeyMode = false;
19253 onViewOver : function(e, t){
19254 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19257 var item = this.view.findItemFromChild(t);
19259 var index = this.view.indexOf(item);
19260 this.select(index, false);
19265 onViewClick : function(doFocus)
19267 var index = this.view.getSelectedIndexes()[0];
19268 var r = this.store.getAt(index);
19270 this.onSelect(r, index);
19272 if(doFocus !== false && !this.blockFocus){
19278 restrictHeight : function(){
19279 this.innerList.dom.style.height = '';
19280 var inner = this.innerList.dom;
19281 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19282 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19283 this.list.beginUpdate();
19284 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19285 this.list.alignTo(this.el, this.listAlign);
19286 this.list.endUpdate();
19290 onEmptyResults : function(){
19295 * Returns true if the dropdown list is expanded, else false.
19297 isExpanded : function(){
19298 return this.list.isVisible();
19302 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19303 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19304 * @param {String} value The data value of the item to select
19305 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19306 * selected item if it is not currently in view (defaults to true)
19307 * @return {Boolean} True if the value matched an item in the list, else false
19309 selectByValue : function(v, scrollIntoView){
19310 if(v !== undefined && v !== null){
19311 var r = this.findRecord(this.valueField || this.displayField, v);
19313 this.select(this.store.indexOf(r), scrollIntoView);
19321 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19322 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19323 * @param {Number} index The zero-based index of the list item to select
19324 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19325 * selected item if it is not currently in view (defaults to true)
19327 select : function(index, scrollIntoView){
19328 this.selectedIndex = index;
19329 this.view.select(index);
19330 if(scrollIntoView !== false){
19331 var el = this.view.getNode(index);
19333 this.innerList.scrollChildIntoView(el, false);
19339 selectNext : function(){
19340 var ct = this.store.getCount();
19342 if(this.selectedIndex == -1){
19344 }else if(this.selectedIndex < ct-1){
19345 this.select(this.selectedIndex+1);
19351 selectPrev : function(){
19352 var ct = this.store.getCount();
19354 if(this.selectedIndex == -1){
19356 }else if(this.selectedIndex != 0){
19357 this.select(this.selectedIndex-1);
19363 onKeyUp : function(e){
19364 if(this.editable !== false && !e.isSpecialKey()){
19365 this.lastKey = e.getKey();
19366 this.dqTask.delay(this.queryDelay);
19371 validateBlur : function(){
19372 return !this.list || !this.list.isVisible();
19376 initQuery : function(){
19377 this.doQuery(this.getRawValue());
19381 doForce : function(){
19382 if(this.el.dom.value.length > 0){
19383 this.el.dom.value =
19384 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19390 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
19391 * query allowing the query action to be canceled if needed.
19392 * @param {String} query The SQL query to execute
19393 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19394 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
19395 * saved in the current store (defaults to false)
19397 doQuery : function(q, forceAll){
19398 if(q === undefined || q === null){
19403 forceAll: forceAll,
19407 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19411 forceAll = qe.forceAll;
19412 if(forceAll === true || (q.length >= this.minChars)){
19413 if(this.lastQuery != q || this.alwaysQuery){
19414 this.lastQuery = q;
19415 if(this.mode == 'local'){
19416 this.selectedIndex = -1;
19418 this.store.clearFilter();
19420 this.store.filter(this.displayField, q);
19424 this.store.baseParams[this.queryParam] = q;
19426 params: this.getParams(q)
19431 this.selectedIndex = -1;
19438 getParams : function(q){
19440 //p[this.queryParam] = q;
19443 p.limit = this.pageSize;
19449 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19451 collapse : function(){
19452 if(!this.isExpanded()){
19456 Roo.get(document).un('mousedown', this.collapseIf, this);
19457 Roo.get(document).un('mousewheel', this.collapseIf, this);
19458 if (!this.editable) {
19459 Roo.get(document).un('keydown', this.listKeyPress, this);
19461 this.fireEvent('collapse', this);
19465 collapseIf : function(e){
19466 if(!e.within(this.wrap) && !e.within(this.list)){
19472 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19474 expand : function(){
19475 if(this.isExpanded() || !this.hasFocus){
19478 this.list.alignTo(this.el, this.listAlign);
19480 Roo.get(document).on('mousedown', this.collapseIf, this);
19481 Roo.get(document).on('mousewheel', this.collapseIf, this);
19482 if (!this.editable) {
19483 Roo.get(document).on('keydown', this.listKeyPress, this);
19486 this.fireEvent('expand', this);
19490 // Implements the default empty TriggerField.onTriggerClick function
19491 onTriggerClick : function(){
19495 if(this.isExpanded()){
19497 if (!this.blockFocus) {
19502 this.hasFocus = true;
19503 if(this.triggerAction == 'all') {
19504 this.doQuery(this.allQuery, true);
19506 this.doQuery(this.getRawValue());
19508 if (!this.blockFocus) {
19513 listKeyPress : function(e)
19515 //Roo.log('listkeypress');
19516 // scroll to first matching element based on key pres..
19517 if (e.isSpecialKey()) {
19520 var k = String.fromCharCode(e.getKey()).toUpperCase();
19523 var csel = this.view.getSelectedNodes();
19524 var cselitem = false;
19526 var ix = this.view.indexOf(csel[0]);
19527 cselitem = this.store.getAt(ix);
19528 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19534 this.store.each(function(v) {
19536 // start at existing selection.
19537 if (cselitem.id == v.id) {
19543 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19544 match = this.store.indexOf(v);
19549 if (match === false) {
19550 return true; // no more action?
19553 this.view.select(match);
19554 var sn = Roo.get(this.view.getSelectedNodes()[0]);
19555 sn.scrollIntoView(sn.dom.parentNode, false);
19559 * @cfg {Boolean} grow
19563 * @cfg {Number} growMin
19567 * @cfg {Number} growMax
19575 * Copyright(c) 2010-2012, Roo J Solutions Limited
19582 * @class Roo.form.ComboBoxArray
19583 * @extends Roo.form.TextField
19584 * A facebook style adder... for lists of email / people / countries etc...
19585 * pick multiple items from a combo box, and shows each one.
19587 * Fred [x] Brian [x] [Pick another |v]
19590 * For this to work: it needs various extra information
19591 * - normal combo problay has
19593 * + displayField, valueField
19595 * For our purpose...
19598 * If we change from 'extends' to wrapping...
19605 * Create a new ComboBoxArray.
19606 * @param {Object} config Configuration options
19610 Roo.form.ComboBoxArray = function(config)
19614 * @event beforeremove
19615 * Fires before remove the value from the list
19616 * @param {Roo.form.ComboBoxArray} _self This combo box array
19617 * @param {Roo.form.ComboBoxArray.Item} item removed item
19619 'beforeremove' : true,
19622 * Fires when remove the value from the list
19623 * @param {Roo.form.ComboBoxArray} _self This combo box array
19624 * @param {Roo.form.ComboBoxArray.Item} item removed item
19631 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19633 this.items = new Roo.util.MixedCollection(false);
19635 // construct the child combo...
19645 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19648 * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19653 // behavies liek a hiddne field
19654 inputType: 'hidden',
19656 * @cfg {Number} width The width of the box that displays the selected element
19663 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
19667 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
19669 hiddenName : false,
19672 // private the array of items that are displayed..
19674 // private - the hidden field el.
19676 // private - the filed el..
19679 //validateValue : function() { return true; }, // all values are ok!
19680 //onAddClick: function() { },
19682 onRender : function(ct, position)
19685 // create the standard hidden element
19686 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19689 // give fake names to child combo;
19690 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19691 this.combo.name = this.name? (this.name+'-subcombo') : this.name;
19693 this.combo = Roo.factory(this.combo, Roo.form);
19694 this.combo.onRender(ct, position);
19695 if (typeof(this.combo.width) != 'undefined') {
19696 this.combo.onResize(this.combo.width,0);
19699 this.combo.initEvents();
19701 // assigned so form know we need to do this..
19702 this.store = this.combo.store;
19703 this.valueField = this.combo.valueField;
19704 this.displayField = this.combo.displayField ;
19707 this.combo.wrap.addClass('x-cbarray-grp');
19709 var cbwrap = this.combo.wrap.createChild(
19710 {tag: 'div', cls: 'x-cbarray-cb'},
19715 this.hiddenEl = this.combo.wrap.createChild({
19716 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
19718 this.el = this.combo.wrap.createChild({
19719 tag: 'input', type:'hidden' , name: this.name, value : ''
19721 // this.el.dom.removeAttribute("name");
19724 this.outerWrap = this.combo.wrap;
19725 this.wrap = cbwrap;
19727 this.outerWrap.setWidth(this.width);
19728 this.outerWrap.dom.removeChild(this.el.dom);
19730 this.wrap.dom.appendChild(this.el.dom);
19731 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19732 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19734 this.combo.trigger.setStyle('position','relative');
19735 this.combo.trigger.setStyle('left', '0px');
19736 this.combo.trigger.setStyle('top', '2px');
19738 this.combo.el.setStyle('vertical-align', 'text-bottom');
19740 //this.trigger.setStyle('vertical-align', 'top');
19742 // this should use the code from combo really... on('add' ....)
19746 this.adder = this.outerWrap.createChild(
19747 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
19749 this.adder.on('click', function(e) {
19750 _t.fireEvent('adderclick', this, e);
19754 //this.adder.on('click', this.onAddClick, _t);
19757 this.combo.on('select', function(cb, rec, ix) {
19758 this.addItem(rec.data);
19761 cb.el.dom.value = '';
19762 //cb.lastData = rec.data;
19771 getName: function()
19773 // returns hidden if it's set..
19774 if (!this.rendered) {return ''};
19775 return this.hiddenName ? this.hiddenName : this.name;
19780 onResize: function(w, h){
19783 // not sure if this is needed..
19784 //this.combo.onResize(w,h);
19786 if(typeof w != 'number'){
19787 // we do not handle it!?!?
19790 var tw = this.combo.trigger.getWidth();
19791 tw += this.addicon ? this.addicon.getWidth() : 0;
19792 tw += this.editicon ? this.editicon.getWidth() : 0;
19794 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19796 this.combo.trigger.setStyle('left', '0px');
19798 if(this.list && this.listWidth === undefined){
19799 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19800 this.list.setWidth(lw);
19801 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19808 addItem: function(rec)
19810 var valueField = this.combo.valueField;
19811 var displayField = this.combo.displayField;
19812 if (this.items.indexOfKey(rec[valueField]) > -1) {
19813 //console.log("GOT " + rec.data.id);
19817 var x = new Roo.form.ComboBoxArray.Item({
19818 //id : rec[this.idField],
19820 displayField : displayField ,
19821 tipField : displayField ,
19825 this.items.add(rec[valueField],x);
19826 // add it before the element..
19827 this.updateHiddenEl();
19828 x.render(this.outerWrap, this.wrap.dom);
19829 // add the image handler..
19832 updateHiddenEl : function()
19835 if (!this.hiddenEl) {
19839 var idField = this.combo.valueField;
19841 this.items.each(function(f) {
19842 ar.push(f.data[idField]);
19845 this.hiddenEl.dom.value = ar.join(',');
19851 this.items.clear();
19853 Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19857 this.el.dom.value = '';
19858 if (this.hiddenEl) {
19859 this.hiddenEl.dom.value = '';
19863 getValue: function()
19865 return this.hiddenEl ? this.hiddenEl.dom.value : '';
19867 setValue: function(v) // not a valid action - must use addItems..
19874 if (this.store.isLocal && (typeof(v) == 'string')) {
19875 // then we can use the store to find the values..
19876 // comma seperated at present.. this needs to allow JSON based encoding..
19877 this.hiddenEl.value = v;
19879 Roo.each(v.split(','), function(k) {
19880 Roo.log("CHECK " + this.valueField + ',' + k);
19881 var li = this.store.query(this.valueField, k);
19886 add[this.valueField] = k;
19887 add[this.displayField] = li.item(0).data[this.displayField];
19893 if (typeof(v) == 'object' ) {
19894 // then let's assume it's an array of objects..
19895 Roo.each(v, function(l) {
19903 setFromData: function(v)
19905 // this recieves an object, if setValues is called.
19907 this.el.dom.value = v[this.displayField];
19908 this.hiddenEl.dom.value = v[this.valueField];
19909 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19912 var kv = v[this.valueField];
19913 var dv = v[this.displayField];
19914 kv = typeof(kv) != 'string' ? '' : kv;
19915 dv = typeof(dv) != 'string' ? '' : dv;
19918 var keys = kv.split(',');
19919 var display = dv.split(',');
19920 for (var i = 0 ; i < keys.length; i++) {
19923 add[this.valueField] = keys[i];
19924 add[this.displayField] = display[i];
19932 * Validates the combox array value
19933 * @return {Boolean} True if the value is valid, else false
19935 validate : function(){
19936 if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19937 this.clearInvalid();
19943 validateValue : function(value){
19944 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19952 isDirty : function() {
19953 if(this.disabled) {
19958 var d = Roo.decode(String(this.originalValue));
19960 return String(this.getValue()) !== String(this.originalValue);
19963 var originalValue = [];
19965 for (var i = 0; i < d.length; i++){
19966 originalValue.push(d[i][this.valueField]);
19969 return String(this.getValue()) !== String(originalValue.join(','));
19978 * @class Roo.form.ComboBoxArray.Item
19979 * @extends Roo.BoxComponent
19980 * A selected item in the list
19981 * Fred [x] Brian [x] [Pick another |v]
19984 * Create a new item.
19985 * @param {Object} config Configuration options
19988 Roo.form.ComboBoxArray.Item = function(config) {
19989 config.id = Roo.id();
19990 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19993 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19996 displayField : false,
20000 defaultAutoCreate : {
20002 cls: 'x-cbarray-item',
20009 src : Roo.BLANK_IMAGE_URL ,
20017 onRender : function(ct, position)
20019 Roo.form.Field.superclass.onRender.call(this, ct, position);
20022 var cfg = this.getAutoCreate();
20023 this.el = ct.createChild(cfg, position);
20026 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20028 this.el.child('div').dom.innerHTML = this.cb.renderer ?
20029 this.cb.renderer(this.data) :
20030 String.format('{0}',this.data[this.displayField]);
20033 this.el.child('div').dom.setAttribute('qtip',
20034 String.format('{0}',this.data[this.tipField])
20037 this.el.child('img').on('click', this.remove, this);
20041 remove : function()
20043 if(this.cb.disabled){
20047 if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20048 this.cb.items.remove(this);
20049 this.el.child('img').un('click', this.remove, this);
20051 this.cb.updateHiddenEl();
20053 this.cb.fireEvent('remove', this.cb, this);
20059 * Ext JS Library 1.1.1
20060 * Copyright(c) 2006-2007, Ext JS, LLC.
20062 * Originally Released Under LGPL - original licence link has changed is not relivant.
20065 * <script type="text/javascript">
20068 * @class Roo.form.Checkbox
20069 * @extends Roo.form.Field
20070 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
20072 * Creates a new Checkbox
20073 * @param {Object} config Configuration options
20075 Roo.form.Checkbox = function(config){
20076 Roo.form.Checkbox.superclass.constructor.call(this, config);
20080 * Fires when the checkbox is checked or unchecked.
20081 * @param {Roo.form.Checkbox} this This checkbox
20082 * @param {Boolean} checked The new checked value
20088 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
20090 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20092 focusClass : undefined,
20094 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20096 fieldClass: "x-form-field",
20098 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20102 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20103 * {tag: "input", type: "checkbox", autocomplete: "off"})
20105 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20107 * @cfg {String} boxLabel The text that appears beside the checkbox
20111 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20115 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20117 valueOff: '0', // value when not checked..
20119 actionMode : 'viewEl',
20122 itemCls : 'x-menu-check-item x-form-item',
20123 groupClass : 'x-menu-group-item',
20124 inputType : 'hidden',
20127 inSetChecked: false, // check that we are not calling self...
20129 inputElement: false, // real input element?
20130 basedOn: false, // ????
20132 isFormField: true, // not sure where this is needed!!!!
20134 onResize : function(){
20135 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20136 if(!this.boxLabel){
20137 this.el.alignTo(this.wrap, 'c-c');
20141 initEvents : function(){
20142 Roo.form.Checkbox.superclass.initEvents.call(this);
20143 this.el.on("click", this.onClick, this);
20144 this.el.on("change", this.onClick, this);
20148 getResizeEl : function(){
20152 getPositionEl : function(){
20157 onRender : function(ct, position){
20158 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20160 if(this.inputValue !== undefined){
20161 this.el.dom.value = this.inputValue;
20164 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20165 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20166 var viewEl = this.wrap.createChild({
20167 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20168 this.viewEl = viewEl;
20169 this.wrap.on('click', this.onClick, this);
20171 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20172 this.el.on('propertychange', this.setFromHidden, this); //ie
20177 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20178 // viewEl.on('click', this.onClick, this);
20180 //if(this.checked){
20181 this.setChecked(this.checked);
20183 //this.checked = this.el.dom;
20189 initValue : Roo.emptyFn,
20192 * Returns the checked state of the checkbox.
20193 * @return {Boolean} True if checked, else false
20195 getValue : function(){
20197 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20199 return this.valueOff;
20204 onClick : function(){
20205 if (this.disabled) {
20208 this.setChecked(!this.checked);
20210 //if(this.el.dom.checked != this.checked){
20211 // this.setValue(this.el.dom.checked);
20216 * Sets the checked state of the checkbox.
20217 * On is always based on a string comparison between inputValue and the param.
20218 * @param {Boolean/String} value - the value to set
20219 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20221 setValue : function(v,suppressEvent){
20224 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20225 //if(this.el && this.el.dom){
20226 // this.el.dom.checked = this.checked;
20227 // this.el.dom.defaultChecked = this.checked;
20229 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20230 //this.fireEvent("check", this, this.checked);
20233 setChecked : function(state,suppressEvent)
20235 if (this.inSetChecked) {
20236 this.checked = state;
20242 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20244 this.checked = state;
20245 if(suppressEvent !== true){
20246 this.fireEvent('check', this, state);
20248 this.inSetChecked = true;
20249 this.el.dom.value = state ? this.inputValue : this.valueOff;
20250 this.inSetChecked = false;
20253 // handle setting of hidden value by some other method!!?!?
20254 setFromHidden: function()
20259 //console.log("SET FROM HIDDEN");
20260 //alert('setFrom hidden');
20261 this.setValue(this.el.dom.value);
20264 onDestroy : function()
20267 Roo.get(this.viewEl).remove();
20270 Roo.form.Checkbox.superclass.onDestroy.call(this);
20273 setBoxLabel : function(str)
20275 this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20280 * Ext JS Library 1.1.1
20281 * Copyright(c) 2006-2007, Ext JS, LLC.
20283 * Originally Released Under LGPL - original licence link has changed is not relivant.
20286 * <script type="text/javascript">
20290 * @class Roo.form.Radio
20291 * @extends Roo.form.Checkbox
20292 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
20293 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20295 * Creates a new Radio
20296 * @param {Object} config Configuration options
20298 Roo.form.Radio = function(){
20299 Roo.form.Radio.superclass.constructor.apply(this, arguments);
20301 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20302 inputType: 'radio',
20305 * If this radio is part of a group, it will return the selected value
20308 getGroupValue : function(){
20309 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20313 onRender : function(ct, position){
20314 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20316 if(this.inputValue !== undefined){
20317 this.el.dom.value = this.inputValue;
20320 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20321 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20322 //var viewEl = this.wrap.createChild({
20323 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20324 //this.viewEl = viewEl;
20325 //this.wrap.on('click', this.onClick, this);
20327 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20328 //this.el.on('propertychange', this.setFromHidden, this); //ie
20333 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20334 // viewEl.on('click', this.onClick, this);
20337 this.el.dom.checked = 'checked' ;
20343 });//<script type="text/javascript">
20346 * Based Ext JS Library 1.1.1
20347 * Copyright(c) 2006-2007, Ext JS, LLC.
20353 * @class Roo.HtmlEditorCore
20354 * @extends Roo.Component
20355 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20357 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20360 Roo.HtmlEditorCore = function(config){
20363 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20368 * @event initialize
20369 * Fires when the editor is fully initialized (including the iframe)
20370 * @param {Roo.HtmlEditorCore} this
20375 * Fires when the editor is first receives the focus. Any insertion must wait
20376 * until after this event.
20377 * @param {Roo.HtmlEditorCore} this
20381 * @event beforesync
20382 * Fires before the textarea is updated with content from the editor iframe. Return false
20383 * to cancel the sync.
20384 * @param {Roo.HtmlEditorCore} this
20385 * @param {String} html
20389 * @event beforepush
20390 * Fires before the iframe editor is updated with content from the textarea. Return false
20391 * to cancel the push.
20392 * @param {Roo.HtmlEditorCore} this
20393 * @param {String} html
20398 * Fires when the textarea is updated with content from the editor iframe.
20399 * @param {Roo.HtmlEditorCore} this
20400 * @param {String} html
20405 * Fires when the iframe editor is updated with content from the textarea.
20406 * @param {Roo.HtmlEditorCore} this
20407 * @param {String} html
20412 * @event editorevent
20413 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20414 * @param {Roo.HtmlEditorCore} this
20420 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20422 // defaults : white / black...
20423 this.applyBlacklists();
20430 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20434 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20440 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20445 * @cfg {Number} height (in pixels)
20449 * @cfg {Number} width (in pixels)
20454 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20457 stylesheets: false,
20462 // private properties
20463 validationEvent : false,
20465 initialized : false,
20467 sourceEditMode : false,
20468 onFocus : Roo.emptyFn,
20470 hideMode:'offsets',
20474 // blacklist + whitelisted elements..
20481 * Protected method that will not generally be called directly. It
20482 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20483 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20485 getDocMarkup : function(){
20489 // inherit styels from page...??
20490 if (this.stylesheets === false) {
20492 Roo.get(document.head).select('style').each(function(node) {
20493 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20496 Roo.get(document.head).select('link').each(function(node) {
20497 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20500 } else if (!this.stylesheets.length) {
20502 st = '<style type="text/css">' +
20503 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20509 st += '<style type="text/css">' +
20510 'IMG { cursor: pointer } ' +
20514 return '<html><head>' + st +
20515 //<style type="text/css">' +
20516 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20518 ' </head><body class="roo-htmleditor-body"></body></html>';
20522 onRender : function(ct, position)
20525 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20526 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20529 this.el.dom.style.border = '0 none';
20530 this.el.dom.setAttribute('tabIndex', -1);
20531 this.el.addClass('x-hidden hide');
20535 if(Roo.isIE){ // fix IE 1px bogus margin
20536 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20540 this.frameId = Roo.id();
20544 var iframe = this.owner.wrap.createChild({
20546 cls: 'form-control', // bootstrap..
20548 name: this.frameId,
20549 frameBorder : 'no',
20550 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20555 this.iframe = iframe.dom;
20557 this.assignDocWin();
20559 this.doc.designMode = 'on';
20562 this.doc.write(this.getDocMarkup());
20566 var task = { // must defer to wait for browser to be ready
20568 //console.log("run task?" + this.doc.readyState);
20569 this.assignDocWin();
20570 if(this.doc.body || this.doc.readyState == 'complete'){
20572 this.doc.designMode="on";
20576 Roo.TaskMgr.stop(task);
20577 this.initEditor.defer(10, this);
20584 Roo.TaskMgr.start(task);
20589 onResize : function(w, h)
20591 Roo.log('resize: ' +w + ',' + h );
20592 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20596 if(typeof w == 'number'){
20598 this.iframe.style.width = w + 'px';
20600 if(typeof h == 'number'){
20602 this.iframe.style.height = h + 'px';
20604 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20611 * Toggles the editor between standard and source edit mode.
20612 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20614 toggleSourceEdit : function(sourceEditMode){
20616 this.sourceEditMode = sourceEditMode === true;
20618 if(this.sourceEditMode){
20620 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20623 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20624 //this.iframe.className = '';
20627 //this.setSize(this.owner.wrap.getSize());
20628 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20635 * Protected method that will not generally be called directly. If you need/want
20636 * custom HTML cleanup, this is the method you should override.
20637 * @param {String} html The HTML to be cleaned
20638 * return {String} The cleaned HTML
20640 cleanHtml : function(html){
20641 html = String(html);
20642 if(html.length > 5){
20643 if(Roo.isSafari){ // strip safari nonsense
20644 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20647 if(html == ' '){
20654 * HTML Editor -> Textarea
20655 * Protected method that will not generally be called directly. Syncs the contents
20656 * of the editor iframe with the textarea.
20658 syncValue : function(){
20659 if(this.initialized){
20660 var bd = (this.doc.body || this.doc.documentElement);
20661 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20662 var html = bd.innerHTML;
20664 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20665 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20667 html = '<div style="'+m[0]+'">' + html + '</div>';
20670 html = this.cleanHtml(html);
20671 // fix up the special chars.. normaly like back quotes in word...
20672 // however we do not want to do this with chinese..
20673 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20674 var cc = b.charCodeAt();
20676 (cc >= 0x4E00 && cc < 0xA000 ) ||
20677 (cc >= 0x3400 && cc < 0x4E00 ) ||
20678 (cc >= 0xf900 && cc < 0xfb00 )
20684 if(this.owner.fireEvent('beforesync', this, html) !== false){
20685 this.el.dom.value = html;
20686 this.owner.fireEvent('sync', this, html);
20692 * Protected method that will not generally be called directly. Pushes the value of the textarea
20693 * into the iframe editor.
20695 pushValue : function(){
20696 if(this.initialized){
20697 var v = this.el.dom.value.trim();
20699 // if(v.length < 1){
20703 if(this.owner.fireEvent('beforepush', this, v) !== false){
20704 var d = (this.doc.body || this.doc.documentElement);
20706 this.cleanUpPaste();
20707 this.el.dom.value = d.innerHTML;
20708 this.owner.fireEvent('push', this, v);
20714 deferFocus : function(){
20715 this.focus.defer(10, this);
20719 focus : function(){
20720 if(this.win && !this.sourceEditMode){
20727 assignDocWin: function()
20729 var iframe = this.iframe;
20732 this.doc = iframe.contentWindow.document;
20733 this.win = iframe.contentWindow;
20735 // if (!Roo.get(this.frameId)) {
20738 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20739 // this.win = Roo.get(this.frameId).dom.contentWindow;
20741 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20745 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20746 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20751 initEditor : function(){
20752 //console.log("INIT EDITOR");
20753 this.assignDocWin();
20757 this.doc.designMode="on";
20759 this.doc.write(this.getDocMarkup());
20762 var dbody = (this.doc.body || this.doc.documentElement);
20763 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20764 // this copies styles from the containing element into thsi one..
20765 // not sure why we need all of this..
20766 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20768 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20769 //ss['background-attachment'] = 'fixed'; // w3c
20770 dbody.bgProperties = 'fixed'; // ie
20771 //Roo.DomHelper.applyStyles(dbody, ss);
20772 Roo.EventManager.on(this.doc, {
20773 //'mousedown': this.onEditorEvent,
20774 'mouseup': this.onEditorEvent,
20775 'dblclick': this.onEditorEvent,
20776 'click': this.onEditorEvent,
20777 'keyup': this.onEditorEvent,
20782 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20784 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20785 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20787 this.initialized = true;
20789 this.owner.fireEvent('initialize', this);
20794 onDestroy : function(){
20800 //for (var i =0; i < this.toolbars.length;i++) {
20801 // // fixme - ask toolbars for heights?
20802 // this.toolbars[i].onDestroy();
20805 //this.wrap.dom.innerHTML = '';
20806 //this.wrap.remove();
20811 onFirstFocus : function(){
20813 this.assignDocWin();
20816 this.activated = true;
20819 if(Roo.isGecko){ // prevent silly gecko errors
20821 var s = this.win.getSelection();
20822 if(!s.focusNode || s.focusNode.nodeType != 3){
20823 var r = s.getRangeAt(0);
20824 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20829 this.execCmd('useCSS', true);
20830 this.execCmd('styleWithCSS', false);
20833 this.owner.fireEvent('activate', this);
20837 adjustFont: function(btn){
20838 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20839 //if(Roo.isSafari){ // safari
20842 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20843 if(Roo.isSafari){ // safari
20844 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20845 v = (v < 10) ? 10 : v;
20846 v = (v > 48) ? 48 : v;
20847 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20852 v = Math.max(1, v+adjust);
20854 this.execCmd('FontSize', v );
20857 onEditorEvent : function(e)
20859 this.owner.fireEvent('editorevent', this, e);
20860 // this.updateToolbar();
20861 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20864 insertTag : function(tg)
20866 // could be a bit smarter... -> wrap the current selected tRoo..
20867 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20869 range = this.createRange(this.getSelection());
20870 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20871 wrappingNode.appendChild(range.extractContents());
20872 range.insertNode(wrappingNode);
20879 this.execCmd("formatblock", tg);
20883 insertText : function(txt)
20887 var range = this.createRange();
20888 range.deleteContents();
20889 //alert(Sender.getAttribute('label'));
20891 range.insertNode(this.doc.createTextNode(txt));
20897 * Executes a Midas editor command on the editor document and performs necessary focus and
20898 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20899 * @param {String} cmd The Midas command
20900 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20902 relayCmd : function(cmd, value){
20904 this.execCmd(cmd, value);
20905 this.owner.fireEvent('editorevent', this);
20906 //this.updateToolbar();
20907 this.owner.deferFocus();
20911 * Executes a Midas editor command directly on the editor document.
20912 * For visual commands, you should use {@link #relayCmd} instead.
20913 * <b>This should only be called after the editor is initialized.</b>
20914 * @param {String} cmd The Midas command
20915 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20917 execCmd : function(cmd, value){
20918 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20925 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20927 * @param {String} text | dom node..
20929 insertAtCursor : function(text)
20932 if(!this.activated){
20938 var r = this.doc.selection.createRange();
20949 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20953 // from jquery ui (MIT licenced)
20955 var win = this.win;
20957 if (win.getSelection && win.getSelection().getRangeAt) {
20958 range = win.getSelection().getRangeAt(0);
20959 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20960 range.insertNode(node);
20961 } else if (win.document.selection && win.document.selection.createRange) {
20962 // no firefox support
20963 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20964 win.document.selection.createRange().pasteHTML(txt);
20966 // no firefox support
20967 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20968 this.execCmd('InsertHTML', txt);
20977 mozKeyPress : function(e){
20979 var c = e.getCharCode(), cmd;
20982 c = String.fromCharCode(c).toLowerCase();
20996 this.cleanUpPaste.defer(100, this);
21004 e.preventDefault();
21012 fixKeys : function(){ // load time branching for fastest keydown performance
21014 return function(e){
21015 var k = e.getKey(), r;
21018 r = this.doc.selection.createRange();
21021 r.pasteHTML('    ');
21028 r = this.doc.selection.createRange();
21030 var target = r.parentElement();
21031 if(!target || target.tagName.toLowerCase() != 'li'){
21033 r.pasteHTML('<br />');
21039 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21040 this.cleanUpPaste.defer(100, this);
21046 }else if(Roo.isOpera){
21047 return function(e){
21048 var k = e.getKey();
21052 this.execCmd('InsertHTML','    ');
21055 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21056 this.cleanUpPaste.defer(100, this);
21061 }else if(Roo.isSafari){
21062 return function(e){
21063 var k = e.getKey();
21067 this.execCmd('InsertText','\t');
21071 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21072 this.cleanUpPaste.defer(100, this);
21080 getAllAncestors: function()
21082 var p = this.getSelectedNode();
21085 a.push(p); // push blank onto stack..
21086 p = this.getParentElement();
21090 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21094 a.push(this.doc.body);
21098 lastSelNode : false,
21101 getSelection : function()
21103 this.assignDocWin();
21104 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21107 getSelectedNode: function()
21109 // this may only work on Gecko!!!
21111 // should we cache this!!!!
21116 var range = this.createRange(this.getSelection()).cloneRange();
21119 var parent = range.parentElement();
21121 var testRange = range.duplicate();
21122 testRange.moveToElementText(parent);
21123 if (testRange.inRange(range)) {
21126 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21129 parent = parent.parentElement;
21134 // is ancestor a text element.
21135 var ac = range.commonAncestorContainer;
21136 if (ac.nodeType == 3) {
21137 ac = ac.parentNode;
21140 var ar = ac.childNodes;
21143 var other_nodes = [];
21144 var has_other_nodes = false;
21145 for (var i=0;i<ar.length;i++) {
21146 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21149 // fullly contained node.
21151 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21156 // probably selected..
21157 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21158 other_nodes.push(ar[i]);
21162 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21167 has_other_nodes = true;
21169 if (!nodes.length && other_nodes.length) {
21170 nodes= other_nodes;
21172 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21178 createRange: function(sel)
21180 // this has strange effects when using with
21181 // top toolbar - not sure if it's a great idea.
21182 //this.editor.contentWindow.focus();
21183 if (typeof sel != "undefined") {
21185 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21187 return this.doc.createRange();
21190 return this.doc.createRange();
21193 getParentElement: function()
21196 this.assignDocWin();
21197 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21199 var range = this.createRange(sel);
21202 var p = range.commonAncestorContainer;
21203 while (p.nodeType == 3) { // text node
21214 * Range intersection.. the hard stuff...
21218 * [ -- selected range --- ]
21222 * if end is before start or hits it. fail.
21223 * if start is after end or hits it fail.
21225 * if either hits (but other is outside. - then it's not
21231 // @see http://www.thismuchiknow.co.uk/?p=64.
21232 rangeIntersectsNode : function(range, node)
21234 var nodeRange = node.ownerDocument.createRange();
21236 nodeRange.selectNode(node);
21238 nodeRange.selectNodeContents(node);
21241 var rangeStartRange = range.cloneRange();
21242 rangeStartRange.collapse(true);
21244 var rangeEndRange = range.cloneRange();
21245 rangeEndRange.collapse(false);
21247 var nodeStartRange = nodeRange.cloneRange();
21248 nodeStartRange.collapse(true);
21250 var nodeEndRange = nodeRange.cloneRange();
21251 nodeEndRange.collapse(false);
21253 return rangeStartRange.compareBoundaryPoints(
21254 Range.START_TO_START, nodeEndRange) == -1 &&
21255 rangeEndRange.compareBoundaryPoints(
21256 Range.START_TO_START, nodeStartRange) == 1;
21260 rangeCompareNode : function(range, node)
21262 var nodeRange = node.ownerDocument.createRange();
21264 nodeRange.selectNode(node);
21266 nodeRange.selectNodeContents(node);
21270 range.collapse(true);
21272 nodeRange.collapse(true);
21274 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21275 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21277 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21279 var nodeIsBefore = ss == 1;
21280 var nodeIsAfter = ee == -1;
21282 if (nodeIsBefore && nodeIsAfter) {
21285 if (!nodeIsBefore && nodeIsAfter) {
21286 return 1; //right trailed.
21289 if (nodeIsBefore && !nodeIsAfter) {
21290 return 2; // left trailed.
21296 // private? - in a new class?
21297 cleanUpPaste : function()
21299 // cleans up the whole document..
21300 Roo.log('cleanuppaste');
21302 this.cleanUpChildren(this.doc.body);
21303 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21304 if (clean != this.doc.body.innerHTML) {
21305 this.doc.body.innerHTML = clean;
21310 cleanWordChars : function(input) {// change the chars to hex code
21311 var he = Roo.HtmlEditorCore;
21313 var output = input;
21314 Roo.each(he.swapCodes, function(sw) {
21315 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21317 output = output.replace(swapper, sw[1]);
21324 cleanUpChildren : function (n)
21326 if (!n.childNodes.length) {
21329 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21330 this.cleanUpChild(n.childNodes[i]);
21337 cleanUpChild : function (node)
21340 //console.log(node);
21341 if (node.nodeName == "#text") {
21342 // clean up silly Windows -- stuff?
21345 if (node.nodeName == "#comment") {
21346 node.parentNode.removeChild(node);
21347 // clean up silly Windows -- stuff?
21350 var lcname = node.tagName.toLowerCase();
21351 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21352 // whitelist of tags..
21354 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21356 node.parentNode.removeChild(node);
21361 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21363 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21364 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21366 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21367 // remove_keep_children = true;
21370 if (remove_keep_children) {
21371 this.cleanUpChildren(node);
21372 // inserts everything just before this node...
21373 while (node.childNodes.length) {
21374 var cn = node.childNodes[0];
21375 node.removeChild(cn);
21376 node.parentNode.insertBefore(cn, node);
21378 node.parentNode.removeChild(node);
21382 if (!node.attributes || !node.attributes.length) {
21383 this.cleanUpChildren(node);
21387 function cleanAttr(n,v)
21390 if (v.match(/^\./) || v.match(/^\//)) {
21393 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21396 if (v.match(/^#/)) {
21399 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21400 node.removeAttribute(n);
21404 var cwhite = this.cwhite;
21405 var cblack = this.cblack;
21407 function cleanStyle(n,v)
21409 if (v.match(/expression/)) { //XSS?? should we even bother..
21410 node.removeAttribute(n);
21414 var parts = v.split(/;/);
21417 Roo.each(parts, function(p) {
21418 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21422 var l = p.split(':').shift().replace(/\s+/g,'');
21423 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21425 if ( cwhite.length && cblack.indexOf(l) > -1) {
21426 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21427 //node.removeAttribute(n);
21431 // only allow 'c whitelisted system attributes'
21432 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21433 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21434 //node.removeAttribute(n);
21444 if (clean.length) {
21445 node.setAttribute(n, clean.join(';'));
21447 node.removeAttribute(n);
21453 for (var i = node.attributes.length-1; i > -1 ; i--) {
21454 var a = node.attributes[i];
21457 if (a.name.toLowerCase().substr(0,2)=='on') {
21458 node.removeAttribute(a.name);
21461 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21462 node.removeAttribute(a.name);
21465 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21466 cleanAttr(a.name,a.value); // fixme..
21469 if (a.name == 'style') {
21470 cleanStyle(a.name,a.value);
21473 /// clean up MS crap..
21474 // tecnically this should be a list of valid class'es..
21477 if (a.name == 'class') {
21478 if (a.value.match(/^Mso/)) {
21479 node.className = '';
21482 if (a.value.match(/^body$/)) {
21483 node.className = '';
21494 this.cleanUpChildren(node);
21500 * Clean up MS wordisms...
21502 cleanWord : function(node)
21507 this.cleanWord(this.doc.body);
21510 if (node.nodeName == "#text") {
21511 // clean up silly Windows -- stuff?
21514 if (node.nodeName == "#comment") {
21515 node.parentNode.removeChild(node);
21516 // clean up silly Windows -- stuff?
21520 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21521 node.parentNode.removeChild(node);
21525 // remove - but keep children..
21526 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21527 while (node.childNodes.length) {
21528 var cn = node.childNodes[0];
21529 node.removeChild(cn);
21530 node.parentNode.insertBefore(cn, node);
21532 node.parentNode.removeChild(node);
21533 this.iterateChildren(node, this.cleanWord);
21537 if (node.className.length) {
21539 var cn = node.className.split(/\W+/);
21541 Roo.each(cn, function(cls) {
21542 if (cls.match(/Mso[a-zA-Z]+/)) {
21547 node.className = cna.length ? cna.join(' ') : '';
21549 node.removeAttribute("class");
21553 if (node.hasAttribute("lang")) {
21554 node.removeAttribute("lang");
21557 if (node.hasAttribute("style")) {
21559 var styles = node.getAttribute("style").split(";");
21561 Roo.each(styles, function(s) {
21562 if (!s.match(/:/)) {
21565 var kv = s.split(":");
21566 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21569 // what ever is left... we allow.
21572 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21573 if (!nstyle.length) {
21574 node.removeAttribute('style');
21577 this.iterateChildren(node, this.cleanWord);
21583 * iterateChildren of a Node, calling fn each time, using this as the scole..
21584 * @param {DomNode} node node to iterate children of.
21585 * @param {Function} fn method of this class to call on each item.
21587 iterateChildren : function(node, fn)
21589 if (!node.childNodes.length) {
21592 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21593 fn.call(this, node.childNodes[i])
21599 * cleanTableWidths.
21601 * Quite often pasting from word etc.. results in tables with column and widths.
21602 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21605 cleanTableWidths : function(node)
21610 this.cleanTableWidths(this.doc.body);
21615 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21618 Roo.log(node.tagName);
21619 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21620 this.iterateChildren(node, this.cleanTableWidths);
21623 if (node.hasAttribute('width')) {
21624 node.removeAttribute('width');
21628 if (node.hasAttribute("style")) {
21631 var styles = node.getAttribute("style").split(";");
21633 Roo.each(styles, function(s) {
21634 if (!s.match(/:/)) {
21637 var kv = s.split(":");
21638 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21641 // what ever is left... we allow.
21644 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21645 if (!nstyle.length) {
21646 node.removeAttribute('style');
21650 this.iterateChildren(node, this.cleanTableWidths);
21658 domToHTML : function(currentElement, depth, nopadtext) {
21660 depth = depth || 0;
21661 nopadtext = nopadtext || false;
21663 if (!currentElement) {
21664 return this.domToHTML(this.doc.body);
21667 //Roo.log(currentElement);
21669 var allText = false;
21670 var nodeName = currentElement.nodeName;
21671 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21673 if (nodeName == '#text') {
21675 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21680 if (nodeName != 'BODY') {
21683 // Prints the node tagName, such as <A>, <IMG>, etc
21686 for(i = 0; i < currentElement.attributes.length;i++) {
21688 var aname = currentElement.attributes.item(i).name;
21689 if (!currentElement.attributes.item(i).value.length) {
21692 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21695 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21704 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21707 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21712 // Traverse the tree
21714 var currentElementChild = currentElement.childNodes.item(i);
21715 var allText = true;
21716 var innerHTML = '';
21718 while (currentElementChild) {
21719 // Formatting code (indent the tree so it looks nice on the screen)
21720 var nopad = nopadtext;
21721 if (lastnode == 'SPAN') {
21725 if (currentElementChild.nodeName == '#text') {
21726 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21727 toadd = nopadtext ? toadd : toadd.trim();
21728 if (!nopad && toadd.length > 80) {
21729 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21731 innerHTML += toadd;
21734 currentElementChild = currentElement.childNodes.item(i);
21740 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21742 // Recursively traverse the tree structure of the child node
21743 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21744 lastnode = currentElementChild.nodeName;
21746 currentElementChild=currentElement.childNodes.item(i);
21752 // The remaining code is mostly for formatting the tree
21753 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21758 ret+= "</"+tagName+">";
21764 applyBlacklists : function()
21766 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21767 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21771 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21772 if (b.indexOf(tag) > -1) {
21775 this.white.push(tag);
21779 Roo.each(w, function(tag) {
21780 if (b.indexOf(tag) > -1) {
21783 if (this.white.indexOf(tag) > -1) {
21786 this.white.push(tag);
21791 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21792 if (w.indexOf(tag) > -1) {
21795 this.black.push(tag);
21799 Roo.each(b, function(tag) {
21800 if (w.indexOf(tag) > -1) {
21803 if (this.black.indexOf(tag) > -1) {
21806 this.black.push(tag);
21811 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21812 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21816 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21817 if (b.indexOf(tag) > -1) {
21820 this.cwhite.push(tag);
21824 Roo.each(w, function(tag) {
21825 if (b.indexOf(tag) > -1) {
21828 if (this.cwhite.indexOf(tag) > -1) {
21831 this.cwhite.push(tag);
21836 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21837 if (w.indexOf(tag) > -1) {
21840 this.cblack.push(tag);
21844 Roo.each(b, function(tag) {
21845 if (w.indexOf(tag) > -1) {
21848 if (this.cblack.indexOf(tag) > -1) {
21851 this.cblack.push(tag);
21856 setStylesheets : function(stylesheets)
21858 if(typeof(stylesheets) == 'string'){
21859 Roo.get(this.iframe.contentDocument.head).createChild({
21861 rel : 'stylesheet',
21870 Roo.each(stylesheets, function(s) {
21875 Roo.get(_this.iframe.contentDocument.head).createChild({
21877 rel : 'stylesheet',
21886 removeStylesheets : function()
21890 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21895 // hide stuff that is not compatible
21909 * @event specialkey
21913 * @cfg {String} fieldClass @hide
21916 * @cfg {String} focusClass @hide
21919 * @cfg {String} autoCreate @hide
21922 * @cfg {String} inputType @hide
21925 * @cfg {String} invalidClass @hide
21928 * @cfg {String} invalidText @hide
21931 * @cfg {String} msgFx @hide
21934 * @cfg {String} validateOnBlur @hide
21938 Roo.HtmlEditorCore.white = [
21939 'area', 'br', 'img', 'input', 'hr', 'wbr',
21941 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21942 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21943 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21944 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21945 'table', 'ul', 'xmp',
21947 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
21950 'dir', 'menu', 'ol', 'ul', 'dl',
21956 Roo.HtmlEditorCore.black = [
21957 // 'embed', 'object', // enable - backend responsiblity to clean thiese
21959 'base', 'basefont', 'bgsound', 'blink', 'body',
21960 'frame', 'frameset', 'head', 'html', 'ilayer',
21961 'iframe', 'layer', 'link', 'meta', 'object',
21962 'script', 'style' ,'title', 'xml' // clean later..
21964 Roo.HtmlEditorCore.clean = [
21965 'script', 'style', 'title', 'xml'
21967 Roo.HtmlEditorCore.remove = [
21972 Roo.HtmlEditorCore.ablack = [
21976 Roo.HtmlEditorCore.aclean = [
21977 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
21981 Roo.HtmlEditorCore.pwhite= [
21982 'http', 'https', 'mailto'
21985 // white listed style attributes.
21986 Roo.HtmlEditorCore.cwhite= [
21987 // 'text-align', /// default is to allow most things..
21993 // black listed style attributes.
21994 Roo.HtmlEditorCore.cblack= [
21995 // 'font-size' -- this can be set by the project
21999 Roo.HtmlEditorCore.swapCodes =[
22010 //<script type="text/javascript">
22013 * Ext JS Library 1.1.1
22014 * Copyright(c) 2006-2007, Ext JS, LLC.
22020 Roo.form.HtmlEditor = function(config){
22024 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22026 if (!this.toolbars) {
22027 this.toolbars = [];
22029 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22035 * @class Roo.form.HtmlEditor
22036 * @extends Roo.form.Field
22037 * Provides a lightweight HTML Editor component.
22039 * This has been tested on Fireforx / Chrome.. IE may not be so great..
22041 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22042 * supported by this editor.</b><br/><br/>
22043 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22044 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22046 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22048 * @cfg {Boolean} clearUp
22052 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22057 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22062 * @cfg {Number} height (in pixels)
22066 * @cfg {Number} width (in pixels)
22071 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22074 stylesheets: false,
22078 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22083 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22089 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22094 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22102 // private properties
22103 validationEvent : false,
22105 initialized : false,
22108 onFocus : Roo.emptyFn,
22110 hideMode:'offsets',
22112 actionMode : 'container', // defaults to hiding it...
22114 defaultAutoCreate : { // modified by initCompnoent..
22116 style:"width:500px;height:300px;",
22117 autocomplete: "new-password"
22121 initComponent : function(){
22124 * @event initialize
22125 * Fires when the editor is fully initialized (including the iframe)
22126 * @param {HtmlEditor} this
22131 * Fires when the editor is first receives the focus. Any insertion must wait
22132 * until after this event.
22133 * @param {HtmlEditor} this
22137 * @event beforesync
22138 * Fires before the textarea is updated with content from the editor iframe. Return false
22139 * to cancel the sync.
22140 * @param {HtmlEditor} this
22141 * @param {String} html
22145 * @event beforepush
22146 * Fires before the iframe editor is updated with content from the textarea. Return false
22147 * to cancel the push.
22148 * @param {HtmlEditor} this
22149 * @param {String} html
22154 * Fires when the textarea is updated with content from the editor iframe.
22155 * @param {HtmlEditor} this
22156 * @param {String} html
22161 * Fires when the iframe editor is updated with content from the textarea.
22162 * @param {HtmlEditor} this
22163 * @param {String} html
22167 * @event editmodechange
22168 * Fires when the editor switches edit modes
22169 * @param {HtmlEditor} this
22170 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22172 editmodechange: true,
22174 * @event editorevent
22175 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22176 * @param {HtmlEditor} this
22180 * @event firstfocus
22181 * Fires when on first focus - needed by toolbars..
22182 * @param {HtmlEditor} this
22187 * Auto save the htmlEditor value as a file into Events
22188 * @param {HtmlEditor} this
22192 * @event savedpreview
22193 * preview the saved version of htmlEditor
22194 * @param {HtmlEditor} this
22196 savedpreview: true,
22199 * @event stylesheetsclick
22200 * Fires when press the Sytlesheets button
22201 * @param {Roo.HtmlEditorCore} this
22203 stylesheetsclick: true
22205 this.defaultAutoCreate = {
22207 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22208 autocomplete: "new-password"
22213 * Protected method that will not generally be called directly. It
22214 * is called when the editor creates its toolbar. Override this method if you need to
22215 * add custom toolbar buttons.
22216 * @param {HtmlEditor} editor
22218 createToolbar : function(editor){
22219 Roo.log("create toolbars");
22220 if (!editor.toolbars || !editor.toolbars.length) {
22221 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22224 for (var i =0 ; i < editor.toolbars.length;i++) {
22225 editor.toolbars[i] = Roo.factory(
22226 typeof(editor.toolbars[i]) == 'string' ?
22227 { xtype: editor.toolbars[i]} : editor.toolbars[i],
22228 Roo.form.HtmlEditor);
22229 editor.toolbars[i].init(editor);
22237 onRender : function(ct, position)
22240 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22242 this.wrap = this.el.wrap({
22243 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22246 this.editorcore.onRender(ct, position);
22248 if (this.resizable) {
22249 this.resizeEl = new Roo.Resizable(this.wrap, {
22253 minHeight : this.height,
22254 height: this.height,
22255 handles : this.resizable,
22258 resize : function(r, w, h) {
22259 _t.onResize(w,h); // -something
22265 this.createToolbar(this);
22269 this.setSize(this.wrap.getSize());
22271 if (this.resizeEl) {
22272 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22273 // should trigger onReize..
22276 this.keyNav = new Roo.KeyNav(this.el, {
22278 "tab" : function(e){
22279 e.preventDefault();
22281 var value = this.getValue();
22283 var start = this.el.dom.selectionStart;
22284 var end = this.el.dom.selectionEnd;
22288 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22289 this.el.dom.setSelectionRange(end + 1, end + 1);
22293 var f = value.substring(0, start).split("\t");
22295 if(f.pop().length != 0){
22299 this.setValue(f.join("\t") + value.substring(end));
22300 this.el.dom.setSelectionRange(start - 1, start - 1);
22304 "home" : function(e){
22305 e.preventDefault();
22307 var curr = this.el.dom.selectionStart;
22308 var lines = this.getValue().split("\n");
22315 this.el.dom.setSelectionRange(0, 0);
22321 for (var i = 0; i < lines.length;i++) {
22322 pos += lines[i].length;
22332 pos -= lines[i].length;
22338 this.el.dom.setSelectionRange(pos, pos);
22342 this.el.dom.selectionStart = pos;
22343 this.el.dom.selectionEnd = curr;
22346 "end" : function(e){
22347 e.preventDefault();
22349 var curr = this.el.dom.selectionStart;
22350 var lines = this.getValue().split("\n");
22357 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22363 for (var i = 0; i < lines.length;i++) {
22365 pos += lines[i].length;
22379 this.el.dom.setSelectionRange(pos, pos);
22383 this.el.dom.selectionStart = curr;
22384 this.el.dom.selectionEnd = pos;
22389 doRelay : function(foo, bar, hname){
22390 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22396 // if(this.autosave && this.w){
22397 // this.autoSaveFn = setInterval(this.autosave, 1000);
22402 onResize : function(w, h)
22404 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22409 if(typeof w == 'number'){
22410 var aw = w - this.wrap.getFrameWidth('lr');
22411 this.el.setWidth(this.adjustWidth('textarea', aw));
22414 if(typeof h == 'number'){
22416 for (var i =0; i < this.toolbars.length;i++) {
22417 // fixme - ask toolbars for heights?
22418 tbh += this.toolbars[i].tb.el.getHeight();
22419 if (this.toolbars[i].footer) {
22420 tbh += this.toolbars[i].footer.el.getHeight();
22427 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22428 ah -= 5; // knock a few pixes off for look..
22430 this.el.setHeight(this.adjustWidth('textarea', ah));
22434 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22435 this.editorcore.onResize(ew,eh);
22440 * Toggles the editor between standard and source edit mode.
22441 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22443 toggleSourceEdit : function(sourceEditMode)
22445 this.editorcore.toggleSourceEdit(sourceEditMode);
22447 if(this.editorcore.sourceEditMode){
22448 Roo.log('editor - showing textarea');
22451 // Roo.log(this.syncValue());
22452 this.editorcore.syncValue();
22453 this.el.removeClass('x-hidden');
22454 this.el.dom.removeAttribute('tabIndex');
22457 for (var i = 0; i < this.toolbars.length; i++) {
22458 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22459 this.toolbars[i].tb.hide();
22460 this.toolbars[i].footer.hide();
22465 Roo.log('editor - hiding textarea');
22467 // Roo.log(this.pushValue());
22468 this.editorcore.pushValue();
22470 this.el.addClass('x-hidden');
22471 this.el.dom.setAttribute('tabIndex', -1);
22473 for (var i = 0; i < this.toolbars.length; i++) {
22474 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22475 this.toolbars[i].tb.show();
22476 this.toolbars[i].footer.show();
22480 //this.deferFocus();
22483 this.setSize(this.wrap.getSize());
22484 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22486 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22489 // private (for BoxComponent)
22490 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22492 // private (for BoxComponent)
22493 getResizeEl : function(){
22497 // private (for BoxComponent)
22498 getPositionEl : function(){
22503 initEvents : function(){
22504 this.originalValue = this.getValue();
22508 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22511 markInvalid : Roo.emptyFn,
22513 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22516 clearInvalid : Roo.emptyFn,
22518 setValue : function(v){
22519 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22520 this.editorcore.pushValue();
22525 deferFocus : function(){
22526 this.focus.defer(10, this);
22530 focus : function(){
22531 this.editorcore.focus();
22537 onDestroy : function(){
22543 for (var i =0; i < this.toolbars.length;i++) {
22544 // fixme - ask toolbars for heights?
22545 this.toolbars[i].onDestroy();
22548 this.wrap.dom.innerHTML = '';
22549 this.wrap.remove();
22554 onFirstFocus : function(){
22555 //Roo.log("onFirstFocus");
22556 this.editorcore.onFirstFocus();
22557 for (var i =0; i < this.toolbars.length;i++) {
22558 this.toolbars[i].onFirstFocus();
22564 syncValue : function()
22566 this.editorcore.syncValue();
22569 pushValue : function()
22571 this.editorcore.pushValue();
22574 setStylesheets : function(stylesheets)
22576 this.editorcore.setStylesheets(stylesheets);
22579 removeStylesheets : function()
22581 this.editorcore.removeStylesheets();
22585 // hide stuff that is not compatible
22599 * @event specialkey
22603 * @cfg {String} fieldClass @hide
22606 * @cfg {String} focusClass @hide
22609 * @cfg {String} autoCreate @hide
22612 * @cfg {String} inputType @hide
22615 * @cfg {String} invalidClass @hide
22618 * @cfg {String} invalidText @hide
22621 * @cfg {String} msgFx @hide
22624 * @cfg {String} validateOnBlur @hide
22628 // <script type="text/javascript">
22631 * Ext JS Library 1.1.1
22632 * Copyright(c) 2006-2007, Ext JS, LLC.
22638 * @class Roo.form.HtmlEditorToolbar1
22643 new Roo.form.HtmlEditor({
22646 new Roo.form.HtmlEditorToolbar1({
22647 disable : { fonts: 1 , format: 1, ..., ... , ...],
22653 * @cfg {Object} disable List of elements to disable..
22654 * @cfg {Array} btns List of additional buttons.
22658 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22661 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22664 Roo.apply(this, config);
22666 // default disabled, based on 'good practice'..
22667 this.disable = this.disable || {};
22668 Roo.applyIf(this.disable, {
22671 specialElements : true
22675 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22676 // dont call parent... till later.
22679 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype, {
22686 editorcore : false,
22688 * @cfg {Object} disable List of toolbar elements to disable
22695 * @cfg {String} createLinkText The default text for the create link prompt
22697 createLinkText : 'Please enter the URL for the link:',
22699 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22701 defaultLinkValue : 'http:/'+'/',
22705 * @cfg {Array} fontFamilies An array of available font families
22723 // "á" , ?? a acute?
22728 "°" // , // degrees
22730 // "é" , // e ecute
22731 // "ú" , // u ecute?
22734 specialElements : [
22736 text: "Insert Table",
22739 ihtml : '<table><tr><td>Cell</td></tr></table>'
22743 text: "Insert Image",
22746 ihtml : '<img src="about:blank"/>'
22755 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
22756 "input:submit", "input:button", "select", "textarea", "label" ],
22759 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
22761 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22769 * @cfg {String} defaultFont default font to use.
22771 defaultFont: 'tahoma',
22773 fontSelect : false,
22776 formatCombo : false,
22778 init : function(editor)
22780 this.editor = editor;
22781 this.editorcore = editor.editorcore ? editor.editorcore : editor;
22782 var editorcore = this.editorcore;
22786 var fid = editorcore.frameId;
22788 function btn(id, toggle, handler){
22789 var xid = fid + '-'+ id ;
22793 cls : 'x-btn-icon x-edit-'+id,
22794 enableToggle:toggle !== false,
22795 scope: _t, // was editor...
22796 handler:handler||_t.relayBtnCmd,
22797 clickEvent:'mousedown',
22798 tooltip: etb.buttonTips[id] || undefined, ///tips ???
22805 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
22807 // stop form submits
22808 tb.el.on('click', function(e){
22809 e.preventDefault(); // what does this do?
22812 if(!this.disable.font) { // && !Roo.isSafari){
22813 /* why no safari for fonts
22814 editor.fontSelect = tb.el.createChild({
22817 cls:'x-font-select',
22818 html: this.createFontOptions()
22821 editor.fontSelect.on('change', function(){
22822 var font = editor.fontSelect.dom.value;
22823 editor.relayCmd('fontname', font);
22824 editor.deferFocus();
22828 editor.fontSelect.dom,
22834 if(!this.disable.formats){
22835 this.formatCombo = new Roo.form.ComboBox({
22836 store: new Roo.data.SimpleStore({
22839 data : this.formats // from states.js
22843 //autoCreate : {tag: "div", size: "20"},
22844 displayField:'tag',
22848 triggerAction: 'all',
22849 emptyText:'Add tag',
22850 selectOnFocus:true,
22853 'select': function(c, r, i) {
22854 editorcore.insertTag(r.get('tag'));
22860 tb.addField(this.formatCombo);
22864 if(!this.disable.format){
22869 btn('strikethrough')
22872 if(!this.disable.fontSize){
22877 btn('increasefontsize', false, editorcore.adjustFont),
22878 btn('decreasefontsize', false, editorcore.adjustFont)
22883 if(!this.disable.colors){
22886 id:editorcore.frameId +'-forecolor',
22887 cls:'x-btn-icon x-edit-forecolor',
22888 clickEvent:'mousedown',
22889 tooltip: this.buttonTips['forecolor'] || undefined,
22891 menu : new Roo.menu.ColorMenu({
22892 allowReselect: true,
22893 focus: Roo.emptyFn,
22896 selectHandler: function(cp, color){
22897 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
22898 editor.deferFocus();
22901 clickEvent:'mousedown'
22904 id:editorcore.frameId +'backcolor',
22905 cls:'x-btn-icon x-edit-backcolor',
22906 clickEvent:'mousedown',
22907 tooltip: this.buttonTips['backcolor'] || undefined,
22909 menu : new Roo.menu.ColorMenu({
22910 focus: Roo.emptyFn,
22913 allowReselect: true,
22914 selectHandler: function(cp, color){
22916 editorcore.execCmd('useCSS', false);
22917 editorcore.execCmd('hilitecolor', color);
22918 editorcore.execCmd('useCSS', true);
22919 editor.deferFocus();
22921 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
22922 Roo.isSafari || Roo.isIE ? '#'+color : color);
22923 editor.deferFocus();
22927 clickEvent:'mousedown'
22932 // now add all the items...
22935 if(!this.disable.alignments){
22938 btn('justifyleft'),
22939 btn('justifycenter'),
22940 btn('justifyright')
22944 //if(!Roo.isSafari){
22945 if(!this.disable.links){
22948 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
22952 if(!this.disable.lists){
22955 btn('insertorderedlist'),
22956 btn('insertunorderedlist')
22959 if(!this.disable.sourceEdit){
22962 btn('sourceedit', true, function(btn){
22963 this.toggleSourceEdit(btn.pressed);
22970 // special menu.. - needs to be tidied up..
22971 if (!this.disable.special) {
22974 cls: 'x-edit-none',
22980 for (var i =0; i < this.specialChars.length; i++) {
22981 smenu.menu.items.push({
22983 html: this.specialChars[i],
22984 handler: function(a,b) {
22985 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
22986 //editor.insertAtCursor(a.html);
23000 if (!this.disable.cleanStyles) {
23002 cls: 'x-btn-icon x-btn-clear',
23008 for (var i =0; i < this.cleanStyles.length; i++) {
23009 cmenu.menu.items.push({
23010 actiontype : this.cleanStyles[i],
23011 html: 'Remove ' + this.cleanStyles[i],
23012 handler: function(a,b) {
23015 var c = Roo.get(editorcore.doc.body);
23016 c.select('[style]').each(function(s) {
23017 s.dom.style.removeProperty(a.actiontype);
23019 editorcore.syncValue();
23024 cmenu.menu.items.push({
23025 actiontype : 'tablewidths',
23026 html: 'Remove Table Widths',
23027 handler: function(a,b) {
23028 editorcore.cleanTableWidths();
23029 editorcore.syncValue();
23033 cmenu.menu.items.push({
23034 actiontype : 'word',
23035 html: 'Remove MS Word Formating',
23036 handler: function(a,b) {
23037 editorcore.cleanWord();
23038 editorcore.syncValue();
23043 cmenu.menu.items.push({
23044 actiontype : 'all',
23045 html: 'Remove All Styles',
23046 handler: function(a,b) {
23048 var c = Roo.get(editorcore.doc.body);
23049 c.select('[style]').each(function(s) {
23050 s.dom.removeAttribute('style');
23052 editorcore.syncValue();
23057 cmenu.menu.items.push({
23058 actiontype : 'all',
23059 html: 'Remove All CSS Classes',
23060 handler: function(a,b) {
23062 var c = Roo.get(editorcore.doc.body);
23063 c.select('[class]').each(function(s) {
23064 s.dom.className = '';
23066 editorcore.syncValue();
23071 cmenu.menu.items.push({
23072 actiontype : 'tidy',
23073 html: 'Tidy HTML Source',
23074 handler: function(a,b) {
23075 editorcore.doc.body.innerHTML = editorcore.domToHTML();
23076 editorcore.syncValue();
23085 if (!this.disable.specialElements) {
23088 cls: 'x-edit-none',
23093 for (var i =0; i < this.specialElements.length; i++) {
23094 semenu.menu.items.push(
23096 handler: function(a,b) {
23097 editor.insertAtCursor(this.ihtml);
23099 }, this.specialElements[i])
23111 for(var i =0; i< this.btns.length;i++) {
23112 var b = Roo.factory(this.btns[i],Roo.form);
23113 b.cls = 'x-edit-none';
23115 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23116 b.cls += ' x-init-enable';
23119 b.scope = editorcore;
23127 // disable everything...
23129 this.tb.items.each(function(item){
23132 item.id != editorcore.frameId+ '-sourceedit' &&
23133 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23139 this.rendered = true;
23141 // the all the btns;
23142 editor.on('editorevent', this.updateToolbar, this);
23143 // other toolbars need to implement this..
23144 //editor.on('editmodechange', this.updateToolbar, this);
23148 relayBtnCmd : function(btn) {
23149 this.editorcore.relayCmd(btn.cmd);
23151 // private used internally
23152 createLink : function(){
23153 Roo.log("create link?");
23154 var url = prompt(this.createLinkText, this.defaultLinkValue);
23155 if(url && url != 'http:/'+'/'){
23156 this.editorcore.relayCmd('createlink', url);
23162 * Protected method that will not generally be called directly. It triggers
23163 * a toolbar update by reading the markup state of the current selection in the editor.
23165 updateToolbar: function(){
23167 if(!this.editorcore.activated){
23168 this.editor.onFirstFocus();
23172 var btns = this.tb.items.map,
23173 doc = this.editorcore.doc,
23174 frameId = this.editorcore.frameId;
23176 if(!this.disable.font && !Roo.isSafari){
23178 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23179 if(name != this.fontSelect.dom.value){
23180 this.fontSelect.dom.value = name;
23184 if(!this.disable.format){
23185 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23186 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23187 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23188 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23190 if(!this.disable.alignments){
23191 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23192 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23193 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23195 if(!Roo.isSafari && !this.disable.lists){
23196 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23197 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23200 var ans = this.editorcore.getAllAncestors();
23201 if (this.formatCombo) {
23204 var store = this.formatCombo.store;
23205 this.formatCombo.setValue("");
23206 for (var i =0; i < ans.length;i++) {
23207 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23209 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23217 // hides menus... - so this cant be on a menu...
23218 Roo.menu.MenuMgr.hideAll();
23220 //this.editorsyncValue();
23224 createFontOptions : function(){
23225 var buf = [], fs = this.fontFamilies, ff, lc;
23229 for(var i = 0, len = fs.length; i< len; i++){
23231 lc = ff.toLowerCase();
23233 '<option value="',lc,'" style="font-family:',ff,';"',
23234 (this.defaultFont == lc ? ' selected="true">' : '>'),
23239 return buf.join('');
23242 toggleSourceEdit : function(sourceEditMode){
23244 Roo.log("toolbar toogle");
23245 if(sourceEditMode === undefined){
23246 sourceEditMode = !this.sourceEditMode;
23248 this.sourceEditMode = sourceEditMode === true;
23249 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23250 // just toggle the button?
23251 if(btn.pressed !== this.sourceEditMode){
23252 btn.toggle(this.sourceEditMode);
23256 if(sourceEditMode){
23257 Roo.log("disabling buttons");
23258 this.tb.items.each(function(item){
23259 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23265 Roo.log("enabling buttons");
23266 if(this.editorcore.initialized){
23267 this.tb.items.each(function(item){
23273 Roo.log("calling toggole on editor");
23274 // tell the editor that it's been pressed..
23275 this.editor.toggleSourceEdit(sourceEditMode);
23279 * Object collection of toolbar tooltips for the buttons in the editor. The key
23280 * is the command id associated with that button and the value is a valid QuickTips object.
23285 title: 'Bold (Ctrl+B)',
23286 text: 'Make the selected text bold.',
23287 cls: 'x-html-editor-tip'
23290 title: 'Italic (Ctrl+I)',
23291 text: 'Make the selected text italic.',
23292 cls: 'x-html-editor-tip'
23300 title: 'Bold (Ctrl+B)',
23301 text: 'Make the selected text bold.',
23302 cls: 'x-html-editor-tip'
23305 title: 'Italic (Ctrl+I)',
23306 text: 'Make the selected text italic.',
23307 cls: 'x-html-editor-tip'
23310 title: 'Underline (Ctrl+U)',
23311 text: 'Underline the selected text.',
23312 cls: 'x-html-editor-tip'
23315 title: 'Strikethrough',
23316 text: 'Strikethrough the selected text.',
23317 cls: 'x-html-editor-tip'
23319 increasefontsize : {
23320 title: 'Grow Text',
23321 text: 'Increase the font size.',
23322 cls: 'x-html-editor-tip'
23324 decreasefontsize : {
23325 title: 'Shrink Text',
23326 text: 'Decrease the font size.',
23327 cls: 'x-html-editor-tip'
23330 title: 'Text Highlight Color',
23331 text: 'Change the background color of the selected text.',
23332 cls: 'x-html-editor-tip'
23335 title: 'Font Color',
23336 text: 'Change the color of the selected text.',
23337 cls: 'x-html-editor-tip'
23340 title: 'Align Text Left',
23341 text: 'Align text to the left.',
23342 cls: 'x-html-editor-tip'
23345 title: 'Center Text',
23346 text: 'Center text in the editor.',
23347 cls: 'x-html-editor-tip'
23350 title: 'Align Text Right',
23351 text: 'Align text to the right.',
23352 cls: 'x-html-editor-tip'
23354 insertunorderedlist : {
23355 title: 'Bullet List',
23356 text: 'Start a bulleted list.',
23357 cls: 'x-html-editor-tip'
23359 insertorderedlist : {
23360 title: 'Numbered List',
23361 text: 'Start a numbered list.',
23362 cls: 'x-html-editor-tip'
23365 title: 'Hyperlink',
23366 text: 'Make the selected text a hyperlink.',
23367 cls: 'x-html-editor-tip'
23370 title: 'Source Edit',
23371 text: 'Switch to source editing mode.',
23372 cls: 'x-html-editor-tip'
23376 onDestroy : function(){
23379 this.tb.items.each(function(item){
23381 item.menu.removeAll();
23383 item.menu.el.destroy();
23391 onFirstFocus: function() {
23392 this.tb.items.each(function(item){
23401 // <script type="text/javascript">
23404 * Ext JS Library 1.1.1
23405 * Copyright(c) 2006-2007, Ext JS, LLC.
23412 * @class Roo.form.HtmlEditor.ToolbarContext
23417 new Roo.form.HtmlEditor({
23420 { xtype: 'ToolbarStandard', styles : {} }
23421 { xtype: 'ToolbarContext', disable : {} }
23427 * @config : {Object} disable List of elements to disable.. (not done yet.)
23428 * @config : {Object} styles Map of styles available.
23432 Roo.form.HtmlEditor.ToolbarContext = function(config)
23435 Roo.apply(this, config);
23436 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23437 // dont call parent... till later.
23438 this.styles = this.styles || {};
23443 Roo.form.HtmlEditor.ToolbarContext.types = {
23455 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23521 opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23526 opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23536 style : 'fontFamily',
23537 displayField: 'display',
23538 optname : 'font-family',
23587 // should we really allow this??
23588 // should this just be
23599 style : 'fontFamily',
23600 displayField: 'display',
23601 optname : 'font-family',
23608 style : 'fontFamily',
23609 displayField: 'display',
23610 optname : 'font-family',
23617 style : 'fontFamily',
23618 displayField: 'display',
23619 optname : 'font-family',
23630 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23631 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23633 Roo.form.HtmlEditor.ToolbarContext.options = {
23635 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23636 [ 'Courier New', 'Courier New'],
23637 [ 'Tahoma', 'Tahoma'],
23638 [ 'Times New Roman,serif', 'Times'],
23639 [ 'Verdana','Verdana' ]
23643 // fixme - these need to be configurable..
23646 //Roo.form.HtmlEditor.ToolbarContext.types
23649 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
23656 editorcore : false,
23658 * @cfg {Object} disable List of toolbar elements to disable
23663 * @cfg {Object} styles List of styles
23664 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
23666 * These must be defined in the page, so they get rendered correctly..
23677 init : function(editor)
23679 this.editor = editor;
23680 this.editorcore = editor.editorcore ? editor.editorcore : editor;
23681 var editorcore = this.editorcore;
23683 var fid = editorcore.frameId;
23685 function btn(id, toggle, handler){
23686 var xid = fid + '-'+ id ;
23690 cls : 'x-btn-icon x-edit-'+id,
23691 enableToggle:toggle !== false,
23692 scope: editorcore, // was editor...
23693 handler:handler||editorcore.relayBtnCmd,
23694 clickEvent:'mousedown',
23695 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23699 // create a new element.
23700 var wdiv = editor.wrap.createChild({
23702 }, editor.wrap.dom.firstChild.nextSibling, true);
23704 // can we do this more than once??
23706 // stop form submits
23709 // disable everything...
23710 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23711 this.toolbars = {};
23713 for (var i in ty) {
23715 this.toolbars[i] = this.buildToolbar(ty[i],i);
23717 this.tb = this.toolbars.BODY;
23719 this.buildFooter();
23720 this.footer.show();
23721 editor.on('hide', function( ) { this.footer.hide() }, this);
23722 editor.on('show', function( ) { this.footer.show() }, this);
23725 this.rendered = true;
23727 // the all the btns;
23728 editor.on('editorevent', this.updateToolbar, this);
23729 // other toolbars need to implement this..
23730 //editor.on('editmodechange', this.updateToolbar, this);
23736 * Protected method that will not generally be called directly. It triggers
23737 * a toolbar update by reading the markup state of the current selection in the editor.
23739 * Note you can force an update by calling on('editorevent', scope, false)
23741 updateToolbar: function(editor,ev,sel){
23744 // capture mouse up - this is handy for selecting images..
23745 // perhaps should go somewhere else...
23746 if(!this.editorcore.activated){
23747 this.editor.onFirstFocus();
23753 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23754 // selectNode - might want to handle IE?
23756 (ev.type == 'mouseup' || ev.type == 'click' ) &&
23757 ev.target && ev.target.tagName == 'IMG') {
23758 // they have click on an image...
23759 // let's see if we can change the selection...
23762 var nodeRange = sel.ownerDocument.createRange();
23764 nodeRange.selectNode(sel);
23766 nodeRange.selectNodeContents(sel);
23768 //nodeRange.collapse(true);
23769 var s = this.editorcore.win.getSelection();
23770 s.removeAllRanges();
23771 s.addRange(nodeRange);
23775 var updateFooter = sel ? false : true;
23778 var ans = this.editorcore.getAllAncestors();
23781 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23784 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
23785 sel = sel ? sel : this.editorcore.doc.body;
23786 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
23789 // pick a menu that exists..
23790 var tn = sel.tagName.toUpperCase();
23791 //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
23793 tn = sel.tagName.toUpperCase();
23795 var lastSel = this.tb.selectedNode;
23797 this.tb.selectedNode = sel;
23799 // if current menu does not match..
23801 if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
23804 ///console.log("show: " + tn);
23805 this.tb = typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
23808 this.tb.items.first().el.innerHTML = tn + ': ';
23811 // update attributes
23812 if (this.tb.fields) {
23813 this.tb.fields.each(function(e) {
23815 e.setValue(sel.style[e.stylename]);
23818 e.setValue(sel.getAttribute(e.attrname));
23822 var hasStyles = false;
23823 for(var i in this.styles) {
23830 var st = this.tb.fields.item(0);
23832 st.store.removeAll();
23835 var cn = sel.className.split(/\s+/);
23838 if (this.styles['*']) {
23840 Roo.each(this.styles['*'], function(v) {
23841 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
23844 if (this.styles[tn]) {
23845 Roo.each(this.styles[tn], function(v) {
23846 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
23850 st.store.loadData(avs);
23854 // flag our selected Node.
23855 this.tb.selectedNode = sel;
23858 Roo.menu.MenuMgr.hideAll();
23862 if (!updateFooter) {
23863 //this.footDisp.dom.innerHTML = '';
23866 // update the footer
23870 this.footerEls = ans.reverse();
23871 Roo.each(this.footerEls, function(a,i) {
23872 if (!a) { return; }
23873 html += html.length ? ' > ' : '';
23875 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
23880 var sz = this.footDisp.up('td').getSize();
23881 this.footDisp.dom.style.width = (sz.width -10) + 'px';
23882 this.footDisp.dom.style.marginLeft = '5px';
23884 this.footDisp.dom.style.overflow = 'hidden';
23886 this.footDisp.dom.innerHTML = html;
23888 //this.editorsyncValue();
23895 onDestroy : function(){
23898 this.tb.items.each(function(item){
23900 item.menu.removeAll();
23902 item.menu.el.destroy();
23910 onFirstFocus: function() {
23911 // need to do this for all the toolbars..
23912 this.tb.items.each(function(item){
23916 buildToolbar: function(tlist, nm)
23918 var editor = this.editor;
23919 var editorcore = this.editorcore;
23920 // create a new element.
23921 var wdiv = editor.wrap.createChild({
23923 }, editor.wrap.dom.firstChild.nextSibling, true);
23926 var tb = new Roo.Toolbar(wdiv);
23929 tb.add(nm+ ": ");
23932 for(var i in this.styles) {
23937 if (styles && styles.length) {
23939 // this needs a multi-select checkbox...
23940 tb.addField( new Roo.form.ComboBox({
23941 store: new Roo.data.SimpleStore({
23943 fields: ['val', 'selected'],
23946 name : '-roo-edit-className',
23947 attrname : 'className',
23948 displayField: 'val',
23952 triggerAction: 'all',
23953 emptyText:'Select Style',
23954 selectOnFocus:true,
23957 'select': function(c, r, i) {
23958 // initial support only for on class per el..
23959 tb.selectedNode.className = r ? r.get('val') : '';
23960 editorcore.syncValue();
23967 var tbc = Roo.form.HtmlEditor.ToolbarContext;
23968 var tbops = tbc.options;
23970 for (var i in tlist) {
23972 var item = tlist[i];
23973 tb.add(item.title + ": ");
23976 //optname == used so you can configure the options available..
23977 var opts = item.opts ? item.opts : false;
23978 if (item.optname) {
23979 opts = tbops[item.optname];
23984 // opts == pulldown..
23985 tb.addField( new Roo.form.ComboBox({
23986 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
23988 fields: ['val', 'display'],
23991 name : '-roo-edit-' + i,
23993 stylename : item.style ? item.style : false,
23994 displayField: item.displayField ? item.displayField : 'val',
23995 valueField : 'val',
23997 mode: typeof(tbc.stores[i]) != 'undefined' ? 'remote' : 'local',
23999 triggerAction: 'all',
24000 emptyText:'Select',
24001 selectOnFocus:true,
24002 width: item.width ? item.width : 130,
24004 'select': function(c, r, i) {
24006 tb.selectedNode.style[c.stylename] = r.get('val');
24009 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24018 tb.addField( new Roo.form.TextField({
24021 //allowBlank:false,
24026 tb.addField( new Roo.form.TextField({
24027 name: '-roo-edit-' + i,
24034 'change' : function(f, nv, ov) {
24035 tb.selectedNode.setAttribute(f.attrname, nv);
24036 editorcore.syncValue();
24049 text: 'Stylesheets',
24052 click : function ()
24054 _this.editor.fireEvent('stylesheetsclick', _this.editor);
24062 text: 'Remove Tag',
24065 click : function ()
24068 // undo does not work.
24070 var sn = tb.selectedNode;
24072 var pn = sn.parentNode;
24074 var stn = sn.childNodes[0];
24075 var en = sn.childNodes[sn.childNodes.length - 1 ];
24076 while (sn.childNodes.length) {
24077 var node = sn.childNodes[0];
24078 sn.removeChild(node);
24080 pn.insertBefore(node, sn);
24083 pn.removeChild(sn);
24084 var range = editorcore.createRange();
24086 range.setStart(stn,0);
24087 range.setEnd(en,0); //????
24088 //range.selectNode(sel);
24091 var selection = editorcore.getSelection();
24092 selection.removeAllRanges();
24093 selection.addRange(range);
24097 //_this.updateToolbar(null, null, pn);
24098 _this.updateToolbar(null, null, null);
24099 _this.footDisp.dom.innerHTML = '';
24109 tb.el.on('click', function(e){
24110 e.preventDefault(); // what does this do?
24112 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24115 // dont need to disable them... as they will get hidden
24120 buildFooter : function()
24123 var fel = this.editor.wrap.createChild();
24124 this.footer = new Roo.Toolbar(fel);
24125 // toolbar has scrolly on left / right?
24126 var footDisp= new Roo.Toolbar.Fill();
24132 handler : function() {
24133 _t.footDisp.scrollTo('left',0,true)
24137 this.footer.add( footDisp );
24142 handler : function() {
24144 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24148 var fel = Roo.get(footDisp.el);
24149 fel.addClass('x-editor-context');
24150 this.footDispWrap = fel;
24151 this.footDispWrap.overflow = 'hidden';
24153 this.footDisp = fel.createChild();
24154 this.footDispWrap.on('click', this.onContextClick, this)
24158 onContextClick : function (ev,dom)
24160 ev.preventDefault();
24161 var cn = dom.className;
24163 if (!cn.match(/x-ed-loc-/)) {
24166 var n = cn.split('-').pop();
24167 var ans = this.footerEls;
24171 var range = this.editorcore.createRange();
24173 range.selectNodeContents(sel);
24174 //range.selectNode(sel);
24177 var selection = this.editorcore.getSelection();
24178 selection.removeAllRanges();
24179 selection.addRange(range);
24183 this.updateToolbar(null, null, sel);
24200 * Ext JS Library 1.1.1
24201 * Copyright(c) 2006-2007, Ext JS, LLC.
24203 * Originally Released Under LGPL - original licence link has changed is not relivant.
24206 * <script type="text/javascript">
24210 * @class Roo.form.BasicForm
24211 * @extends Roo.util.Observable
24212 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24214 * @param {String/HTMLElement/Roo.Element} el The form element or its id
24215 * @param {Object} config Configuration options
24217 Roo.form.BasicForm = function(el, config){
24218 this.allItems = [];
24219 this.childForms = [];
24220 Roo.apply(this, config);
24222 * The Roo.form.Field items in this form.
24223 * @type MixedCollection
24227 this.items = new Roo.util.MixedCollection(false, function(o){
24228 return o.id || (o.id = Roo.id());
24232 * @event beforeaction
24233 * Fires before any action is performed. Return false to cancel the action.
24234 * @param {Form} this
24235 * @param {Action} action The action to be performed
24237 beforeaction: true,
24239 * @event actionfailed
24240 * Fires when an action fails.
24241 * @param {Form} this
24242 * @param {Action} action The action that failed
24244 actionfailed : true,
24246 * @event actioncomplete
24247 * Fires when an action is completed.
24248 * @param {Form} this
24249 * @param {Action} action The action that completed
24251 actioncomplete : true
24256 Roo.form.BasicForm.superclass.constructor.call(this);
24259 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24261 * @cfg {String} method
24262 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24265 * @cfg {DataReader} reader
24266 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24267 * This is optional as there is built-in support for processing JSON.
24270 * @cfg {DataReader} errorReader
24271 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24272 * This is completely optional as there is built-in support for processing JSON.
24275 * @cfg {String} url
24276 * The URL to use for form actions if one isn't supplied in the action options.
24279 * @cfg {Boolean} fileUpload
24280 * Set to true if this form is a file upload.
24284 * @cfg {Object} baseParams
24285 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24290 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24295 activeAction : null,
24298 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24299 * or setValues() data instead of when the form was first created.
24301 trackResetOnLoad : false,
24305 * childForms - used for multi-tab forms
24308 childForms : false,
24311 * allItems - full list of fields.
24317 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24318 * element by passing it or its id or mask the form itself by passing in true.
24321 waitMsgTarget : false,
24324 initEl : function(el){
24325 this.el = Roo.get(el);
24326 this.id = this.el.id || Roo.id();
24327 this.el.on('submit', this.onSubmit, this);
24328 this.el.addClass('x-form');
24332 onSubmit : function(e){
24337 * Returns true if client-side validation on the form is successful.
24340 isValid : function(){
24342 this.items.each(function(f){
24351 * DEPRICATED Returns true if any fields in this form have changed since their original load.
24354 isDirty : function(){
24356 this.items.each(function(f){
24366 * Returns true if any fields in this form have changed since their original load. (New version)
24370 hasChanged : function()
24373 this.items.each(function(f){
24374 if(f.hasChanged()){
24383 * Resets all hasChanged to 'false' -
24384 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24385 * So hasChanged storage is only to be used for this purpose
24388 resetHasChanged : function()
24390 this.items.each(function(f){
24391 f.resetHasChanged();
24398 * Performs a predefined action (submit or load) or custom actions you define on this form.
24399 * @param {String} actionName The name of the action type
24400 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
24401 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24402 * accept other config options):
24404 Property Type Description
24405 ---------------- --------------- ----------------------------------------------------------------------------------
24406 url String The url for the action (defaults to the form's url)
24407 method String The form method to use (defaults to the form's method, or POST if not defined)
24408 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
24409 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
24410 validate the form on the client (defaults to false)
24412 * @return {BasicForm} this
24414 doAction : function(action, options){
24415 if(typeof action == 'string'){
24416 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24418 if(this.fireEvent('beforeaction', this, action) !== false){
24419 this.beforeAction(action);
24420 action.run.defer(100, action);
24426 * Shortcut to do a submit action.
24427 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24428 * @return {BasicForm} this
24430 submit : function(options){
24431 this.doAction('submit', options);
24436 * Shortcut to do a load action.
24437 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24438 * @return {BasicForm} this
24440 load : function(options){
24441 this.doAction('load', options);
24446 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24447 * @param {Record} record The record to edit
24448 * @return {BasicForm} this
24450 updateRecord : function(record){
24451 record.beginEdit();
24452 var fs = record.fields;
24453 fs.each(function(f){
24454 var field = this.findField(f.name);
24456 record.set(f.name, field.getValue());
24464 * Loads an Roo.data.Record into this form.
24465 * @param {Record} record The record to load
24466 * @return {BasicForm} this
24468 loadRecord : function(record){
24469 this.setValues(record.data);
24474 beforeAction : function(action){
24475 var o = action.options;
24478 if(this.waitMsgTarget === true){
24479 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24480 }else if(this.waitMsgTarget){
24481 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24482 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24484 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24490 afterAction : function(action, success){
24491 this.activeAction = null;
24492 var o = action.options;
24494 if(this.waitMsgTarget === true){
24496 }else if(this.waitMsgTarget){
24497 this.waitMsgTarget.unmask();
24499 Roo.MessageBox.updateProgress(1);
24500 Roo.MessageBox.hide();
24507 Roo.callback(o.success, o.scope, [this, action]);
24508 this.fireEvent('actioncomplete', this, action);
24512 // failure condition..
24513 // we have a scenario where updates need confirming.
24514 // eg. if a locking scenario exists..
24515 // we look for { errors : { needs_confirm : true }} in the response.
24517 (typeof(action.result) != 'undefined') &&
24518 (typeof(action.result.errors) != 'undefined') &&
24519 (typeof(action.result.errors.needs_confirm) != 'undefined')
24522 Roo.MessageBox.confirm(
24523 "Change requires confirmation",
24524 action.result.errorMsg,
24529 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
24539 Roo.callback(o.failure, o.scope, [this, action]);
24540 // show an error message if no failed handler is set..
24541 if (!this.hasListener('actionfailed')) {
24542 Roo.MessageBox.alert("Error",
24543 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24544 action.result.errorMsg :
24545 "Saving Failed, please check your entries or try again"
24549 this.fireEvent('actionfailed', this, action);
24555 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24556 * @param {String} id The value to search for
24559 findField : function(id){
24560 var field = this.items.get(id);
24562 this.items.each(function(f){
24563 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24569 return field || null;
24573 * Add a secondary form to this one,
24574 * Used to provide tabbed forms. One form is primary, with hidden values
24575 * which mirror the elements from the other forms.
24577 * @param {Roo.form.Form} form to add.
24580 addForm : function(form)
24583 if (this.childForms.indexOf(form) > -1) {
24587 this.childForms.push(form);
24589 Roo.each(form.allItems, function (fe) {
24591 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24592 if (this.findField(n)) { // already added..
24595 var add = new Roo.form.Hidden({
24598 add.render(this.el);
24605 * Mark fields in this form invalid in bulk.
24606 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24607 * @return {BasicForm} this
24609 markInvalid : function(errors){
24610 if(errors instanceof Array){
24611 for(var i = 0, len = errors.length; i < len; i++){
24612 var fieldError = errors[i];
24613 var f = this.findField(fieldError.id);
24615 f.markInvalid(fieldError.msg);
24621 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24622 field.markInvalid(errors[id]);
24626 Roo.each(this.childForms || [], function (f) {
24627 f.markInvalid(errors);
24634 * Set values for fields in this form in bulk.
24635 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24636 * @return {BasicForm} this
24638 setValues : function(values){
24639 if(values instanceof Array){ // array of objects
24640 for(var i = 0, len = values.length; i < len; i++){
24642 var f = this.findField(v.id);
24644 f.setValue(v.value);
24645 if(this.trackResetOnLoad){
24646 f.originalValue = f.getValue();
24650 }else{ // object hash
24653 if(typeof values[id] != 'function' && (field = this.findField(id))){
24655 if (field.setFromData &&
24656 field.valueField &&
24657 field.displayField &&
24658 // combos' with local stores can
24659 // be queried via setValue()
24660 // to set their value..
24661 (field.store && !field.store.isLocal)
24665 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24666 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24667 field.setFromData(sd);
24670 field.setValue(values[id]);
24674 if(this.trackResetOnLoad){
24675 field.originalValue = field.getValue();
24680 this.resetHasChanged();
24683 Roo.each(this.childForms || [], function (f) {
24684 f.setValues(values);
24685 f.resetHasChanged();
24692 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24693 * they are returned as an array.
24694 * @param {Boolean} asString
24697 getValues : function(asString){
24698 if (this.childForms) {
24699 // copy values from the child forms
24700 Roo.each(this.childForms, function (f) {
24701 this.setValues(f.getValues());
24707 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24708 if(asString === true){
24711 return Roo.urlDecode(fs);
24715 * Returns the fields in this form as an object with key/value pairs.
24716 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24719 getFieldValues : function(with_hidden)
24721 if (this.childForms) {
24722 // copy values from the child forms
24723 // should this call getFieldValues - probably not as we do not currently copy
24724 // hidden fields when we generate..
24725 Roo.each(this.childForms, function (f) {
24726 this.setValues(f.getValues());
24731 this.items.each(function(f){
24732 if (!f.getName()) {
24735 var v = f.getValue();
24736 if (f.inputType =='radio') {
24737 if (typeof(ret[f.getName()]) == 'undefined') {
24738 ret[f.getName()] = ''; // empty..
24741 if (!f.el.dom.checked) {
24745 v = f.el.dom.value;
24749 // not sure if this supported any more..
24750 if ((typeof(v) == 'object') && f.getRawValue) {
24751 v = f.getRawValue() ; // dates..
24753 // combo boxes where name != hiddenName...
24754 if (f.name != f.getName()) {
24755 ret[f.name] = f.getRawValue();
24757 ret[f.getName()] = v;
24764 * Clears all invalid messages in this form.
24765 * @return {BasicForm} this
24767 clearInvalid : function(){
24768 this.items.each(function(f){
24772 Roo.each(this.childForms || [], function (f) {
24781 * Resets this form.
24782 * @return {BasicForm} this
24784 reset : function(){
24785 this.items.each(function(f){
24789 Roo.each(this.childForms || [], function (f) {
24792 this.resetHasChanged();
24798 * Add Roo.form components to this form.
24799 * @param {Field} field1
24800 * @param {Field} field2 (optional)
24801 * @param {Field} etc (optional)
24802 * @return {BasicForm} this
24805 this.items.addAll(Array.prototype.slice.call(arguments, 0));
24811 * Removes a field from the items collection (does NOT remove its markup).
24812 * @param {Field} field
24813 * @return {BasicForm} this
24815 remove : function(field){
24816 this.items.remove(field);
24821 * Looks at the fields in this form, checks them for an id attribute,
24822 * and calls applyTo on the existing dom element with that id.
24823 * @return {BasicForm} this
24825 render : function(){
24826 this.items.each(function(f){
24827 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
24835 * Calls {@link Ext#apply} for all fields in this form with the passed object.
24836 * @param {Object} values
24837 * @return {BasicForm} this
24839 applyToFields : function(o){
24840 this.items.each(function(f){
24847 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
24848 * @param {Object} values
24849 * @return {BasicForm} this
24851 applyIfToFields : function(o){
24852 this.items.each(function(f){
24860 Roo.BasicForm = Roo.form.BasicForm;/*
24862 * Ext JS Library 1.1.1
24863 * Copyright(c) 2006-2007, Ext JS, LLC.
24865 * Originally Released Under LGPL - original licence link has changed is not relivant.
24868 * <script type="text/javascript">
24872 * @class Roo.form.Form
24873 * @extends Roo.form.BasicForm
24874 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
24876 * @param {Object} config Configuration options
24878 Roo.form.Form = function(config){
24880 if (config.items) {
24881 xitems = config.items;
24882 delete config.items;
24886 Roo.form.Form.superclass.constructor.call(this, null, config);
24887 this.url = this.url || this.action;
24889 this.root = new Roo.form.Layout(Roo.applyIf({
24893 this.active = this.root;
24895 * Array of all the buttons that have been added to this form via {@link addButton}
24899 this.allItems = [];
24902 * @event clientvalidation
24903 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
24904 * @param {Form} this
24905 * @param {Boolean} valid true if the form has passed client-side validation
24907 clientvalidation: true,
24910 * Fires when the form is rendered
24911 * @param {Roo.form.Form} form
24916 if (this.progressUrl) {
24917 // push a hidden field onto the list of fields..
24921 name : 'UPLOAD_IDENTIFIER'
24926 Roo.each(xitems, this.addxtype, this);
24932 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
24934 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
24937 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
24940 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
24942 buttonAlign:'center',
24945 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
24950 * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
24951 * This property cascades to child containers if not set.
24956 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
24957 * fires a looping event with that state. This is required to bind buttons to the valid
24958 * state using the config value formBind:true on the button.
24960 monitorValid : false,
24963 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
24968 * @cfg {String} progressUrl - Url to return progress data
24971 progressUrl : false,
24974 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
24975 * fields are added and the column is closed. If no fields are passed the column remains open
24976 * until end() is called.
24977 * @param {Object} config The config to pass to the column
24978 * @param {Field} field1 (optional)
24979 * @param {Field} field2 (optional)
24980 * @param {Field} etc (optional)
24981 * @return Column The column container object
24983 column : function(c){
24984 var col = new Roo.form.Column(c);
24986 if(arguments.length > 1){ // duplicate code required because of Opera
24987 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
24994 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
24995 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
24996 * until end() is called.
24997 * @param {Object} config The config to pass to the fieldset
24998 * @param {Field} field1 (optional)
24999 * @param {Field} field2 (optional)
25000 * @param {Field} etc (optional)
25001 * @return FieldSet The fieldset container object
25003 fieldset : function(c){
25004 var fs = new Roo.form.FieldSet(c);
25006 if(arguments.length > 1){ // duplicate code required because of Opera
25007 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25014 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25015 * fields are added and the container is closed. If no fields are passed the container remains open
25016 * until end() is called.
25017 * @param {Object} config The config to pass to the Layout
25018 * @param {Field} field1 (optional)
25019 * @param {Field} field2 (optional)
25020 * @param {Field} etc (optional)
25021 * @return Layout The container object
25023 container : function(c){
25024 var l = new Roo.form.Layout(c);
25026 if(arguments.length > 1){ // duplicate code required because of Opera
25027 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25034 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25035 * @param {Object} container A Roo.form.Layout or subclass of Layout
25036 * @return {Form} this
25038 start : function(c){
25039 // cascade label info
25040 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25041 this.active.stack.push(c);
25042 c.ownerCt = this.active;
25048 * Closes the current open container
25049 * @return {Form} this
25052 if(this.active == this.root){
25055 this.active = this.active.ownerCt;
25060 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
25061 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25062 * as the label of the field.
25063 * @param {Field} field1
25064 * @param {Field} field2 (optional)
25065 * @param {Field} etc. (optional)
25066 * @return {Form} this
25069 this.active.stack.push.apply(this.active.stack, arguments);
25070 this.allItems.push.apply(this.allItems,arguments);
25072 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25073 if(a[i].isFormField){
25078 Roo.form.Form.superclass.add.apply(this, r);
25088 * Find any element that has been added to a form, using it's ID or name
25089 * This can include framesets, columns etc. along with regular fields..
25090 * @param {String} id - id or name to find.
25092 * @return {Element} e - or false if nothing found.
25094 findbyId : function(id)
25100 Roo.each(this.allItems, function(f){
25101 if (f.id == id || f.name == id ){
25112 * Render this form into the passed container. This should only be called once!
25113 * @param {String/HTMLElement/Element} container The element this component should be rendered into
25114 * @return {Form} this
25116 render : function(ct)
25122 var o = this.autoCreate || {
25124 method : this.method || 'POST',
25125 id : this.id || Roo.id()
25127 this.initEl(ct.createChild(o));
25129 this.root.render(this.el);
25133 this.items.each(function(f){
25134 f.render('x-form-el-'+f.id);
25137 if(this.buttons.length > 0){
25138 // tables are required to maintain order and for correct IE layout
25139 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25140 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25141 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25143 var tr = tb.getElementsByTagName('tr')[0];
25144 for(var i = 0, len = this.buttons.length; i < len; i++) {
25145 var b = this.buttons[i];
25146 var td = document.createElement('td');
25147 td.className = 'x-form-btn-td';
25148 b.render(tr.appendChild(td));
25151 if(this.monitorValid){ // initialize after render
25152 this.startMonitoring();
25154 this.fireEvent('rendered', this);
25159 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25160 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25161 * object or a valid Roo.DomHelper element config
25162 * @param {Function} handler The function called when the button is clicked
25163 * @param {Object} scope (optional) The scope of the handler function
25164 * @return {Roo.Button}
25166 addButton : function(config, handler, scope){
25170 minWidth: this.minButtonWidth,
25173 if(typeof config == "string"){
25176 Roo.apply(bc, config);
25178 var btn = new Roo.Button(null, bc);
25179 this.buttons.push(btn);
25184 * Adds a series of form elements (using the xtype property as the factory method.
25185 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25186 * @param {Object} config
25189 addxtype : function()
25191 var ar = Array.prototype.slice.call(arguments, 0);
25193 for(var i = 0; i < ar.length; i++) {
25195 continue; // skip -- if this happends something invalid got sent, we
25196 // should ignore it, as basically that interface element will not show up
25197 // and that should be pretty obvious!!
25200 if (Roo.form[ar[i].xtype]) {
25202 var fe = Roo.factory(ar[i], Roo.form);
25208 fe.store.form = this;
25213 this.allItems.push(fe);
25214 if (fe.items && fe.addxtype) {
25215 fe.addxtype.apply(fe, fe.items);
25225 // console.log('adding ' + ar[i].xtype);
25227 if (ar[i].xtype == 'Button') {
25228 //console.log('adding button');
25229 //console.log(ar[i]);
25230 this.addButton(ar[i]);
25231 this.allItems.push(fe);
25235 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25236 alert('end is not supported on xtype any more, use items');
25238 // //console.log('adding end');
25246 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25247 * option "monitorValid"
25249 startMonitoring : function(){
25252 Roo.TaskMgr.start({
25253 run : this.bindHandler,
25254 interval : this.monitorPoll || 200,
25261 * Stops monitoring of the valid state of this form
25263 stopMonitoring : function(){
25264 this.bound = false;
25268 bindHandler : function(){
25270 return false; // stops binding
25273 this.items.each(function(f){
25274 if(!f.isValid(true)){
25279 for(var i = 0, len = this.buttons.length; i < len; i++){
25280 var btn = this.buttons[i];
25281 if(btn.formBind === true && btn.disabled === valid){
25282 btn.setDisabled(!valid);
25285 this.fireEvent('clientvalidation', this, valid);
25299 Roo.Form = Roo.form.Form;
25302 * Ext JS Library 1.1.1
25303 * Copyright(c) 2006-2007, Ext JS, LLC.
25305 * Originally Released Under LGPL - original licence link has changed is not relivant.
25308 * <script type="text/javascript">
25311 // as we use this in bootstrap.
25312 Roo.namespace('Roo.form');
25314 * @class Roo.form.Action
25315 * Internal Class used to handle form actions
25317 * @param {Roo.form.BasicForm} el The form element or its id
25318 * @param {Object} config Configuration options
25323 // define the action interface
25324 Roo.form.Action = function(form, options){
25326 this.options = options || {};
25329 * Client Validation Failed
25332 Roo.form.Action.CLIENT_INVALID = 'client';
25334 * Server Validation Failed
25337 Roo.form.Action.SERVER_INVALID = 'server';
25339 * Connect to Server Failed
25342 Roo.form.Action.CONNECT_FAILURE = 'connect';
25344 * Reading Data from Server Failed
25347 Roo.form.Action.LOAD_FAILURE = 'load';
25349 Roo.form.Action.prototype = {
25351 failureType : undefined,
25352 response : undefined,
25353 result : undefined,
25355 // interface method
25356 run : function(options){
25360 // interface method
25361 success : function(response){
25365 // interface method
25366 handleResponse : function(response){
25370 // default connection failure
25371 failure : function(response){
25373 this.response = response;
25374 this.failureType = Roo.form.Action.CONNECT_FAILURE;
25375 this.form.afterAction(this, false);
25378 processResponse : function(response){
25379 this.response = response;
25380 if(!response.responseText){
25383 this.result = this.handleResponse(response);
25384 return this.result;
25387 // utility functions used internally
25388 getUrl : function(appendParams){
25389 var url = this.options.url || this.form.url || this.form.el.dom.action;
25391 var p = this.getParams();
25393 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25399 getMethod : function(){
25400 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25403 getParams : function(){
25404 var bp = this.form.baseParams;
25405 var p = this.options.params;
25407 if(typeof p == "object"){
25408 p = Roo.urlEncode(Roo.applyIf(p, bp));
25409 }else if(typeof p == 'string' && bp){
25410 p += '&' + Roo.urlEncode(bp);
25413 p = Roo.urlEncode(bp);
25418 createCallback : function(){
25420 success: this.success,
25421 failure: this.failure,
25423 timeout: (this.form.timeout*1000),
25424 upload: this.form.fileUpload ? this.success : undefined
25429 Roo.form.Action.Submit = function(form, options){
25430 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25433 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25436 haveProgress : false,
25437 uploadComplete : false,
25439 // uploadProgress indicator.
25440 uploadProgress : function()
25442 if (!this.form.progressUrl) {
25446 if (!this.haveProgress) {
25447 Roo.MessageBox.progress("Uploading", "Uploading");
25449 if (this.uploadComplete) {
25450 Roo.MessageBox.hide();
25454 this.haveProgress = true;
25456 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25458 var c = new Roo.data.Connection();
25460 url : this.form.progressUrl,
25465 success : function(req){
25466 //console.log(data);
25470 rdata = Roo.decode(req.responseText)
25472 Roo.log("Invalid data from server..");
25476 if (!rdata || !rdata.success) {
25478 Roo.MessageBox.alert(Roo.encode(rdata));
25481 var data = rdata.data;
25483 if (this.uploadComplete) {
25484 Roo.MessageBox.hide();
25489 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25490 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25493 this.uploadProgress.defer(2000,this);
25496 failure: function(data) {
25497 Roo.log('progress url failed ');
25508 // run get Values on the form, so it syncs any secondary forms.
25509 this.form.getValues();
25511 var o = this.options;
25512 var method = this.getMethod();
25513 var isPost = method == 'POST';
25514 if(o.clientValidation === false || this.form.isValid()){
25516 if (this.form.progressUrl) {
25517 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25518 (new Date() * 1) + '' + Math.random());
25523 Roo.Ajax.request(Roo.apply(this.createCallback(), {
25524 form:this.form.el.dom,
25525 url:this.getUrl(!isPost),
25527 params:isPost ? this.getParams() : null,
25528 isUpload: this.form.fileUpload
25531 this.uploadProgress();
25533 }else if (o.clientValidation !== false){ // client validation failed
25534 this.failureType = Roo.form.Action.CLIENT_INVALID;
25535 this.form.afterAction(this, false);
25539 success : function(response)
25541 this.uploadComplete= true;
25542 if (this.haveProgress) {
25543 Roo.MessageBox.hide();
25547 var result = this.processResponse(response);
25548 if(result === true || result.success){
25549 this.form.afterAction(this, true);
25553 this.form.markInvalid(result.errors);
25554 this.failureType = Roo.form.Action.SERVER_INVALID;
25556 this.form.afterAction(this, false);
25558 failure : function(response)
25560 this.uploadComplete= true;
25561 if (this.haveProgress) {
25562 Roo.MessageBox.hide();
25565 this.response = response;
25566 this.failureType = Roo.form.Action.CONNECT_FAILURE;
25567 this.form.afterAction(this, false);
25570 handleResponse : function(response){
25571 if(this.form.errorReader){
25572 var rs = this.form.errorReader.read(response);
25575 for(var i = 0, len = rs.records.length; i < len; i++) {
25576 var r = rs.records[i];
25577 errors[i] = r.data;
25580 if(errors.length < 1){
25584 success : rs.success,
25590 ret = Roo.decode(response.responseText);
25594 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
25604 Roo.form.Action.Load = function(form, options){
25605 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
25606 this.reader = this.form.reader;
25609 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
25614 Roo.Ajax.request(Roo.apply(
25615 this.createCallback(), {
25616 method:this.getMethod(),
25617 url:this.getUrl(false),
25618 params:this.getParams()
25622 success : function(response){
25624 var result = this.processResponse(response);
25625 if(result === true || !result.success || !result.data){
25626 this.failureType = Roo.form.Action.LOAD_FAILURE;
25627 this.form.afterAction(this, false);
25630 this.form.clearInvalid();
25631 this.form.setValues(result.data);
25632 this.form.afterAction(this, true);
25635 handleResponse : function(response){
25636 if(this.form.reader){
25637 var rs = this.form.reader.read(response);
25638 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
25640 success : rs.success,
25644 return Roo.decode(response.responseText);
25648 Roo.form.Action.ACTION_TYPES = {
25649 'load' : Roo.form.Action.Load,
25650 'submit' : Roo.form.Action.Submit
25653 * Ext JS Library 1.1.1
25654 * Copyright(c) 2006-2007, Ext JS, LLC.
25656 * Originally Released Under LGPL - original licence link has changed is not relivant.
25659 * <script type="text/javascript">
25663 * @class Roo.form.Layout
25664 * @extends Roo.Component
25665 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
25667 * @param {Object} config Configuration options
25669 Roo.form.Layout = function(config){
25671 if (config.items) {
25672 xitems = config.items;
25673 delete config.items;
25675 Roo.form.Layout.superclass.constructor.call(this, config);
25677 Roo.each(xitems, this.addxtype, this);
25681 Roo.extend(Roo.form.Layout, Roo.Component, {
25683 * @cfg {String/Object} autoCreate
25684 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
25687 * @cfg {String/Object/Function} style
25688 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
25689 * a function which returns such a specification.
25692 * @cfg {String} labelAlign
25693 * Valid values are "left," "top" and "right" (defaults to "left")
25696 * @cfg {Number} labelWidth
25697 * Fixed width in pixels of all field labels (defaults to undefined)
25700 * @cfg {Boolean} clear
25701 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
25705 * @cfg {String} labelSeparator
25706 * The separator to use after field labels (defaults to ':')
25708 labelSeparator : ':',
25710 * @cfg {Boolean} hideLabels
25711 * True to suppress the display of field labels in this layout (defaults to false)
25713 hideLabels : false,
25716 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
25721 onRender : function(ct, position){
25722 if(this.el){ // from markup
25723 this.el = Roo.get(this.el);
25724 }else { // generate
25725 var cfg = this.getAutoCreate();
25726 this.el = ct.createChild(cfg, position);
25729 this.el.applyStyles(this.style);
25731 if(this.labelAlign){
25732 this.el.addClass('x-form-label-'+this.labelAlign);
25734 if(this.hideLabels){
25735 this.labelStyle = "display:none";
25736 this.elementStyle = "padding-left:0;";
25738 if(typeof this.labelWidth == 'number'){
25739 this.labelStyle = "width:"+this.labelWidth+"px;";
25740 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
25742 if(this.labelAlign == 'top'){
25743 this.labelStyle = "width:auto;";
25744 this.elementStyle = "padding-left:0;";
25747 var stack = this.stack;
25748 var slen = stack.length;
25750 if(!this.fieldTpl){
25751 var t = new Roo.Template(
25752 '<div class="x-form-item {5}">',
25753 '<label for="{0}" style="{2}">{1}{4}</label>',
25754 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25756 '</div><div class="x-form-clear-left"></div>'
25758 t.disableFormats = true;
25760 Roo.form.Layout.prototype.fieldTpl = t;
25762 for(var i = 0; i < slen; i++) {
25763 if(stack[i].isFormField){
25764 this.renderField(stack[i]);
25766 this.renderComponent(stack[i]);
25771 this.el.createChild({cls:'x-form-clear'});
25776 renderField : function(f){
25777 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
25780 f.labelStyle||this.labelStyle||'', //2
25781 this.elementStyle||'', //3
25782 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
25783 f.itemCls||this.itemCls||'' //5
25784 ], true).getPrevSibling());
25788 renderComponent : function(c){
25789 c.render(c.isLayout ? this.el : this.el.createChild());
25792 * Adds a object form elements (using the xtype property as the factory method.)
25793 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
25794 * @param {Object} config
25796 addxtype : function(o)
25798 // create the lement.
25799 o.form = this.form;
25800 var fe = Roo.factory(o, Roo.form);
25801 this.form.allItems.push(fe);
25802 this.stack.push(fe);
25804 if (fe.isFormField) {
25805 this.form.items.add(fe);
25813 * @class Roo.form.Column
25814 * @extends Roo.form.Layout
25815 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
25817 * @param {Object} config Configuration options
25819 Roo.form.Column = function(config){
25820 Roo.form.Column.superclass.constructor.call(this, config);
25823 Roo.extend(Roo.form.Column, Roo.form.Layout, {
25825 * @cfg {Number/String} width
25826 * The fixed width of the column in pixels or CSS value (defaults to "auto")
25829 * @cfg {String/Object} autoCreate
25830 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
25834 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
25837 onRender : function(ct, position){
25838 Roo.form.Column.superclass.onRender.call(this, ct, position);
25840 this.el.setWidth(this.width);
25847 * @class Roo.form.Row
25848 * @extends Roo.form.Layout
25849 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
25851 * @param {Object} config Configuration options
25855 Roo.form.Row = function(config){
25856 Roo.form.Row.superclass.constructor.call(this, config);
25859 Roo.extend(Roo.form.Row, Roo.form.Layout, {
25861 * @cfg {Number/String} width
25862 * The fixed width of the column in pixels or CSS value (defaults to "auto")
25865 * @cfg {Number/String} height
25866 * The fixed height of the column in pixels or CSS value (defaults to "auto")
25868 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
25872 onRender : function(ct, position){
25873 //console.log('row render');
25875 var t = new Roo.Template(
25876 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
25877 '<label for="{0}" style="{2}">{1}{4}</label>',
25878 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25882 t.disableFormats = true;
25884 Roo.form.Layout.prototype.rowTpl = t;
25886 this.fieldTpl = this.rowTpl;
25888 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
25889 var labelWidth = 100;
25891 if ((this.labelAlign != 'top')) {
25892 if (typeof this.labelWidth == 'number') {
25893 labelWidth = this.labelWidth
25895 this.padWidth = 20 + labelWidth;
25899 Roo.form.Column.superclass.onRender.call(this, ct, position);
25901 this.el.setWidth(this.width);
25904 this.el.setHeight(this.height);
25909 renderField : function(f){
25910 f.fieldEl = this.fieldTpl.append(this.el, [
25911 f.id, f.fieldLabel,
25912 f.labelStyle||this.labelStyle||'',
25913 this.elementStyle||'',
25914 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
25915 f.itemCls||this.itemCls||'',
25916 f.width ? f.width + this.padWidth : 160 + this.padWidth
25923 * @class Roo.form.FieldSet
25924 * @extends Roo.form.Layout
25925 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
25927 * @param {Object} config Configuration options
25929 Roo.form.FieldSet = function(config){
25930 Roo.form.FieldSet.superclass.constructor.call(this, config);
25933 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
25935 * @cfg {String} legend
25936 * The text to display as the legend for the FieldSet (defaults to '')
25939 * @cfg {String/Object} autoCreate
25940 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
25944 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
25947 onRender : function(ct, position){
25948 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
25950 this.setLegend(this.legend);
25955 setLegend : function(text){
25957 this.el.child('legend').update(text);
25962 * Ext JS Library 1.1.1
25963 * Copyright(c) 2006-2007, Ext JS, LLC.
25965 * Originally Released Under LGPL - original licence link has changed is not relivant.
25968 * <script type="text/javascript">
25971 * @class Roo.form.VTypes
25972 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
25975 Roo.form.VTypes = function(){
25976 // closure these in so they are only created once.
25977 var alpha = /^[a-zA-Z_]+$/;
25978 var alphanum = /^[a-zA-Z0-9_]+$/;
25979 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
25980 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
25982 // All these messages and functions are configurable
25985 * The function used to validate email addresses
25986 * @param {String} value The email address
25988 'email' : function(v){
25989 return email.test(v);
25992 * The error text to display when the email validation function returns false
25995 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
25997 * The keystroke filter mask to be applied on email input
26000 'emailMask' : /[a-z0-9_\.\-@]/i,
26003 * The function used to validate URLs
26004 * @param {String} value The URL
26006 'url' : function(v){
26007 return url.test(v);
26010 * The error text to display when the url validation function returns false
26013 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26016 * The function used to validate alpha values
26017 * @param {String} value The value
26019 'alpha' : function(v){
26020 return alpha.test(v);
26023 * The error text to display when the alpha validation function returns false
26026 'alphaText' : 'This field should only contain letters and _',
26028 * The keystroke filter mask to be applied on alpha input
26031 'alphaMask' : /[a-z_]/i,
26034 * The function used to validate alphanumeric values
26035 * @param {String} value The value
26037 'alphanum' : function(v){
26038 return alphanum.test(v);
26041 * The error text to display when the alphanumeric validation function returns false
26044 'alphanumText' : 'This field should only contain letters, numbers and _',
26046 * The keystroke filter mask to be applied on alphanumeric input
26049 'alphanumMask' : /[a-z0-9_]/i
26051 }();//<script type="text/javascript">
26054 * @class Roo.form.FCKeditor
26055 * @extends Roo.form.TextArea
26056 * Wrapper around the FCKEditor http://www.fckeditor.net
26058 * Creates a new FCKeditor
26059 * @param {Object} config Configuration options
26061 Roo.form.FCKeditor = function(config){
26062 Roo.form.FCKeditor.superclass.constructor.call(this, config);
26065 * @event editorinit
26066 * Fired when the editor is initialized - you can add extra handlers here..
26067 * @param {FCKeditor} this
26068 * @param {Object} the FCK object.
26075 Roo.form.FCKeditor.editors = { };
26076 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26078 //defaultAutoCreate : {
26079 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
26083 * @cfg {Object} fck options - see fck manual for details.
26088 * @cfg {Object} fck toolbar set (Basic or Default)
26090 toolbarSet : 'Basic',
26092 * @cfg {Object} fck BasePath
26094 basePath : '/fckeditor/',
26102 onRender : function(ct, position)
26105 this.defaultAutoCreate = {
26107 style:"width:300px;height:60px;",
26108 autocomplete: "new-password"
26111 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26114 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26115 if(this.preventScrollbars){
26116 this.el.setStyle("overflow", "hidden");
26118 this.el.setHeight(this.growMin);
26121 //console.log('onrender' + this.getId() );
26122 Roo.form.FCKeditor.editors[this.getId()] = this;
26125 this.replaceTextarea() ;
26129 getEditor : function() {
26130 return this.fckEditor;
26133 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
26134 * @param {Mixed} value The value to set
26138 setValue : function(value)
26140 //console.log('setValue: ' + value);
26142 if(typeof(value) == 'undefined') { // not sure why this is happending...
26145 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26147 //if(!this.el || !this.getEditor()) {
26148 // this.value = value;
26149 //this.setValue.defer(100,this,[value]);
26153 if(!this.getEditor()) {
26157 this.getEditor().SetData(value);
26164 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
26165 * @return {Mixed} value The field value
26167 getValue : function()
26170 if (this.frame && this.frame.dom.style.display == 'none') {
26171 return Roo.form.FCKeditor.superclass.getValue.call(this);
26174 if(!this.el || !this.getEditor()) {
26176 // this.getValue.defer(100,this);
26181 var value=this.getEditor().GetData();
26182 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26183 return Roo.form.FCKeditor.superclass.getValue.call(this);
26189 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
26190 * @return {Mixed} value The field value
26192 getRawValue : function()
26194 if (this.frame && this.frame.dom.style.display == 'none') {
26195 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26198 if(!this.el || !this.getEditor()) {
26199 //this.getRawValue.defer(100,this);
26206 var value=this.getEditor().GetData();
26207 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26208 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26212 setSize : function(w,h) {
26216 //if (this.frame && this.frame.dom.style.display == 'none') {
26217 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26220 //if(!this.el || !this.getEditor()) {
26221 // this.setSize.defer(100,this, [w,h]);
26227 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26229 this.frame.dom.setAttribute('width', w);
26230 this.frame.dom.setAttribute('height', h);
26231 this.frame.setSize(w,h);
26235 toggleSourceEdit : function(value) {
26239 this.el.dom.style.display = value ? '' : 'none';
26240 this.frame.dom.style.display = value ? 'none' : '';
26245 focus: function(tag)
26247 if (this.frame.dom.style.display == 'none') {
26248 return Roo.form.FCKeditor.superclass.focus.call(this);
26250 if(!this.el || !this.getEditor()) {
26251 this.focus.defer(100,this, [tag]);
26258 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26259 this.getEditor().Focus();
26261 if (!this.getEditor().Selection.GetSelection()) {
26262 this.focus.defer(100,this, [tag]);
26267 var r = this.getEditor().EditorDocument.createRange();
26268 r.setStart(tgs[0],0);
26269 r.setEnd(tgs[0],0);
26270 this.getEditor().Selection.GetSelection().removeAllRanges();
26271 this.getEditor().Selection.GetSelection().addRange(r);
26272 this.getEditor().Focus();
26279 replaceTextarea : function()
26281 if ( document.getElementById( this.getId() + '___Frame' ) ) {
26284 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26286 // We must check the elements firstly using the Id and then the name.
26287 var oTextarea = document.getElementById( this.getId() );
26289 var colElementsByName = document.getElementsByName( this.getId() ) ;
26291 oTextarea.style.display = 'none' ;
26293 if ( oTextarea.tabIndex ) {
26294 this.TabIndex = oTextarea.tabIndex ;
26297 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26298 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26299 this.frame = Roo.get(this.getId() + '___Frame')
26302 _getConfigHtml : function()
26306 for ( var o in this.fckconfig ) {
26307 sConfig += sConfig.length > 0 ? '&' : '';
26308 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26311 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26315 _getIFrameHtml : function()
26317 var sFile = 'fckeditor.html' ;
26318 /* no idea what this is about..
26321 if ( (/fcksource=true/i).test( window.top.location.search ) )
26322 sFile = 'fckeditor.original.html' ;
26327 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26328 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
26331 var html = '<iframe id="' + this.getId() +
26332 '___Frame" src="' + sLink +
26333 '" width="' + this.width +
26334 '" height="' + this.height + '"' +
26335 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
26336 ' frameborder="0" scrolling="no"></iframe>' ;
26341 _insertHtmlBefore : function( html, element )
26343 if ( element.insertAdjacentHTML ) {
26345 element.insertAdjacentHTML( 'beforeBegin', html ) ;
26347 var oRange = document.createRange() ;
26348 oRange.setStartBefore( element ) ;
26349 var oFragment = oRange.createContextualFragment( html );
26350 element.parentNode.insertBefore( oFragment, element ) ;
26363 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26365 function FCKeditor_OnComplete(editorInstance){
26366 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26367 f.fckEditor = editorInstance;
26368 //console.log("loaded");
26369 f.fireEvent('editorinit', f, editorInstance);
26389 //<script type="text/javascript">
26391 * @class Roo.form.GridField
26392 * @extends Roo.form.Field
26393 * Embed a grid (or editable grid into a form)
26396 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26398 * xgrid.store = Roo.data.Store
26399 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26400 * xgrid.store.reader = Roo.data.JsonReader
26404 * Creates a new GridField
26405 * @param {Object} config Configuration options
26407 Roo.form.GridField = function(config){
26408 Roo.form.GridField.superclass.constructor.call(this, config);
26412 Roo.extend(Roo.form.GridField, Roo.form.Field, {
26414 * @cfg {Number} width - used to restrict width of grid..
26418 * @cfg {Number} height - used to restrict height of grid..
26422 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26428 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26429 * {tag: "input", type: "checkbox", autocomplete: "off"})
26431 // defaultAutoCreate : { tag: 'div' },
26432 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26434 * @cfg {String} addTitle Text to include for adding a title.
26438 onResize : function(){
26439 Roo.form.Field.superclass.onResize.apply(this, arguments);
26442 initEvents : function(){
26443 // Roo.form.Checkbox.superclass.initEvents.call(this);
26444 // has no events...
26449 getResizeEl : function(){
26453 getPositionEl : function(){
26458 onRender : function(ct, position){
26460 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26461 var style = this.style;
26464 Roo.form.GridField.superclass.onRender.call(this, ct, position);
26465 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26466 this.viewEl = this.wrap.createChild({ tag: 'div' });
26468 this.viewEl.applyStyles(style);
26471 this.viewEl.setWidth(this.width);
26474 this.viewEl.setHeight(this.height);
26476 //if(this.inputValue !== undefined){
26477 //this.setValue(this.value);
26480 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26483 this.grid.render();
26484 this.grid.getDataSource().on('remove', this.refreshValue, this);
26485 this.grid.getDataSource().on('update', this.refreshValue, this);
26486 this.grid.on('afteredit', this.refreshValue, this);
26492 * Sets the value of the item.
26493 * @param {String} either an object or a string..
26495 setValue : function(v){
26497 v = v || []; // empty set..
26498 // this does not seem smart - it really only affects memoryproxy grids..
26499 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26500 var ds = this.grid.getDataSource();
26501 // assumes a json reader..
26503 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
26504 ds.loadData( data);
26506 // clear selection so it does not get stale.
26507 if (this.grid.sm) {
26508 this.grid.sm.clearSelections();
26511 Roo.form.GridField.superclass.setValue.call(this, v);
26512 this.refreshValue();
26513 // should load data in the grid really....
26517 refreshValue: function() {
26519 this.grid.getDataSource().each(function(r) {
26522 this.el.dom.value = Roo.encode(val);
26530 * Ext JS Library 1.1.1
26531 * Copyright(c) 2006-2007, Ext JS, LLC.
26533 * Originally Released Under LGPL - original licence link has changed is not relivant.
26536 * <script type="text/javascript">
26539 * @class Roo.form.DisplayField
26540 * @extends Roo.form.Field
26541 * A generic Field to display non-editable data.
26542 * @cfg {Boolean} closable (true|false) default false
26544 * Creates a new Display Field item.
26545 * @param {Object} config Configuration options
26547 Roo.form.DisplayField = function(config){
26548 Roo.form.DisplayField.superclass.constructor.call(this, config);
26553 * Fires after the click the close btn
26554 * @param {Roo.form.DisplayField} this
26560 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
26561 inputType: 'hidden',
26567 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26569 focusClass : undefined,
26571 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26573 fieldClass: 'x-form-field',
26576 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
26578 valueRenderer: undefined,
26582 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26583 * {tag: "input", type: "checkbox", autocomplete: "off"})
26586 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
26590 onResize : function(){
26591 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
26595 initEvents : function(){
26596 // Roo.form.Checkbox.superclass.initEvents.call(this);
26597 // has no events...
26600 this.closeEl.on('click', this.onClose, this);
26606 getResizeEl : function(){
26610 getPositionEl : function(){
26615 onRender : function(ct, position){
26617 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
26618 //if(this.inputValue !== undefined){
26619 this.wrap = this.el.wrap();
26621 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
26624 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
26627 if (this.bodyStyle) {
26628 this.viewEl.applyStyles(this.bodyStyle);
26630 //this.viewEl.setStyle('padding', '2px');
26632 this.setValue(this.value);
26637 initValue : Roo.emptyFn,
26642 onClick : function(){
26647 * Sets the checked state of the checkbox.
26648 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
26650 setValue : function(v){
26652 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
26653 // this might be called before we have a dom element..
26654 if (!this.viewEl) {
26657 this.viewEl.dom.innerHTML = html;
26658 Roo.form.DisplayField.superclass.setValue.call(this, v);
26662 onClose : function(e)
26664 e.preventDefault();
26666 this.fireEvent('close', this);
26675 * @class Roo.form.DayPicker
26676 * @extends Roo.form.Field
26677 * A Day picker show [M] [T] [W] ....
26679 * Creates a new Day Picker
26680 * @param {Object} config Configuration options
26682 Roo.form.DayPicker= function(config){
26683 Roo.form.DayPicker.superclass.constructor.call(this, config);
26687 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
26689 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26691 focusClass : undefined,
26693 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26695 fieldClass: "x-form-field",
26698 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26699 * {tag: "input", type: "checkbox", autocomplete: "off"})
26701 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
26704 actionMode : 'viewEl',
26708 inputType : 'hidden',
26711 inputElement: false, // real input element?
26712 basedOn: false, // ????
26714 isFormField: true, // not sure where this is needed!!!!
26716 onResize : function(){
26717 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
26718 if(!this.boxLabel){
26719 this.el.alignTo(this.wrap, 'c-c');
26723 initEvents : function(){
26724 Roo.form.Checkbox.superclass.initEvents.call(this);
26725 this.el.on("click", this.onClick, this);
26726 this.el.on("change", this.onClick, this);
26730 getResizeEl : function(){
26734 getPositionEl : function(){
26740 onRender : function(ct, position){
26741 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
26743 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
26745 var r1 = '<table><tr>';
26746 var r2 = '<tr class="x-form-daypick-icons">';
26747 for (var i=0; i < 7; i++) {
26748 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
26749 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
26752 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
26753 viewEl.select('img').on('click', this.onClick, this);
26754 this.viewEl = viewEl;
26757 // this will not work on Chrome!!!
26758 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
26759 this.el.on('propertychange', this.setFromHidden, this); //ie
26767 initValue : Roo.emptyFn,
26770 * Returns the checked state of the checkbox.
26771 * @return {Boolean} True if checked, else false
26773 getValue : function(){
26774 return this.el.dom.value;
26779 onClick : function(e){
26780 //this.setChecked(!this.checked);
26781 Roo.get(e.target).toggleClass('x-menu-item-checked');
26782 this.refreshValue();
26783 //if(this.el.dom.checked != this.checked){
26784 // this.setValue(this.el.dom.checked);
26789 refreshValue : function()
26792 this.viewEl.select('img',true).each(function(e,i,n) {
26793 val += e.is(".x-menu-item-checked") ? String(n) : '';
26795 this.setValue(val, true);
26799 * Sets the checked state of the checkbox.
26800 * On is always based on a string comparison between inputValue and the param.
26801 * @param {Boolean/String} value - the value to set
26802 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
26804 setValue : function(v,suppressEvent){
26805 if (!this.el.dom) {
26808 var old = this.el.dom.value ;
26809 this.el.dom.value = v;
26810 if (suppressEvent) {
26814 // update display..
26815 this.viewEl.select('img',true).each(function(e,i,n) {
26817 var on = e.is(".x-menu-item-checked");
26818 var newv = v.indexOf(String(n)) > -1;
26820 e.toggleClass('x-menu-item-checked');
26826 this.fireEvent('change', this, v, old);
26831 // handle setting of hidden value by some other method!!?!?
26832 setFromHidden: function()
26837 //console.log("SET FROM HIDDEN");
26838 //alert('setFrom hidden');
26839 this.setValue(this.el.dom.value);
26842 onDestroy : function()
26845 Roo.get(this.viewEl).remove();
26848 Roo.form.DayPicker.superclass.onDestroy.call(this);
26852 * RooJS Library 1.1.1
26853 * Copyright(c) 2008-2011 Alan Knowles
26860 * @class Roo.form.ComboCheck
26861 * @extends Roo.form.ComboBox
26862 * A combobox for multiple select items.
26864 * FIXME - could do with a reset button..
26867 * Create a new ComboCheck
26868 * @param {Object} config Configuration options
26870 Roo.form.ComboCheck = function(config){
26871 Roo.form.ComboCheck.superclass.constructor.call(this, config);
26872 // should verify some data...
26874 // hiddenName = required..
26875 // displayField = required
26876 // valudField == required
26877 var req= [ 'hiddenName', 'displayField', 'valueField' ];
26879 Roo.each(req, function(e) {
26880 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
26881 throw "Roo.form.ComboCheck : missing value for: " + e;
26888 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
26893 selectedClass: 'x-menu-item-checked',
26896 onRender : function(ct, position){
26902 var cls = 'x-combo-list';
26905 this.tpl = new Roo.Template({
26906 html : '<div class="'+cls+'-item x-menu-check-item">' +
26907 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
26908 '<span>{' + this.displayField + '}</span>' +
26915 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
26916 this.view.singleSelect = false;
26917 this.view.multiSelect = true;
26918 this.view.toggleSelect = true;
26919 this.pageTb.add(new Roo.Toolbar.Fill(), {
26922 handler: function()
26929 onViewOver : function(e, t){
26935 onViewClick : function(doFocus,index){
26939 select: function () {
26940 //Roo.log("SELECT CALLED");
26943 selectByValue : function(xv, scrollIntoView){
26944 var ar = this.getValueArray();
26947 Roo.each(ar, function(v) {
26948 if(v === undefined || v === null){
26951 var r = this.findRecord(this.valueField, v);
26953 sels.push(this.store.indexOf(r))
26957 this.view.select(sels);
26963 onSelect : function(record, index){
26964 // Roo.log("onselect Called");
26965 // this is only called by the clear button now..
26966 this.view.clearSelections();
26967 this.setValue('[]');
26968 if (this.value != this.valueBefore) {
26969 this.fireEvent('change', this, this.value, this.valueBefore);
26970 this.valueBefore = this.value;
26973 getValueArray : function()
26978 //Roo.log(this.value);
26979 if (typeof(this.value) == 'undefined') {
26982 var ar = Roo.decode(this.value);
26983 return ar instanceof Array ? ar : []; //?? valid?
26986 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
26991 expand : function ()
26994 Roo.form.ComboCheck.superclass.expand.call(this);
26995 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
26996 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27001 collapse : function(){
27002 Roo.form.ComboCheck.superclass.collapse.call(this);
27003 var sl = this.view.getSelectedIndexes();
27004 var st = this.store;
27008 Roo.each(sl, function(i) {
27010 nv.push(r.get(this.valueField));
27012 this.setValue(Roo.encode(nv));
27013 if (this.value != this.valueBefore) {
27015 this.fireEvent('change', this, this.value, this.valueBefore);
27016 this.valueBefore = this.value;
27021 setValue : function(v){
27025 var vals = this.getValueArray();
27027 Roo.each(vals, function(k) {
27028 var r = this.findRecord(this.valueField, k);
27030 tv.push(r.data[this.displayField]);
27031 }else if(this.valueNotFoundText !== undefined){
27032 tv.push( this.valueNotFoundText );
27037 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27038 this.hiddenField.value = v;
27044 * Ext JS Library 1.1.1
27045 * Copyright(c) 2006-2007, Ext JS, LLC.
27047 * Originally Released Under LGPL - original licence link has changed is not relivant.
27050 * <script type="text/javascript">
27054 * @class Roo.form.Signature
27055 * @extends Roo.form.Field
27059 * @param {Object} config Configuration options
27062 Roo.form.Signature = function(config){
27063 Roo.form.Signature.superclass.constructor.call(this, config);
27065 this.addEvents({// not in used??
27068 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27069 * @param {Roo.form.Signature} combo This combo box
27074 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27075 * @param {Roo.form.ComboBox} combo This combo box
27076 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27082 Roo.extend(Roo.form.Signature, Roo.form.Field, {
27084 * @cfg {Object} labels Label to use when rendering a form.
27088 * confirm : "Confirm"
27093 confirm : "Confirm"
27096 * @cfg {Number} width The signature panel width (defaults to 300)
27100 * @cfg {Number} height The signature panel height (defaults to 100)
27104 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27106 allowBlank : false,
27109 // {Object} signPanel The signature SVG panel element (defaults to {})
27111 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27112 isMouseDown : false,
27113 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27114 isConfirmed : false,
27115 // {String} signatureTmp SVG mapping string (defaults to empty string)
27119 defaultAutoCreate : { // modified by initCompnoent..
27125 onRender : function(ct, position){
27127 Roo.form.Signature.superclass.onRender.call(this, ct, position);
27129 this.wrap = this.el.wrap({
27130 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27133 this.createToolbar(this);
27134 this.signPanel = this.wrap.createChild({
27136 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27140 this.svgID = Roo.id();
27141 this.svgEl = this.signPanel.createChild({
27142 xmlns : 'http://www.w3.org/2000/svg',
27144 id : this.svgID + "-svg",
27146 height: this.height,
27147 viewBox: '0 0 '+this.width+' '+this.height,
27151 id: this.svgID + "-svg-r",
27153 height: this.height,
27158 id: this.svgID + "-svg-l",
27160 y1: (this.height*0.8), // start set the line in 80% of height
27161 x2: this.width, // end
27162 y2: (this.height*0.8), // end set the line in 80% of height
27164 'stroke-width': "1",
27165 'stroke-dasharray': "3",
27166 'shape-rendering': "crispEdges",
27167 'pointer-events': "none"
27171 id: this.svgID + "-svg-p",
27173 'stroke-width': "3",
27175 'pointer-events': 'none'
27180 this.svgBox = this.svgEl.dom.getScreenCTM();
27182 createSVG : function(){
27183 var svg = this.signPanel;
27184 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27187 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27188 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27189 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27190 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27191 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27192 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27193 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27196 isTouchEvent : function(e){
27197 return e.type.match(/^touch/);
27199 getCoords : function (e) {
27200 var pt = this.svgEl.dom.createSVGPoint();
27203 if (this.isTouchEvent(e)) {
27204 pt.x = e.targetTouches[0].clientX;
27205 pt.y = e.targetTouches[0].clientY;
27207 var a = this.svgEl.dom.getScreenCTM();
27208 var b = a.inverse();
27209 var mx = pt.matrixTransform(b);
27210 return mx.x + ',' + mx.y;
27212 //mouse event headler
27213 down : function (e) {
27214 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27215 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27217 this.isMouseDown = true;
27219 e.preventDefault();
27221 move : function (e) {
27222 if (this.isMouseDown) {
27223 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27224 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27227 e.preventDefault();
27229 up : function (e) {
27230 this.isMouseDown = false;
27231 var sp = this.signatureTmp.split(' ');
27234 if(!sp[sp.length-2].match(/^L/)){
27238 this.signatureTmp = sp.join(" ");
27241 if(this.getValue() != this.signatureTmp){
27242 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27243 this.isConfirmed = false;
27245 e.preventDefault();
27249 * Protected method that will not generally be called directly. It
27250 * is called when the editor creates its toolbar. Override this method if you need to
27251 * add custom toolbar buttons.
27252 * @param {HtmlEditor} editor
27254 createToolbar : function(editor){
27255 function btn(id, toggle, handler){
27256 var xid = fid + '-'+ id ;
27260 cls : 'x-btn-icon x-edit-'+id,
27261 enableToggle:toggle !== false,
27262 scope: editor, // was editor...
27263 handler:handler||editor.relayBtnCmd,
27264 clickEvent:'mousedown',
27265 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27271 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27275 cls : ' x-signature-btn x-signature-'+id,
27276 scope: editor, // was editor...
27277 handler: this.reset,
27278 clickEvent:'mousedown',
27279 text: this.labels.clear
27286 cls : ' x-signature-btn x-signature-'+id,
27287 scope: editor, // was editor...
27288 handler: this.confirmHandler,
27289 clickEvent:'mousedown',
27290 text: this.labels.confirm
27297 * when user is clicked confirm then show this image.....
27299 * @return {String} Image Data URI
27301 getImageDataURI : function(){
27302 var svg = this.svgEl.dom.parentNode.innerHTML;
27303 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27308 * @return {Boolean} this.isConfirmed
27310 getConfirmed : function(){
27311 return this.isConfirmed;
27315 * @return {Number} this.width
27317 getWidth : function(){
27322 * @return {Number} this.height
27324 getHeight : function(){
27325 return this.height;
27328 getSignature : function(){
27329 return this.signatureTmp;
27332 reset : function(){
27333 this.signatureTmp = '';
27334 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27335 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27336 this.isConfirmed = false;
27337 Roo.form.Signature.superclass.reset.call(this);
27339 setSignature : function(s){
27340 this.signatureTmp = s;
27341 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27342 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27344 this.isConfirmed = false;
27345 Roo.form.Signature.superclass.reset.call(this);
27348 // Roo.log(this.signPanel.dom.contentWindow.up())
27351 setConfirmed : function(){
27355 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27358 confirmHandler : function(){
27359 if(!this.getSignature()){
27363 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27364 this.setValue(this.getSignature());
27365 this.isConfirmed = true;
27367 this.fireEvent('confirm', this);
27370 // Subclasses should provide the validation implementation by overriding this
27371 validateValue : function(value){
27372 if(this.allowBlank){
27376 if(this.isConfirmed){
27383 * Ext JS Library 1.1.1
27384 * Copyright(c) 2006-2007, Ext JS, LLC.
27386 * Originally Released Under LGPL - original licence link has changed is not relivant.
27389 * <script type="text/javascript">
27394 * @class Roo.form.ComboBox
27395 * @extends Roo.form.TriggerField
27396 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27398 * Create a new ComboBox.
27399 * @param {Object} config Configuration options
27401 Roo.form.Select = function(config){
27402 Roo.form.Select.superclass.constructor.call(this, config);
27406 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27408 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27411 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27412 * rendering into an Roo.Editor, defaults to false)
27415 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27416 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27419 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27422 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27423 * the dropdown list (defaults to undefined, with no header element)
27427 * @cfg {String/Roo.Template} tpl The template to use to render the output
27431 defaultAutoCreate : {tag: "select" },
27433 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27435 listWidth: undefined,
27437 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27438 * mode = 'remote' or 'text' if mode = 'local')
27440 displayField: undefined,
27442 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27443 * mode = 'remote' or 'value' if mode = 'local').
27444 * Note: use of a valueField requires the user make a selection
27445 * in order for a value to be mapped.
27447 valueField: undefined,
27451 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27452 * field's data value (defaults to the underlying DOM element's name)
27454 hiddenName: undefined,
27456 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27460 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27462 selectedClass: 'x-combo-selected',
27464 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
27465 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27466 * which displays a downward arrow icon).
27468 triggerClass : 'x-form-arrow-trigger',
27470 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27474 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27475 * anchor positions (defaults to 'tl-bl')
27477 listAlign: 'tl-bl?',
27479 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27483 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
27484 * query specified by the allQuery config option (defaults to 'query')
27486 triggerAction: 'query',
27488 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27489 * (defaults to 4, does not apply if editable = false)
27493 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27494 * delay (typeAheadDelay) if it matches a known value (defaults to false)
27498 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27499 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27503 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27504 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
27508 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
27509 * when editable = true (defaults to false)
27511 selectOnFocus:false,
27513 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27515 queryParam: 'query',
27517 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
27518 * when mode = 'remote' (defaults to 'Loading...')
27520 loadingText: 'Loading...',
27522 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27526 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27530 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27531 * traditional select (defaults to true)
27535 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27539 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27543 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27544 * listWidth has a higher value)
27548 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27549 * allow the user to set arbitrary text into the field (defaults to false)
27551 forceSelection:false,
27553 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27554 * if typeAhead = true (defaults to 250)
27556 typeAheadDelay : 250,
27558 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27559 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27561 valueNotFoundText : undefined,
27564 * @cfg {String} defaultValue The value displayed after loading the store.
27569 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27571 blockFocus : false,
27574 * @cfg {Boolean} disableClear Disable showing of clear button.
27576 disableClear : false,
27578 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
27580 alwaysQuery : false,
27586 // element that contains real text value.. (when hidden is used..)
27589 onRender : function(ct, position){
27590 Roo.form.Field.prototype.onRender.call(this, ct, position);
27593 this.store.on('beforeload', this.onBeforeLoad, this);
27594 this.store.on('load', this.onLoad, this);
27595 this.store.on('loadexception', this.onLoadException, this);
27596 this.store.load({});
27604 initEvents : function(){
27605 //Roo.form.ComboBox.superclass.initEvents.call(this);
27609 onDestroy : function(){
27612 this.store.un('beforeload', this.onBeforeLoad, this);
27613 this.store.un('load', this.onLoad, this);
27614 this.store.un('loadexception', this.onLoadException, this);
27616 //Roo.form.ComboBox.superclass.onDestroy.call(this);
27620 fireKey : function(e){
27621 if(e.isNavKeyPress() && !this.list.isVisible()){
27622 this.fireEvent("specialkey", this, e);
27627 onResize: function(w, h){
27635 * Allow or prevent the user from directly editing the field text. If false is passed,
27636 * the user will only be able to select from the items defined in the dropdown list. This method
27637 * is the runtime equivalent of setting the 'editable' config option at config time.
27638 * @param {Boolean} value True to allow the user to directly edit the field text
27640 setEditable : function(value){
27645 onBeforeLoad : function(){
27647 Roo.log("Select before load");
27650 this.innerList.update(this.loadingText ?
27651 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
27652 //this.restrictHeight();
27653 this.selectedIndex = -1;
27657 onLoad : function(){
27660 var dom = this.el.dom;
27661 dom.innerHTML = '';
27662 var od = dom.ownerDocument;
27664 if (this.emptyText) {
27665 var op = od.createElement('option');
27666 op.setAttribute('value', '');
27667 op.innerHTML = String.format('{0}', this.emptyText);
27668 dom.appendChild(op);
27670 if(this.store.getCount() > 0){
27672 var vf = this.valueField;
27673 var df = this.displayField;
27674 this.store.data.each(function(r) {
27675 // which colmsn to use... testing - cdoe / title..
27676 var op = od.createElement('option');
27677 op.setAttribute('value', r.data[vf]);
27678 op.innerHTML = String.format('{0}', r.data[df]);
27679 dom.appendChild(op);
27681 if (typeof(this.defaultValue != 'undefined')) {
27682 this.setValue(this.defaultValue);
27687 //this.onEmptyResults();
27692 onLoadException : function()
27694 dom.innerHTML = '';
27696 Roo.log("Select on load exception");
27700 Roo.log(this.store.reader.jsonData);
27701 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
27702 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
27708 onTypeAhead : function(){
27713 onSelect : function(record, index){
27714 Roo.log('on select?');
27716 if(this.fireEvent('beforeselect', this, record, index) !== false){
27717 this.setFromData(index > -1 ? record.data : false);
27719 this.fireEvent('select', this, record, index);
27724 * Returns the currently selected field value or empty string if no value is set.
27725 * @return {String} value The selected value
27727 getValue : function(){
27728 var dom = this.el.dom;
27729 this.value = dom.options[dom.selectedIndex].value;
27735 * Clears any text/value currently set in the field
27737 clearValue : function(){
27739 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
27744 * Sets the specified value into the field. If the value finds a match, the corresponding record text
27745 * will be displayed in the field. If the value does not match the data value of an existing item,
27746 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
27747 * Otherwise the field will be blank (although the value will still be set).
27748 * @param {String} value The value to match
27750 setValue : function(v){
27751 var d = this.el.dom;
27752 for (var i =0; i < d.options.length;i++) {
27753 if (v == d.options[i].value) {
27754 d.selectedIndex = i;
27762 * @property {Object} the last set data for the element
27767 * Sets the value of the field based on a object which is related to the record format for the store.
27768 * @param {Object} value the value to set as. or false on reset?
27770 setFromData : function(o){
27771 Roo.log('setfrom data?');
27777 reset : function(){
27781 findRecord : function(prop, value){
27786 if(this.store.getCount() > 0){
27787 this.store.each(function(r){
27788 if(r.data[prop] == value){
27798 getName: function()
27800 // returns hidden if it's set..
27801 if (!this.rendered) {return ''};
27802 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
27810 onEmptyResults : function(){
27811 Roo.log('empty results');
27816 * Returns true if the dropdown list is expanded, else false.
27818 isExpanded : function(){
27823 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
27824 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27825 * @param {String} value The data value of the item to select
27826 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27827 * selected item if it is not currently in view (defaults to true)
27828 * @return {Boolean} True if the value matched an item in the list, else false
27830 selectByValue : function(v, scrollIntoView){
27831 Roo.log('select By Value');
27834 if(v !== undefined && v !== null){
27835 var r = this.findRecord(this.valueField || this.displayField, v);
27837 this.select(this.store.indexOf(r), scrollIntoView);
27845 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
27846 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27847 * @param {Number} index The zero-based index of the list item to select
27848 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27849 * selected item if it is not currently in view (defaults to true)
27851 select : function(index, scrollIntoView){
27852 Roo.log('select ');
27855 this.selectedIndex = index;
27856 this.view.select(index);
27857 if(scrollIntoView !== false){
27858 var el = this.view.getNode(index);
27860 this.innerList.scrollChildIntoView(el, false);
27868 validateBlur : function(){
27875 initQuery : function(){
27876 this.doQuery(this.getRawValue());
27880 doForce : function(){
27881 if(this.el.dom.value.length > 0){
27882 this.el.dom.value =
27883 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
27889 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
27890 * query allowing the query action to be canceled if needed.
27891 * @param {String} query The SQL query to execute
27892 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
27893 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
27894 * saved in the current store (defaults to false)
27896 doQuery : function(q, forceAll){
27898 Roo.log('doQuery?');
27899 if(q === undefined || q === null){
27904 forceAll: forceAll,
27908 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
27912 forceAll = qe.forceAll;
27913 if(forceAll === true || (q.length >= this.minChars)){
27914 if(this.lastQuery != q || this.alwaysQuery){
27915 this.lastQuery = q;
27916 if(this.mode == 'local'){
27917 this.selectedIndex = -1;
27919 this.store.clearFilter();
27921 this.store.filter(this.displayField, q);
27925 this.store.baseParams[this.queryParam] = q;
27927 params: this.getParams(q)
27932 this.selectedIndex = -1;
27939 getParams : function(q){
27941 //p[this.queryParam] = q;
27944 p.limit = this.pageSize;
27950 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
27952 collapse : function(){
27957 collapseIf : function(e){
27962 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
27964 expand : function(){
27972 * @cfg {Boolean} grow
27976 * @cfg {Number} growMin
27980 * @cfg {Number} growMax
27988 setWidth : function()
27992 getResizeEl : function(){
27995 });//<script type="text/javasscript">
27999 * @class Roo.DDView
28000 * A DnD enabled version of Roo.View.
28001 * @param {Element/String} container The Element in which to create the View.
28002 * @param {String} tpl The template string used to create the markup for each element of the View
28003 * @param {Object} config The configuration properties. These include all the config options of
28004 * {@link Roo.View} plus some specific to this class.<br>
28006 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28007 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28009 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28010 .x-view-drag-insert-above {
28011 border-top:1px dotted #3366cc;
28013 .x-view-drag-insert-below {
28014 border-bottom:1px dotted #3366cc;
28020 Roo.DDView = function(container, tpl, config) {
28021 Roo.DDView.superclass.constructor.apply(this, arguments);
28022 this.getEl().setStyle("outline", "0px none");
28023 this.getEl().unselectable();
28024 if (this.dragGroup) {
28025 this.setDraggable(this.dragGroup.split(","));
28027 if (this.dropGroup) {
28028 this.setDroppable(this.dropGroup.split(","));
28030 if (this.deletable) {
28031 this.setDeletable();
28033 this.isDirtyFlag = false;
28039 Roo.extend(Roo.DDView, Roo.View, {
28040 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28041 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28042 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28043 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28047 reset: Roo.emptyFn,
28049 clearInvalid: Roo.form.Field.prototype.clearInvalid,
28051 validate: function() {
28055 destroy: function() {
28056 this.purgeListeners();
28057 this.getEl.removeAllListeners();
28058 this.getEl().remove();
28059 if (this.dragZone) {
28060 if (this.dragZone.destroy) {
28061 this.dragZone.destroy();
28064 if (this.dropZone) {
28065 if (this.dropZone.destroy) {
28066 this.dropZone.destroy();
28071 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28072 getName: function() {
28076 /** Loads the View from a JSON string representing the Records to put into the Store. */
28077 setValue: function(v) {
28079 throw "DDView.setValue(). DDView must be constructed with a valid Store";
28082 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28083 this.store.proxy = new Roo.data.MemoryProxy(data);
28087 /** @return {String} a parenthesised list of the ids of the Records in the View. */
28088 getValue: function() {
28090 this.store.each(function(rec) {
28091 result += rec.id + ',';
28093 return result.substr(0, result.length - 1) + ')';
28096 getIds: function() {
28097 var i = 0, result = new Array(this.store.getCount());
28098 this.store.each(function(rec) {
28099 result[i++] = rec.id;
28104 isDirty: function() {
28105 return this.isDirtyFlag;
28109 * Part of the Roo.dd.DropZone interface. If no target node is found, the
28110 * whole Element becomes the target, and this causes the drop gesture to append.
28112 getTargetFromEvent : function(e) {
28113 var target = e.getTarget();
28114 while ((target !== null) && (target.parentNode != this.el.dom)) {
28115 target = target.parentNode;
28118 target = this.el.dom.lastChild || this.el.dom;
28124 * Create the drag data which consists of an object which has the property "ddel" as
28125 * the drag proxy element.
28127 getDragData : function(e) {
28128 var target = this.findItemFromChild(e.getTarget());
28130 this.handleSelection(e);
28131 var selNodes = this.getSelectedNodes();
28134 copy: this.copy || (this.allowCopy && e.ctrlKey),
28138 var selectedIndices = this.getSelectedIndexes();
28139 for (var i = 0; i < selectedIndices.length; i++) {
28140 dragData.records.push(this.store.getAt(selectedIndices[i]));
28142 if (selNodes.length == 1) {
28143 dragData.ddel = target.cloneNode(true); // the div element
28145 var div = document.createElement('div'); // create the multi element drag "ghost"
28146 div.className = 'multi-proxy';
28147 for (var i = 0, len = selNodes.length; i < len; i++) {
28148 div.appendChild(selNodes[i].cloneNode(true));
28150 dragData.ddel = div;
28152 //console.log(dragData)
28153 //console.log(dragData.ddel.innerHTML)
28156 //console.log('nodragData')
28160 /** Specify to which ddGroup items in this DDView may be dragged. */
28161 setDraggable: function(ddGroup) {
28162 if (ddGroup instanceof Array) {
28163 Roo.each(ddGroup, this.setDraggable, this);
28166 if (this.dragZone) {
28167 this.dragZone.addToGroup(ddGroup);
28169 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28170 containerScroll: true,
28174 // Draggability implies selection. DragZone's mousedown selects the element.
28175 if (!this.multiSelect) { this.singleSelect = true; }
28177 // Wire the DragZone's handlers up to methods in *this*
28178 this.dragZone.getDragData = this.getDragData.createDelegate(this);
28182 /** Specify from which ddGroup this DDView accepts drops. */
28183 setDroppable: function(ddGroup) {
28184 if (ddGroup instanceof Array) {
28185 Roo.each(ddGroup, this.setDroppable, this);
28188 if (this.dropZone) {
28189 this.dropZone.addToGroup(ddGroup);
28191 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28192 containerScroll: true,
28196 // Wire the DropZone's handlers up to methods in *this*
28197 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28198 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28199 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28200 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28201 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28205 /** Decide whether to drop above or below a View node. */
28206 getDropPoint : function(e, n, dd){
28207 if (n == this.el.dom) { return "above"; }
28208 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28209 var c = t + (b - t) / 2;
28210 var y = Roo.lib.Event.getPageY(e);
28218 onNodeEnter : function(n, dd, e, data){
28222 onNodeOver : function(n, dd, e, data){
28223 var pt = this.getDropPoint(e, n, dd);
28224 // set the insert point style on the target node
28225 var dragElClass = this.dropNotAllowed;
28228 if (pt == "above"){
28229 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28230 targetElClass = "x-view-drag-insert-above";
28232 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28233 targetElClass = "x-view-drag-insert-below";
28235 if (this.lastInsertClass != targetElClass){
28236 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28237 this.lastInsertClass = targetElClass;
28240 return dragElClass;
28243 onNodeOut : function(n, dd, e, data){
28244 this.removeDropIndicators(n);
28247 onNodeDrop : function(n, dd, e, data){
28248 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28251 var pt = this.getDropPoint(e, n, dd);
28252 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28253 if (pt == "below") { insertAt++; }
28254 for (var i = 0; i < data.records.length; i++) {
28255 var r = data.records[i];
28256 var dup = this.store.getById(r.id);
28257 if (dup && (dd != this.dragZone)) {
28258 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28261 this.store.insert(insertAt++, r.copy());
28263 data.source.isDirtyFlag = true;
28265 this.store.insert(insertAt++, r);
28267 this.isDirtyFlag = true;
28270 this.dragZone.cachedTarget = null;
28274 removeDropIndicators : function(n){
28276 Roo.fly(n).removeClass([
28277 "x-view-drag-insert-above",
28278 "x-view-drag-insert-below"]);
28279 this.lastInsertClass = "_noclass";
28284 * Utility method. Add a delete option to the DDView's context menu.
28285 * @param {String} imageUrl The URL of the "delete" icon image.
28287 setDeletable: function(imageUrl) {
28288 if (!this.singleSelect && !this.multiSelect) {
28289 this.singleSelect = true;
28291 var c = this.getContextMenu();
28292 this.contextMenu.on("itemclick", function(item) {
28295 this.remove(this.getSelectedIndexes());
28299 this.contextMenu.add({
28306 /** Return the context menu for this DDView. */
28307 getContextMenu: function() {
28308 if (!this.contextMenu) {
28309 // Create the View's context menu
28310 this.contextMenu = new Roo.menu.Menu({
28311 id: this.id + "-contextmenu"
28313 this.el.on("contextmenu", this.showContextMenu, this);
28315 return this.contextMenu;
28318 disableContextMenu: function() {
28319 if (this.contextMenu) {
28320 this.el.un("contextmenu", this.showContextMenu, this);
28324 showContextMenu: function(e, item) {
28325 item = this.findItemFromChild(e.getTarget());
28328 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28329 this.contextMenu.showAt(e.getXY());
28334 * Remove {@link Roo.data.Record}s at the specified indices.
28335 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28337 remove: function(selectedIndices) {
28338 selectedIndices = [].concat(selectedIndices);
28339 for (var i = 0; i < selectedIndices.length; i++) {
28340 var rec = this.store.getAt(selectedIndices[i]);
28341 this.store.remove(rec);
28346 * Double click fires the event, but also, if this is draggable, and there is only one other
28347 * related DropZone, it transfers the selected node.
28349 onDblClick : function(e){
28350 var item = this.findItemFromChild(e.getTarget());
28352 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28355 if (this.dragGroup) {
28356 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28357 while (targets.indexOf(this.dropZone) > -1) {
28358 targets.remove(this.dropZone);
28360 if (targets.length == 1) {
28361 this.dragZone.cachedTarget = null;
28362 var el = Roo.get(targets[0].getEl());
28363 var box = el.getBox(true);
28364 targets[0].onNodeDrop(el.dom, {
28366 xy: [box.x, box.y + box.height - 1]
28367 }, null, this.getDragData(e));
28373 handleSelection: function(e) {
28374 this.dragZone.cachedTarget = null;
28375 var item = this.findItemFromChild(e.getTarget());
28377 this.clearSelections(true);
28380 if (item && (this.multiSelect || this.singleSelect)){
28381 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28382 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28383 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28384 this.unselect(item);
28386 this.select(item, this.multiSelect && e.ctrlKey);
28387 this.lastSelection = item;
28392 onItemClick : function(item, index, e){
28393 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28399 unselect : function(nodeInfo, suppressEvent){
28400 var node = this.getNode(nodeInfo);
28401 if(node && this.isSelected(node)){
28402 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28403 Roo.fly(node).removeClass(this.selectedClass);
28404 this.selections.remove(node);
28405 if(!suppressEvent){
28406 this.fireEvent("selectionchange", this, this.selections);
28414 * Ext JS Library 1.1.1
28415 * Copyright(c) 2006-2007, Ext JS, LLC.
28417 * Originally Released Under LGPL - original licence link has changed is not relivant.
28420 * <script type="text/javascript">
28424 * @class Roo.LayoutManager
28425 * @extends Roo.util.Observable
28426 * Base class for layout managers.
28428 Roo.LayoutManager = function(container, config){
28429 Roo.LayoutManager.superclass.constructor.call(this);
28430 this.el = Roo.get(container);
28431 // ie scrollbar fix
28432 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28433 document.body.scroll = "no";
28434 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28435 this.el.position('relative');
28437 this.id = this.el.id;
28438 this.el.addClass("x-layout-container");
28439 /** false to disable window resize monitoring @type Boolean */
28440 this.monitorWindowResize = true;
28445 * Fires when a layout is performed.
28446 * @param {Roo.LayoutManager} this
28450 * @event regionresized
28451 * Fires when the user resizes a region.
28452 * @param {Roo.LayoutRegion} region The resized region
28453 * @param {Number} newSize The new size (width for east/west, height for north/south)
28455 "regionresized" : true,
28457 * @event regioncollapsed
28458 * Fires when a region is collapsed.
28459 * @param {Roo.LayoutRegion} region The collapsed region
28461 "regioncollapsed" : true,
28463 * @event regionexpanded
28464 * Fires when a region is expanded.
28465 * @param {Roo.LayoutRegion} region The expanded region
28467 "regionexpanded" : true
28469 this.updating = false;
28470 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28473 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28475 * Returns true if this layout is currently being updated
28476 * @return {Boolean}
28478 isUpdating : function(){
28479 return this.updating;
28483 * Suspend the LayoutManager from doing auto-layouts while
28484 * making multiple add or remove calls
28486 beginUpdate : function(){
28487 this.updating = true;
28491 * Restore auto-layouts and optionally disable the manager from performing a layout
28492 * @param {Boolean} noLayout true to disable a layout update
28494 endUpdate : function(noLayout){
28495 this.updating = false;
28501 layout: function(){
28505 onRegionResized : function(region, newSize){
28506 this.fireEvent("regionresized", region, newSize);
28510 onRegionCollapsed : function(region){
28511 this.fireEvent("regioncollapsed", region);
28514 onRegionExpanded : function(region){
28515 this.fireEvent("regionexpanded", region);
28519 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28520 * performs box-model adjustments.
28521 * @return {Object} The size as an object {width: (the width), height: (the height)}
28523 getViewSize : function(){
28525 if(this.el.dom != document.body){
28526 size = this.el.getSize();
28528 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28530 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28531 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28536 * Returns the Element this layout is bound to.
28537 * @return {Roo.Element}
28539 getEl : function(){
28544 * Returns the specified region.
28545 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28546 * @return {Roo.LayoutRegion}
28548 getRegion : function(target){
28549 return this.regions[target.toLowerCase()];
28552 onWindowResize : function(){
28553 if(this.monitorWindowResize){
28559 * Ext JS Library 1.1.1
28560 * Copyright(c) 2006-2007, Ext JS, LLC.
28562 * Originally Released Under LGPL - original licence link has changed is not relivant.
28565 * <script type="text/javascript">
28568 * @class Roo.BorderLayout
28569 * @extends Roo.LayoutManager
28570 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28571 * please see: <br><br>
28572 * <a href="http://www.jackslocum.com/yui/2006/10/19/cross-browser-web-20-layouts-with-yahoo-ui/">Cross Browser Layouts - Part 1</a><br>
28573 * <a href="http://www.jackslocum.com/yui/2006/10/28/cross-browser-web-20-layouts-part-2-ajax-feed-viewer-20/">Cross Browser Layouts - Part 2</a><br><br>
28576 var layout = new Roo.BorderLayout(document.body, {
28610 preferredTabWidth: 150
28615 var CP = Roo.ContentPanel;
28617 layout.beginUpdate();
28618 layout.add("north", new CP("north", "North"));
28619 layout.add("south", new CP("south", {title: "South", closable: true}));
28620 layout.add("west", new CP("west", {title: "West"}));
28621 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28622 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28623 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28624 layout.getRegion("center").showPanel("center1");
28625 layout.endUpdate();
28628 <b>The container the layout is rendered into can be either the body element or any other element.
28629 If it is not the body element, the container needs to either be an absolute positioned element,
28630 or you will need to add "position:relative" to the css of the container. You will also need to specify
28631 the container size if it is not the body element.</b>
28634 * Create a new BorderLayout
28635 * @param {String/HTMLElement/Element} container The container this layout is bound to
28636 * @param {Object} config Configuration options
28638 Roo.BorderLayout = function(container, config){
28639 config = config || {};
28640 Roo.BorderLayout.superclass.constructor.call(this, container, config);
28641 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28642 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28643 var target = this.factory.validRegions[i];
28644 if(config[target]){
28645 this.addRegion(target, config[target]);
28650 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28652 * Creates and adds a new region if it doesn't already exist.
28653 * @param {String} target The target region key (north, south, east, west or center).
28654 * @param {Object} config The regions config object
28655 * @return {BorderLayoutRegion} The new region
28657 addRegion : function(target, config){
28658 if(!this.regions[target]){
28659 var r = this.factory.create(target, this, config);
28660 this.bindRegion(target, r);
28662 return this.regions[target];
28666 bindRegion : function(name, r){
28667 this.regions[name] = r;
28668 r.on("visibilitychange", this.layout, this);
28669 r.on("paneladded", this.layout, this);
28670 r.on("panelremoved", this.layout, this);
28671 r.on("invalidated", this.layout, this);
28672 r.on("resized", this.onRegionResized, this);
28673 r.on("collapsed", this.onRegionCollapsed, this);
28674 r.on("expanded", this.onRegionExpanded, this);
28678 * Performs a layout update.
28680 layout : function(){
28681 if(this.updating) {
28684 var size = this.getViewSize();
28685 var w = size.width;
28686 var h = size.height;
28691 //var x = 0, y = 0;
28693 var rs = this.regions;
28694 var north = rs["north"];
28695 var south = rs["south"];
28696 var west = rs["west"];
28697 var east = rs["east"];
28698 var center = rs["center"];
28699 //if(this.hideOnLayout){ // not supported anymore
28700 //c.el.setStyle("display", "none");
28702 if(north && north.isVisible()){
28703 var b = north.getBox();
28704 var m = north.getMargins();
28705 b.width = w - (m.left+m.right);
28708 centerY = b.height + b.y + m.bottom;
28709 centerH -= centerY;
28710 north.updateBox(this.safeBox(b));
28712 if(south && south.isVisible()){
28713 var b = south.getBox();
28714 var m = south.getMargins();
28715 b.width = w - (m.left+m.right);
28717 var totalHeight = (b.height + m.top + m.bottom);
28718 b.y = h - totalHeight + m.top;
28719 centerH -= totalHeight;
28720 south.updateBox(this.safeBox(b));
28722 if(west && west.isVisible()){
28723 var b = west.getBox();
28724 var m = west.getMargins();
28725 b.height = centerH - (m.top+m.bottom);
28727 b.y = centerY + m.top;
28728 var totalWidth = (b.width + m.left + m.right);
28729 centerX += totalWidth;
28730 centerW -= totalWidth;
28731 west.updateBox(this.safeBox(b));
28733 if(east && east.isVisible()){
28734 var b = east.getBox();
28735 var m = east.getMargins();
28736 b.height = centerH - (m.top+m.bottom);
28737 var totalWidth = (b.width + m.left + m.right);
28738 b.x = w - totalWidth + m.left;
28739 b.y = centerY + m.top;
28740 centerW -= totalWidth;
28741 east.updateBox(this.safeBox(b));
28744 var m = center.getMargins();
28746 x: centerX + m.left,
28747 y: centerY + m.top,
28748 width: centerW - (m.left+m.right),
28749 height: centerH - (m.top+m.bottom)
28751 //if(this.hideOnLayout){
28752 //center.el.setStyle("display", "block");
28754 center.updateBox(this.safeBox(centerBox));
28757 this.fireEvent("layout", this);
28761 safeBox : function(box){
28762 box.width = Math.max(0, box.width);
28763 box.height = Math.max(0, box.height);
28768 * Adds a ContentPanel (or subclass) to this layout.
28769 * @param {String} target The target region key (north, south, east, west or center).
28770 * @param {Roo.ContentPanel} panel The panel to add
28771 * @return {Roo.ContentPanel} The added panel
28773 add : function(target, panel){
28775 target = target.toLowerCase();
28776 return this.regions[target].add(panel);
28780 * Remove a ContentPanel (or subclass) to this layout.
28781 * @param {String} target The target region key (north, south, east, west or center).
28782 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28783 * @return {Roo.ContentPanel} The removed panel
28785 remove : function(target, panel){
28786 target = target.toLowerCase();
28787 return this.regions[target].remove(panel);
28791 * Searches all regions for a panel with the specified id
28792 * @param {String} panelId
28793 * @return {Roo.ContentPanel} The panel or null if it wasn't found
28795 findPanel : function(panelId){
28796 var rs = this.regions;
28797 for(var target in rs){
28798 if(typeof rs[target] != "function"){
28799 var p = rs[target].getPanel(panelId);
28809 * Searches all regions for a panel with the specified id and activates (shows) it.
28810 * @param {String/ContentPanel} panelId The panels id or the panel itself
28811 * @return {Roo.ContentPanel} The shown panel or null
28813 showPanel : function(panelId) {
28814 var rs = this.regions;
28815 for(var target in rs){
28816 var r = rs[target];
28817 if(typeof r != "function"){
28818 if(r.hasPanel(panelId)){
28819 return r.showPanel(panelId);
28827 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28828 * @param {Roo.state.Provider} provider (optional) An alternate state provider
28830 restoreState : function(provider){
28832 provider = Roo.state.Manager;
28834 var sm = new Roo.LayoutStateManager();
28835 sm.init(this, provider);
28839 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
28840 * object should contain properties for each region to add ContentPanels to, and each property's value should be
28841 * a valid ContentPanel config object. Example:
28843 // Create the main layout
28844 var layout = new Roo.BorderLayout('main-ct', {
28855 // Create and add multiple ContentPanels at once via configs
28858 id: 'source-files',
28860 title:'Ext Source Files',
28873 * @param {Object} regions An object containing ContentPanel configs by region name
28875 batchAdd : function(regions){
28876 this.beginUpdate();
28877 for(var rname in regions){
28878 var lr = this.regions[rname];
28880 this.addTypedPanels(lr, regions[rname]);
28887 addTypedPanels : function(lr, ps){
28888 if(typeof ps == 'string'){
28889 lr.add(new Roo.ContentPanel(ps));
28891 else if(ps instanceof Array){
28892 for(var i =0, len = ps.length; i < len; i++){
28893 this.addTypedPanels(lr, ps[i]);
28896 else if(!ps.events){ // raw config?
28898 delete ps.el; // prevent conflict
28899 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
28901 else { // panel object assumed!
28906 * Adds a xtype elements to the layout.
28910 xtype : 'ContentPanel',
28917 xtype : 'NestedLayoutPanel',
28923 items : [ ... list of content panels or nested layout panels.. ]
28927 * @param {Object} cfg Xtype definition of item to add.
28929 addxtype : function(cfg)
28931 // basically accepts a pannel...
28932 // can accept a layout region..!?!?
28933 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
28935 if (!cfg.xtype.match(/Panel$/)) {
28940 if (typeof(cfg.region) == 'undefined') {
28941 Roo.log("Failed to add Panel, region was not set");
28945 var region = cfg.region;
28951 xitems = cfg.items;
28958 case 'ContentPanel': // ContentPanel (el, cfg)
28959 case 'ScrollPanel': // ContentPanel (el, cfg)
28961 if(cfg.autoCreate) {
28962 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28964 var el = this.el.createChild();
28965 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
28968 this.add(region, ret);
28972 case 'TreePanel': // our new panel!
28973 cfg.el = this.el.createChild();
28974 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28975 this.add(region, ret);
28978 case 'NestedLayoutPanel':
28979 // create a new Layout (which is a Border Layout...
28980 var el = this.el.createChild();
28981 var clayout = cfg.layout;
28983 clayout.items = clayout.items || [];
28984 // replace this exitems with the clayout ones..
28985 xitems = clayout.items;
28988 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
28989 cfg.background = false;
28991 var layout = new Roo.BorderLayout(el, clayout);
28993 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
28994 //console.log('adding nested layout panel ' + cfg.toSource());
28995 this.add(region, ret);
28996 nb = {}; /// find first...
29001 // needs grid and region
29003 //var el = this.getRegion(region).el.createChild();
29004 var el = this.el.createChild();
29005 // create the grid first...
29007 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29009 if (region == 'center' && this.active ) {
29010 cfg.background = false;
29012 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29014 this.add(region, ret);
29015 if (cfg.background) {
29016 ret.on('activate', function(gp) {
29017 if (!gp.grid.rendered) {
29032 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29034 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29035 this.add(region, ret);
29038 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29042 // GridPanel (grid, cfg)
29045 this.beginUpdate();
29049 Roo.each(xitems, function(i) {
29050 region = nb && i.region ? i.region : false;
29052 var add = ret.addxtype(i);
29055 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29056 if (!i.background) {
29057 abn[region] = nb[region] ;
29064 // make the last non-background panel active..
29065 //if (nb) { Roo.log(abn); }
29068 for(var r in abn) {
29069 region = this.getRegion(r);
29071 // tried using nb[r], but it does not work..
29073 region.showPanel(abn[r]);
29084 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29085 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
29086 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29087 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
29090 var CP = Roo.ContentPanel;
29092 var layout = Roo.BorderLayout.create({
29096 panels: [new CP("north", "North")]
29105 panels: [new CP("west", {title: "West"})]
29114 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29123 panels: [new CP("south", {title: "South", closable: true})]
29130 preferredTabWidth: 150,
29132 new CP("center1", {title: "Close Me", closable: true}),
29133 new CP("center2", {title: "Center Panel", closable: false})
29138 layout.getRegion("center").showPanel("center1");
29143 Roo.BorderLayout.create = function(config, targetEl){
29144 var layout = new Roo.BorderLayout(targetEl || document.body, config);
29145 layout.beginUpdate();
29146 var regions = Roo.BorderLayout.RegionFactory.validRegions;
29147 for(var j = 0, jlen = regions.length; j < jlen; j++){
29148 var lr = regions[j];
29149 if(layout.regions[lr] && config[lr].panels){
29150 var r = layout.regions[lr];
29151 var ps = config[lr].panels;
29152 layout.addTypedPanels(r, ps);
29155 layout.endUpdate();
29160 Roo.BorderLayout.RegionFactory = {
29162 validRegions : ["north","south","east","west","center"],
29165 create : function(target, mgr, config){
29166 target = target.toLowerCase();
29167 if(config.lightweight || config.basic){
29168 return new Roo.BasicLayoutRegion(mgr, config, target);
29172 return new Roo.NorthLayoutRegion(mgr, config);
29174 return new Roo.SouthLayoutRegion(mgr, config);
29176 return new Roo.EastLayoutRegion(mgr, config);
29178 return new Roo.WestLayoutRegion(mgr, config);
29180 return new Roo.CenterLayoutRegion(mgr, config);
29182 throw 'Layout region "'+target+'" not supported.';
29186 * Ext JS Library 1.1.1
29187 * Copyright(c) 2006-2007, Ext JS, LLC.
29189 * Originally Released Under LGPL - original licence link has changed is not relivant.
29192 * <script type="text/javascript">
29196 * @class Roo.BasicLayoutRegion
29197 * @extends Roo.util.Observable
29198 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29199 * and does not have a titlebar, tabs or any other features. All it does is size and position
29200 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29202 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29204 this.position = pos;
29207 * @scope Roo.BasicLayoutRegion
29211 * @event beforeremove
29212 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29213 * @param {Roo.LayoutRegion} this
29214 * @param {Roo.ContentPanel} panel The panel
29215 * @param {Object} e The cancel event object
29217 "beforeremove" : true,
29219 * @event invalidated
29220 * Fires when the layout for this region is changed.
29221 * @param {Roo.LayoutRegion} this
29223 "invalidated" : true,
29225 * @event visibilitychange
29226 * Fires when this region is shown or hidden
29227 * @param {Roo.LayoutRegion} this
29228 * @param {Boolean} visibility true or false
29230 "visibilitychange" : true,
29232 * @event paneladded
29233 * Fires when a panel is added.
29234 * @param {Roo.LayoutRegion} this
29235 * @param {Roo.ContentPanel} panel The panel
29237 "paneladded" : true,
29239 * @event panelremoved
29240 * Fires when a panel is removed.
29241 * @param {Roo.LayoutRegion} this
29242 * @param {Roo.ContentPanel} panel The panel
29244 "panelremoved" : true,
29246 * @event beforecollapse
29247 * Fires when this region before collapse.
29248 * @param {Roo.LayoutRegion} this
29250 "beforecollapse" : true,
29253 * Fires when this region is collapsed.
29254 * @param {Roo.LayoutRegion} this
29256 "collapsed" : true,
29259 * Fires when this region is expanded.
29260 * @param {Roo.LayoutRegion} this
29265 * Fires when this region is slid into view.
29266 * @param {Roo.LayoutRegion} this
29268 "slideshow" : true,
29271 * Fires when this region slides out of view.
29272 * @param {Roo.LayoutRegion} this
29274 "slidehide" : true,
29276 * @event panelactivated
29277 * Fires when a panel is activated.
29278 * @param {Roo.LayoutRegion} this
29279 * @param {Roo.ContentPanel} panel The activated panel
29281 "panelactivated" : true,
29284 * Fires when the user resizes this region.
29285 * @param {Roo.LayoutRegion} this
29286 * @param {Number} newSize The new size (width for east/west, height for north/south)
29290 /** A collection of panels in this region. @type Roo.util.MixedCollection */
29291 this.panels = new Roo.util.MixedCollection();
29292 this.panels.getKey = this.getPanelId.createDelegate(this);
29294 this.activePanel = null;
29295 // ensure listeners are added...
29297 if (config.listeners || config.events) {
29298 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29299 listeners : config.listeners || {},
29300 events : config.events || {}
29304 if(skipConfig !== true){
29305 this.applyConfig(config);
29309 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29310 getPanelId : function(p){
29314 applyConfig : function(config){
29315 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29316 this.config = config;
29321 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
29322 * the width, for horizontal (north, south) the height.
29323 * @param {Number} newSize The new width or height
29325 resizeTo : function(newSize){
29326 var el = this.el ? this.el :
29327 (this.activePanel ? this.activePanel.getEl() : null);
29329 switch(this.position){
29332 el.setWidth(newSize);
29333 this.fireEvent("resized", this, newSize);
29337 el.setHeight(newSize);
29338 this.fireEvent("resized", this, newSize);
29344 getBox : function(){
29345 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29348 getMargins : function(){
29349 return this.margins;
29352 updateBox : function(box){
29354 var el = this.activePanel.getEl();
29355 el.dom.style.left = box.x + "px";
29356 el.dom.style.top = box.y + "px";
29357 this.activePanel.setSize(box.width, box.height);
29361 * Returns the container element for this region.
29362 * @return {Roo.Element}
29364 getEl : function(){
29365 return this.activePanel;
29369 * Returns true if this region is currently visible.
29370 * @return {Boolean}
29372 isVisible : function(){
29373 return this.activePanel ? true : false;
29376 setActivePanel : function(panel){
29377 panel = this.getPanel(panel);
29378 if(this.activePanel && this.activePanel != panel){
29379 this.activePanel.setActiveState(false);
29380 this.activePanel.getEl().setLeftTop(-10000,-10000);
29382 this.activePanel = panel;
29383 panel.setActiveState(true);
29385 panel.setSize(this.box.width, this.box.height);
29387 this.fireEvent("panelactivated", this, panel);
29388 this.fireEvent("invalidated");
29392 * Show the specified panel.
29393 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29394 * @return {Roo.ContentPanel} The shown panel or null
29396 showPanel : function(panel){
29397 if(panel = this.getPanel(panel)){
29398 this.setActivePanel(panel);
29404 * Get the active panel for this region.
29405 * @return {Roo.ContentPanel} The active panel or null
29407 getActivePanel : function(){
29408 return this.activePanel;
29412 * Add the passed ContentPanel(s)
29413 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29414 * @return {Roo.ContentPanel} The panel added (if only one was added)
29416 add : function(panel){
29417 if(arguments.length > 1){
29418 for(var i = 0, len = arguments.length; i < len; i++) {
29419 this.add(arguments[i]);
29423 if(this.hasPanel(panel)){
29424 this.showPanel(panel);
29427 var el = panel.getEl();
29428 if(el.dom.parentNode != this.mgr.el.dom){
29429 this.mgr.el.dom.appendChild(el.dom);
29431 if(panel.setRegion){
29432 panel.setRegion(this);
29434 this.panels.add(panel);
29435 el.setStyle("position", "absolute");
29436 if(!panel.background){
29437 this.setActivePanel(panel);
29438 if(this.config.initialSize && this.panels.getCount()==1){
29439 this.resizeTo(this.config.initialSize);
29442 this.fireEvent("paneladded", this, panel);
29447 * Returns true if the panel is in this region.
29448 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29449 * @return {Boolean}
29451 hasPanel : function(panel){
29452 if(typeof panel == "object"){ // must be panel obj
29453 panel = panel.getId();
29455 return this.getPanel(panel) ? true : false;
29459 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29460 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29461 * @param {Boolean} preservePanel Overrides the config preservePanel option
29462 * @return {Roo.ContentPanel} The panel that was removed
29464 remove : function(panel, preservePanel){
29465 panel = this.getPanel(panel);
29470 this.fireEvent("beforeremove", this, panel, e);
29471 if(e.cancel === true){
29474 var panelId = panel.getId();
29475 this.panels.removeKey(panelId);
29480 * Returns the panel specified or null if it's not in this region.
29481 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29482 * @return {Roo.ContentPanel}
29484 getPanel : function(id){
29485 if(typeof id == "object"){ // must be panel obj
29488 return this.panels.get(id);
29492 * Returns this regions position (north/south/east/west/center).
29495 getPosition: function(){
29496 return this.position;
29500 * Ext JS Library 1.1.1
29501 * Copyright(c) 2006-2007, Ext JS, LLC.
29503 * Originally Released Under LGPL - original licence link has changed is not relivant.
29506 * <script type="text/javascript">
29510 * @class Roo.LayoutRegion
29511 * @extends Roo.BasicLayoutRegion
29512 * This class represents a region in a layout manager.
29513 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
29514 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
29515 * @cfg {Boolean} floatable False to disable floating (defaults to true)
29516 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29517 * @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})
29518 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
29519 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
29520 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
29521 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
29522 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
29523 * @cfg {String} title The title for the region (overrides panel titles)
29524 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
29525 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29526 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
29527 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29528 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
29529 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29530 * the space available, similar to FireFox 1.5 tabs (defaults to false)
29531 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
29532 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
29533 * @cfg {Boolean} showPin True to show a pin button
29534 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
29535 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
29536 * @cfg {Boolean} disableTabTips True to disable tab tooltips
29537 * @cfg {Number} width For East/West panels
29538 * @cfg {Number} height For North/South panels
29539 * @cfg {Boolean} split To show the splitter
29540 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
29542 Roo.LayoutRegion = function(mgr, config, pos){
29543 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29544 var dh = Roo.DomHelper;
29545 /** This region's container element
29546 * @type Roo.Element */
29547 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29548 /** This region's title element
29549 * @type Roo.Element */
29551 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29552 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
29553 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29555 this.titleEl.enableDisplayMode();
29556 /** This region's title text element
29557 * @type HTMLElement */
29558 this.titleTextEl = this.titleEl.dom.firstChild;
29559 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29560 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29561 this.closeBtn.enableDisplayMode();
29562 this.closeBtn.on("click", this.closeClicked, this);
29563 this.closeBtn.hide();
29565 this.createBody(config);
29566 this.visible = true;
29567 this.collapsed = false;
29569 if(config.hideWhenEmpty){
29571 this.on("paneladded", this.validateVisibility, this);
29572 this.on("panelremoved", this.validateVisibility, this);
29574 this.applyConfig(config);
29577 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29579 createBody : function(){
29580 /** This region's body element
29581 * @type Roo.Element */
29582 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29585 applyConfig : function(c){
29586 if(c.collapsible && this.position != "center" && !this.collapsedEl){
29587 var dh = Roo.DomHelper;
29588 if(c.titlebar !== false){
29589 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29590 this.collapseBtn.on("click", this.collapse, this);
29591 this.collapseBtn.enableDisplayMode();
29593 if(c.showPin === true || this.showPin){
29594 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29595 this.stickBtn.enableDisplayMode();
29596 this.stickBtn.on("click", this.expand, this);
29597 this.stickBtn.hide();
29600 /** This region's collapsed element
29601 * @type Roo.Element */
29602 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29603 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29605 if(c.floatable !== false){
29606 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29607 this.collapsedEl.on("click", this.collapseClick, this);
29610 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29611 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29612 id: "message", unselectable: "on", style:{"float":"left"}});
29613 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29615 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29616 this.expandBtn.on("click", this.expand, this);
29618 if(this.collapseBtn){
29619 this.collapseBtn.setVisible(c.collapsible == true);
29621 this.cmargins = c.cmargins || this.cmargins ||
29622 (this.position == "west" || this.position == "east" ?
29623 {top: 0, left: 2, right:2, bottom: 0} :
29624 {top: 2, left: 0, right:0, bottom: 2});
29625 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29626 this.bottomTabs = c.tabPosition != "top";
29627 this.autoScroll = c.autoScroll || false;
29628 if(this.autoScroll){
29629 this.bodyEl.setStyle("overflow", "auto");
29631 this.bodyEl.setStyle("overflow", "hidden");
29633 //if(c.titlebar !== false){
29634 if((!c.titlebar && !c.title) || c.titlebar === false){
29635 this.titleEl.hide();
29637 this.titleEl.show();
29639 this.titleTextEl.innerHTML = c.title;
29643 this.duration = c.duration || .30;
29644 this.slideDuration = c.slideDuration || .45;
29647 this.collapse(true);
29654 * Returns true if this region is currently visible.
29655 * @return {Boolean}
29657 isVisible : function(){
29658 return this.visible;
29662 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29663 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
29665 setCollapsedTitle : function(title){
29666 title = title || " ";
29667 if(this.collapsedTitleTextEl){
29668 this.collapsedTitleTextEl.innerHTML = title;
29672 getBox : function(){
29674 if(!this.collapsed){
29675 b = this.el.getBox(false, true);
29677 b = this.collapsedEl.getBox(false, true);
29682 getMargins : function(){
29683 return this.collapsed ? this.cmargins : this.margins;
29686 highlight : function(){
29687 this.el.addClass("x-layout-panel-dragover");
29690 unhighlight : function(){
29691 this.el.removeClass("x-layout-panel-dragover");
29694 updateBox : function(box){
29696 if(!this.collapsed){
29697 this.el.dom.style.left = box.x + "px";
29698 this.el.dom.style.top = box.y + "px";
29699 this.updateBody(box.width, box.height);
29701 this.collapsedEl.dom.style.left = box.x + "px";
29702 this.collapsedEl.dom.style.top = box.y + "px";
29703 this.collapsedEl.setSize(box.width, box.height);
29706 this.tabs.autoSizeTabs();
29710 updateBody : function(w, h){
29712 this.el.setWidth(w);
29713 w -= this.el.getBorderWidth("rl");
29714 if(this.config.adjustments){
29715 w += this.config.adjustments[0];
29719 this.el.setHeight(h);
29720 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29721 h -= this.el.getBorderWidth("tb");
29722 if(this.config.adjustments){
29723 h += this.config.adjustments[1];
29725 this.bodyEl.setHeight(h);
29727 h = this.tabs.syncHeight(h);
29730 if(this.panelSize){
29731 w = w !== null ? w : this.panelSize.width;
29732 h = h !== null ? h : this.panelSize.height;
29734 if(this.activePanel){
29735 var el = this.activePanel.getEl();
29736 w = w !== null ? w : el.getWidth();
29737 h = h !== null ? h : el.getHeight();
29738 this.panelSize = {width: w, height: h};
29739 this.activePanel.setSize(w, h);
29741 if(Roo.isIE && this.tabs){
29742 this.tabs.el.repaint();
29747 * Returns the container element for this region.
29748 * @return {Roo.Element}
29750 getEl : function(){
29755 * Hides this region.
29758 if(!this.collapsed){
29759 this.el.dom.style.left = "-2000px";
29762 this.collapsedEl.dom.style.left = "-2000px";
29763 this.collapsedEl.hide();
29765 this.visible = false;
29766 this.fireEvent("visibilitychange", this, false);
29770 * Shows this region if it was previously hidden.
29773 if(!this.collapsed){
29776 this.collapsedEl.show();
29778 this.visible = true;
29779 this.fireEvent("visibilitychange", this, true);
29782 closeClicked : function(){
29783 if(this.activePanel){
29784 this.remove(this.activePanel);
29788 collapseClick : function(e){
29790 e.stopPropagation();
29793 e.stopPropagation();
29799 * Collapses this region.
29800 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29802 collapse : function(skipAnim, skipCheck = false){
29803 if(this.collapsed) {
29807 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
29809 this.collapsed = true;
29811 this.split.el.hide();
29813 if(this.config.animate && skipAnim !== true){
29814 this.fireEvent("invalidated", this);
29815 this.animateCollapse();
29817 this.el.setLocation(-20000,-20000);
29819 this.collapsedEl.show();
29820 this.fireEvent("collapsed", this);
29821 this.fireEvent("invalidated", this);
29827 animateCollapse : function(){
29832 * Expands this region if it was previously collapsed.
29833 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29834 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29836 expand : function(e, skipAnim){
29838 e.stopPropagation();
29840 if(!this.collapsed || this.el.hasActiveFx()) {
29844 this.afterSlideIn();
29847 this.collapsed = false;
29848 if(this.config.animate && skipAnim !== true){
29849 this.animateExpand();
29853 this.split.el.show();
29855 this.collapsedEl.setLocation(-2000,-2000);
29856 this.collapsedEl.hide();
29857 this.fireEvent("invalidated", this);
29858 this.fireEvent("expanded", this);
29862 animateExpand : function(){
29866 initTabs : function()
29868 this.bodyEl.setStyle("overflow", "hidden");
29869 var ts = new Roo.TabPanel(
29872 tabPosition: this.bottomTabs ? 'bottom' : 'top',
29873 disableTooltips: this.config.disableTabTips,
29874 toolbar : this.config.toolbar
29877 if(this.config.hideTabs){
29878 ts.stripWrap.setDisplayed(false);
29881 ts.resizeTabs = this.config.resizeTabs === true;
29882 ts.minTabWidth = this.config.minTabWidth || 40;
29883 ts.maxTabWidth = this.config.maxTabWidth || 250;
29884 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
29885 ts.monitorResize = false;
29886 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29887 ts.bodyEl.addClass('x-layout-tabs-body');
29888 this.panels.each(this.initPanelAsTab, this);
29891 initPanelAsTab : function(panel){
29892 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
29893 this.config.closeOnTab && panel.isClosable());
29894 if(panel.tabTip !== undefined){
29895 ti.setTooltip(panel.tabTip);
29897 ti.on("activate", function(){
29898 this.setActivePanel(panel);
29900 if(this.config.closeOnTab){
29901 ti.on("beforeclose", function(t, e){
29903 this.remove(panel);
29909 updatePanelTitle : function(panel, title){
29910 if(this.activePanel == panel){
29911 this.updateTitle(title);
29914 var ti = this.tabs.getTab(panel.getEl().id);
29916 if(panel.tabTip !== undefined){
29917 ti.setTooltip(panel.tabTip);
29922 updateTitle : function(title){
29923 if(this.titleTextEl && !this.config.title){
29924 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
29928 setActivePanel : function(panel){
29929 panel = this.getPanel(panel);
29930 if(this.activePanel && this.activePanel != panel){
29931 this.activePanel.setActiveState(false);
29933 this.activePanel = panel;
29934 panel.setActiveState(true);
29935 if(this.panelSize){
29936 panel.setSize(this.panelSize.width, this.panelSize.height);
29939 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
29941 this.updateTitle(panel.getTitle());
29943 this.fireEvent("invalidated", this);
29945 this.fireEvent("panelactivated", this, panel);
29949 * Shows the specified panel.
29950 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
29951 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
29953 showPanel : function(panel)
29955 panel = this.getPanel(panel);
29958 var tab = this.tabs.getTab(panel.getEl().id);
29959 if(tab.isHidden()){
29960 this.tabs.unhideTab(tab.id);
29964 this.setActivePanel(panel);
29971 * Get the active panel for this region.
29972 * @return {Roo.ContentPanel} The active panel or null
29974 getActivePanel : function(){
29975 return this.activePanel;
29978 validateVisibility : function(){
29979 if(this.panels.getCount() < 1){
29980 this.updateTitle(" ");
29981 this.closeBtn.hide();
29984 if(!this.isVisible()){
29991 * Adds the passed ContentPanel(s) to this region.
29992 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29993 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
29995 add : function(panel){
29996 if(arguments.length > 1){
29997 for(var i = 0, len = arguments.length; i < len; i++) {
29998 this.add(arguments[i]);
30002 if(this.hasPanel(panel)){
30003 this.showPanel(panel);
30006 panel.setRegion(this);
30007 this.panels.add(panel);
30008 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30009 this.bodyEl.dom.appendChild(panel.getEl().dom);
30010 if(panel.background !== true){
30011 this.setActivePanel(panel);
30013 this.fireEvent("paneladded", this, panel);
30019 this.initPanelAsTab(panel);
30021 if(panel.background !== true){
30022 this.tabs.activate(panel.getEl().id);
30024 this.fireEvent("paneladded", this, panel);
30029 * Hides the tab for the specified panel.
30030 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30032 hidePanel : function(panel){
30033 if(this.tabs && (panel = this.getPanel(panel))){
30034 this.tabs.hideTab(panel.getEl().id);
30039 * Unhides the tab for a previously hidden panel.
30040 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30042 unhidePanel : function(panel){
30043 if(this.tabs && (panel = this.getPanel(panel))){
30044 this.tabs.unhideTab(panel.getEl().id);
30048 clearPanels : function(){
30049 while(this.panels.getCount() > 0){
30050 this.remove(this.panels.first());
30055 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30056 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30057 * @param {Boolean} preservePanel Overrides the config preservePanel option
30058 * @return {Roo.ContentPanel} The panel that was removed
30060 remove : function(panel, preservePanel){
30061 panel = this.getPanel(panel);
30066 this.fireEvent("beforeremove", this, panel, e);
30067 if(e.cancel === true){
30070 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30071 var panelId = panel.getId();
30072 this.panels.removeKey(panelId);
30074 document.body.appendChild(panel.getEl().dom);
30077 this.tabs.removeTab(panel.getEl().id);
30078 }else if (!preservePanel){
30079 this.bodyEl.dom.removeChild(panel.getEl().dom);
30081 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30082 var p = this.panels.first();
30083 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30084 tempEl.appendChild(p.getEl().dom);
30085 this.bodyEl.update("");
30086 this.bodyEl.dom.appendChild(p.getEl().dom);
30088 this.updateTitle(p.getTitle());
30090 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30091 this.setActivePanel(p);
30093 panel.setRegion(null);
30094 if(this.activePanel == panel){
30095 this.activePanel = null;
30097 if(this.config.autoDestroy !== false && preservePanel !== true){
30098 try{panel.destroy();}catch(e){}
30100 this.fireEvent("panelremoved", this, panel);
30105 * Returns the TabPanel component used by this region
30106 * @return {Roo.TabPanel}
30108 getTabs : function(){
30112 createTool : function(parentEl, className){
30113 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30114 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
30115 btn.addClassOnOver("x-layout-tools-button-over");
30120 * Ext JS Library 1.1.1
30121 * Copyright(c) 2006-2007, Ext JS, LLC.
30123 * Originally Released Under LGPL - original licence link has changed is not relivant.
30126 * <script type="text/javascript">
30132 * @class Roo.SplitLayoutRegion
30133 * @extends Roo.LayoutRegion
30134 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30136 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30137 this.cursor = cursor;
30138 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30141 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30142 splitTip : "Drag to resize.",
30143 collapsibleSplitTip : "Drag to resize. Double click to hide.",
30144 useSplitTips : false,
30146 applyConfig : function(config){
30147 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30150 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
30151 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
30152 /** The SplitBar for this region
30153 * @type Roo.SplitBar */
30154 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30155 this.split.on("moved", this.onSplitMove, this);
30156 this.split.useShim = config.useShim === true;
30157 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30158 if(this.useSplitTips){
30159 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30161 if(config.collapsible){
30162 this.split.el.on("dblclick", this.collapse, this);
30165 if(typeof config.minSize != "undefined"){
30166 this.split.minSize = config.minSize;
30168 if(typeof config.maxSize != "undefined"){
30169 this.split.maxSize = config.maxSize;
30171 if(config.hideWhenEmpty || config.hidden || config.collapsed){
30172 this.hideSplitter();
30177 getHMaxSize : function(){
30178 var cmax = this.config.maxSize || 10000;
30179 var center = this.mgr.getRegion("center");
30180 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30183 getVMaxSize : function(){
30184 var cmax = this.config.maxSize || 10000;
30185 var center = this.mgr.getRegion("center");
30186 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30189 onSplitMove : function(split, newSize){
30190 this.fireEvent("resized", this, newSize);
30194 * Returns the {@link Roo.SplitBar} for this region.
30195 * @return {Roo.SplitBar}
30197 getSplitBar : function(){
30202 this.hideSplitter();
30203 Roo.SplitLayoutRegion.superclass.hide.call(this);
30206 hideSplitter : function(){
30208 this.split.el.setLocation(-2000,-2000);
30209 this.split.el.hide();
30215 this.split.el.show();
30217 Roo.SplitLayoutRegion.superclass.show.call(this);
30220 beforeSlide: function(){
30221 if(Roo.isGecko){// firefox overflow auto bug workaround
30222 this.bodyEl.clip();
30224 this.tabs.bodyEl.clip();
30226 if(this.activePanel){
30227 this.activePanel.getEl().clip();
30229 if(this.activePanel.beforeSlide){
30230 this.activePanel.beforeSlide();
30236 afterSlide : function(){
30237 if(Roo.isGecko){// firefox overflow auto bug workaround
30238 this.bodyEl.unclip();
30240 this.tabs.bodyEl.unclip();
30242 if(this.activePanel){
30243 this.activePanel.getEl().unclip();
30244 if(this.activePanel.afterSlide){
30245 this.activePanel.afterSlide();
30251 initAutoHide : function(){
30252 if(this.autoHide !== false){
30253 if(!this.autoHideHd){
30254 var st = new Roo.util.DelayedTask(this.slideIn, this);
30255 this.autoHideHd = {
30256 "mouseout": function(e){
30257 if(!e.within(this.el, true)){
30261 "mouseover" : function(e){
30267 this.el.on(this.autoHideHd);
30271 clearAutoHide : function(){
30272 if(this.autoHide !== false){
30273 this.el.un("mouseout", this.autoHideHd.mouseout);
30274 this.el.un("mouseover", this.autoHideHd.mouseover);
30278 clearMonitor : function(){
30279 Roo.get(document).un("click", this.slideInIf, this);
30282 // these names are backwards but not changed for compat
30283 slideOut : function(){
30284 if(this.isSlid || this.el.hasActiveFx()){
30287 this.isSlid = true;
30288 if(this.collapseBtn){
30289 this.collapseBtn.hide();
30291 this.closeBtnState = this.closeBtn.getStyle('display');
30292 this.closeBtn.hide();
30294 this.stickBtn.show();
30297 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30298 this.beforeSlide();
30299 this.el.setStyle("z-index", 10001);
30300 this.el.slideIn(this.getSlideAnchor(), {
30301 callback: function(){
30303 this.initAutoHide();
30304 Roo.get(document).on("click", this.slideInIf, this);
30305 this.fireEvent("slideshow", this);
30312 afterSlideIn : function(){
30313 this.clearAutoHide();
30314 this.isSlid = false;
30315 this.clearMonitor();
30316 this.el.setStyle("z-index", "");
30317 if(this.collapseBtn){
30318 this.collapseBtn.show();
30320 this.closeBtn.setStyle('display', this.closeBtnState);
30322 this.stickBtn.hide();
30324 this.fireEvent("slidehide", this);
30327 slideIn : function(cb){
30328 if(!this.isSlid || this.el.hasActiveFx()){
30332 this.isSlid = false;
30333 this.beforeSlide();
30334 this.el.slideOut(this.getSlideAnchor(), {
30335 callback: function(){
30336 this.el.setLeftTop(-10000, -10000);
30338 this.afterSlideIn();
30346 slideInIf : function(e){
30347 if(!e.within(this.el)){
30352 animateCollapse : function(){
30353 this.beforeSlide();
30354 this.el.setStyle("z-index", 20000);
30355 var anchor = this.getSlideAnchor();
30356 this.el.slideOut(anchor, {
30357 callback : function(){
30358 this.el.setStyle("z-index", "");
30359 this.collapsedEl.slideIn(anchor, {duration:.3});
30361 this.el.setLocation(-10000,-10000);
30363 this.fireEvent("collapsed", this);
30370 animateExpand : function(){
30371 this.beforeSlide();
30372 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30373 this.el.setStyle("z-index", 20000);
30374 this.collapsedEl.hide({
30377 this.el.slideIn(this.getSlideAnchor(), {
30378 callback : function(){
30379 this.el.setStyle("z-index", "");
30382 this.split.el.show();
30384 this.fireEvent("invalidated", this);
30385 this.fireEvent("expanded", this);
30413 getAnchor : function(){
30414 return this.anchors[this.position];
30417 getCollapseAnchor : function(){
30418 return this.canchors[this.position];
30421 getSlideAnchor : function(){
30422 return this.sanchors[this.position];
30425 getAlignAdj : function(){
30426 var cm = this.cmargins;
30427 switch(this.position){
30443 getExpandAdj : function(){
30444 var c = this.collapsedEl, cm = this.cmargins;
30445 switch(this.position){
30447 return [-(cm.right+c.getWidth()+cm.left), 0];
30450 return [cm.right+c.getWidth()+cm.left, 0];
30453 return [0, -(cm.top+cm.bottom+c.getHeight())];
30456 return [0, cm.top+cm.bottom+c.getHeight()];
30462 * Ext JS Library 1.1.1
30463 * Copyright(c) 2006-2007, Ext JS, LLC.
30465 * Originally Released Under LGPL - original licence link has changed is not relivant.
30468 * <script type="text/javascript">
30471 * These classes are private internal classes
30473 Roo.CenterLayoutRegion = function(mgr, config){
30474 Roo.LayoutRegion.call(this, mgr, config, "center");
30475 this.visible = true;
30476 this.minWidth = config.minWidth || 20;
30477 this.minHeight = config.minHeight || 20;
30480 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30482 // center panel can't be hidden
30486 // center panel can't be hidden
30489 getMinWidth: function(){
30490 return this.minWidth;
30493 getMinHeight: function(){
30494 return this.minHeight;
30499 Roo.NorthLayoutRegion = function(mgr, config){
30500 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30502 this.split.placement = Roo.SplitBar.TOP;
30503 this.split.orientation = Roo.SplitBar.VERTICAL;
30504 this.split.el.addClass("x-layout-split-v");
30506 var size = config.initialSize || config.height;
30507 if(typeof size != "undefined"){
30508 this.el.setHeight(size);
30511 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30512 orientation: Roo.SplitBar.VERTICAL,
30513 getBox : function(){
30514 if(this.collapsed){
30515 return this.collapsedEl.getBox();
30517 var box = this.el.getBox();
30519 box.height += this.split.el.getHeight();
30524 updateBox : function(box){
30525 if(this.split && !this.collapsed){
30526 box.height -= this.split.el.getHeight();
30527 this.split.el.setLeft(box.x);
30528 this.split.el.setTop(box.y+box.height);
30529 this.split.el.setWidth(box.width);
30531 if(this.collapsed){
30532 this.updateBody(box.width, null);
30534 Roo.LayoutRegion.prototype.updateBox.call(this, box);
30538 Roo.SouthLayoutRegion = function(mgr, config){
30539 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30541 this.split.placement = Roo.SplitBar.BOTTOM;
30542 this.split.orientation = Roo.SplitBar.VERTICAL;
30543 this.split.el.addClass("x-layout-split-v");
30545 var size = config.initialSize || config.height;
30546 if(typeof size != "undefined"){
30547 this.el.setHeight(size);
30550 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30551 orientation: Roo.SplitBar.VERTICAL,
30552 getBox : function(){
30553 if(this.collapsed){
30554 return this.collapsedEl.getBox();
30556 var box = this.el.getBox();
30558 var sh = this.split.el.getHeight();
30565 updateBox : function(box){
30566 if(this.split && !this.collapsed){
30567 var sh = this.split.el.getHeight();
30570 this.split.el.setLeft(box.x);
30571 this.split.el.setTop(box.y-sh);
30572 this.split.el.setWidth(box.width);
30574 if(this.collapsed){
30575 this.updateBody(box.width, null);
30577 Roo.LayoutRegion.prototype.updateBox.call(this, box);
30581 Roo.EastLayoutRegion = function(mgr, config){
30582 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30584 this.split.placement = Roo.SplitBar.RIGHT;
30585 this.split.orientation = Roo.SplitBar.HORIZONTAL;
30586 this.split.el.addClass("x-layout-split-h");
30588 var size = config.initialSize || config.width;
30589 if(typeof size != "undefined"){
30590 this.el.setWidth(size);
30593 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30594 orientation: Roo.SplitBar.HORIZONTAL,
30595 getBox : function(){
30596 if(this.collapsed){
30597 return this.collapsedEl.getBox();
30599 var box = this.el.getBox();
30601 var sw = this.split.el.getWidth();
30608 updateBox : function(box){
30609 if(this.split && !this.collapsed){
30610 var sw = this.split.el.getWidth();
30612 this.split.el.setLeft(box.x);
30613 this.split.el.setTop(box.y);
30614 this.split.el.setHeight(box.height);
30617 if(this.collapsed){
30618 this.updateBody(null, box.height);
30620 Roo.LayoutRegion.prototype.updateBox.call(this, box);
30624 Roo.WestLayoutRegion = function(mgr, config){
30625 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30627 this.split.placement = Roo.SplitBar.LEFT;
30628 this.split.orientation = Roo.SplitBar.HORIZONTAL;
30629 this.split.el.addClass("x-layout-split-h");
30631 var size = config.initialSize || config.width;
30632 if(typeof size != "undefined"){
30633 this.el.setWidth(size);
30636 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30637 orientation: Roo.SplitBar.HORIZONTAL,
30638 getBox : function(){
30639 if(this.collapsed){
30640 return this.collapsedEl.getBox();
30642 var box = this.el.getBox();
30644 box.width += this.split.el.getWidth();
30649 updateBox : function(box){
30650 if(this.split && !this.collapsed){
30651 var sw = this.split.el.getWidth();
30653 this.split.el.setLeft(box.x+box.width);
30654 this.split.el.setTop(box.y);
30655 this.split.el.setHeight(box.height);
30657 if(this.collapsed){
30658 this.updateBody(null, box.height);
30660 Roo.LayoutRegion.prototype.updateBox.call(this, box);
30665 * Ext JS Library 1.1.1
30666 * Copyright(c) 2006-2007, Ext JS, LLC.
30668 * Originally Released Under LGPL - original licence link has changed is not relivant.
30671 * <script type="text/javascript">
30676 * Private internal class for reading and applying state
30678 Roo.LayoutStateManager = function(layout){
30679 // default empty state
30688 Roo.LayoutStateManager.prototype = {
30689 init : function(layout, provider){
30690 this.provider = provider;
30691 var state = provider.get(layout.id+"-layout-state");
30693 var wasUpdating = layout.isUpdating();
30695 layout.beginUpdate();
30697 for(var key in state){
30698 if(typeof state[key] != "function"){
30699 var rstate = state[key];
30700 var r = layout.getRegion(key);
30703 r.resizeTo(rstate.size);
30705 if(rstate.collapsed == true){
30708 r.expand(null, true);
30714 layout.endUpdate();
30716 this.state = state;
30718 this.layout = layout;
30719 layout.on("regionresized", this.onRegionResized, this);
30720 layout.on("regioncollapsed", this.onRegionCollapsed, this);
30721 layout.on("regionexpanded", this.onRegionExpanded, this);
30724 storeState : function(){
30725 this.provider.set(this.layout.id+"-layout-state", this.state);
30728 onRegionResized : function(region, newSize){
30729 this.state[region.getPosition()].size = newSize;
30733 onRegionCollapsed : function(region){
30734 this.state[region.getPosition()].collapsed = true;
30738 onRegionExpanded : function(region){
30739 this.state[region.getPosition()].collapsed = false;
30744 * Ext JS Library 1.1.1
30745 * Copyright(c) 2006-2007, Ext JS, LLC.
30747 * Originally Released Under LGPL - original licence link has changed is not relivant.
30750 * <script type="text/javascript">
30753 * @class Roo.ContentPanel
30754 * @extends Roo.util.Observable
30755 * A basic ContentPanel element.
30756 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
30757 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
30758 * @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
30759 * @cfg {Boolean} closable True if the panel can be closed/removed
30760 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
30761 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30762 * @cfg {Toolbar} toolbar A toolbar for this panel
30763 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
30764 * @cfg {String} title The title for this panel
30765 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30766 * @cfg {String} url Calls {@link #setUrl} with this value
30767 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30768 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
30769 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
30770 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
30773 * Create a new ContentPanel.
30774 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30775 * @param {String/Object} config A string to set only the title or a config object
30776 * @param {String} content (optional) Set the HTML content for this panel
30777 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30779 Roo.ContentPanel = function(el, config, content){
30783 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30787 if (config && config.parentLayout) {
30788 el = config.parentLayout.el.createChild();
30791 if(el.autoCreate){ // xtype is available if this is called from factory
30795 this.el = Roo.get(el);
30796 if(!this.el && config && config.autoCreate){
30797 if(typeof config.autoCreate == "object"){
30798 if(!config.autoCreate.id){
30799 config.autoCreate.id = config.id||el;
30801 this.el = Roo.DomHelper.append(document.body,
30802 config.autoCreate, true);
30804 this.el = Roo.DomHelper.append(document.body,
30805 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30808 this.closable = false;
30809 this.loaded = false;
30810 this.active = false;
30811 if(typeof config == "string"){
30812 this.title = config;
30814 Roo.apply(this, config);
30817 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30818 this.wrapEl = this.el.wrap();
30819 this.toolbar.container = this.el.insertSibling(false, 'before');
30820 this.toolbar = new Roo.Toolbar(this.toolbar);
30823 // xtype created footer. - not sure if will work as we normally have to render first..
30824 if (this.footer && !this.footer.el && this.footer.xtype) {
30825 if (!this.wrapEl) {
30826 this.wrapEl = this.el.wrap();
30829 this.footer.container = this.wrapEl.createChild();
30831 this.footer = Roo.factory(this.footer, Roo);
30836 this.resizeEl = Roo.get(this.resizeEl, true);
30838 this.resizeEl = this.el;
30840 // handle view.xtype
30848 * Fires when this panel is activated.
30849 * @param {Roo.ContentPanel} this
30853 * @event deactivate
30854 * Fires when this panel is activated.
30855 * @param {Roo.ContentPanel} this
30857 "deactivate" : true,
30861 * Fires when this panel is resized if fitToFrame is true.
30862 * @param {Roo.ContentPanel} this
30863 * @param {Number} width The width after any component adjustments
30864 * @param {Number} height The height after any component adjustments
30870 * Fires when this tab is created
30871 * @param {Roo.ContentPanel} this
30882 if(this.autoScroll){
30883 this.resizeEl.setStyle("overflow", "auto");
30885 // fix randome scrolling
30886 this.el.on('scroll', function() {
30887 Roo.log('fix random scolling');
30888 this.scrollTo('top',0);
30891 content = content || this.content;
30893 this.setContent(content);
30895 if(config && config.url){
30896 this.setUrl(this.url, this.params, this.loadOnce);
30901 Roo.ContentPanel.superclass.constructor.call(this);
30903 if (this.view && typeof(this.view.xtype) != 'undefined') {
30904 this.view.el = this.el.appendChild(document.createElement("div"));
30905 this.view = Roo.factory(this.view);
30906 this.view.render && this.view.render(false, '');
30910 this.fireEvent('render', this);
30913 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
30915 setRegion : function(region){
30916 this.region = region;
30918 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
30920 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
30925 * Returns the toolbar for this Panel if one was configured.
30926 * @return {Roo.Toolbar}
30928 getToolbar : function(){
30929 return this.toolbar;
30932 setActiveState : function(active){
30933 this.active = active;
30935 this.fireEvent("deactivate", this);
30937 this.fireEvent("activate", this);
30941 * Updates this panel's element
30942 * @param {String} content The new content
30943 * @param {Boolean} loadScripts (optional) true to look for and process scripts
30945 setContent : function(content, loadScripts){
30946 this.el.update(content, loadScripts);
30949 ignoreResize : function(w, h){
30950 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
30953 this.lastSize = {width: w, height: h};
30958 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
30959 * @return {Roo.UpdateManager} The UpdateManager
30961 getUpdateManager : function(){
30962 return this.el.getUpdateManager();
30965 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
30966 * @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:
30969 url: "your-url.php",
30970 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
30971 callback: yourFunction,
30972 scope: yourObject, //(optional scope)
30975 text: "Loading...",
30980 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
30981 * 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.
30982 * @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}
30983 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
30984 * @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.
30985 * @return {Roo.ContentPanel} this
30988 var um = this.el.getUpdateManager();
30989 um.update.apply(um, arguments);
30995 * 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.
30996 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
30997 * @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)
30998 * @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)
30999 * @return {Roo.UpdateManager} The UpdateManager
31001 setUrl : function(url, params, loadOnce){
31002 if(this.refreshDelegate){
31003 this.removeListener("activate", this.refreshDelegate);
31005 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31006 this.on("activate", this.refreshDelegate);
31007 return this.el.getUpdateManager();
31010 _handleRefresh : function(url, params, loadOnce){
31011 if(!loadOnce || !this.loaded){
31012 var updater = this.el.getUpdateManager();
31013 updater.update(url, params, this._setLoaded.createDelegate(this));
31017 _setLoaded : function(){
31018 this.loaded = true;
31022 * Returns this panel's id
31025 getId : function(){
31030 * Returns this panel's element - used by regiosn to add.
31031 * @return {Roo.Element}
31033 getEl : function(){
31034 return this.wrapEl || this.el;
31037 adjustForComponents : function(width, height)
31039 //Roo.log('adjustForComponents ');
31040 if(this.resizeEl != this.el){
31041 width -= this.el.getFrameWidth('lr');
31042 height -= this.el.getFrameWidth('tb');
31045 var te = this.toolbar.getEl();
31046 height -= te.getHeight();
31047 te.setWidth(width);
31050 var te = this.footer.getEl();
31051 Roo.log("footer:" + te.getHeight());
31053 height -= te.getHeight();
31054 te.setWidth(width);
31058 if(this.adjustments){
31059 width += this.adjustments[0];
31060 height += this.adjustments[1];
31062 return {"width": width, "height": height};
31065 setSize : function(width, height){
31066 if(this.fitToFrame && !this.ignoreResize(width, height)){
31067 if(this.fitContainer && this.resizeEl != this.el){
31068 this.el.setSize(width, height);
31070 var size = this.adjustForComponents(width, height);
31071 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31072 this.fireEvent('resize', this, size.width, size.height);
31077 * Returns this panel's title
31080 getTitle : function(){
31085 * Set this panel's title
31086 * @param {String} title
31088 setTitle : function(title){
31089 this.title = title;
31091 this.region.updatePanelTitle(this, title);
31096 * Returns true is this panel was configured to be closable
31097 * @return {Boolean}
31099 isClosable : function(){
31100 return this.closable;
31103 beforeSlide : function(){
31105 this.resizeEl.clip();
31108 afterSlide : function(){
31110 this.resizeEl.unclip();
31114 * Force a content refresh from the URL specified in the {@link #setUrl} method.
31115 * Will fail silently if the {@link #setUrl} method has not been called.
31116 * This does not activate the panel, just updates its content.
31118 refresh : function(){
31119 if(this.refreshDelegate){
31120 this.loaded = false;
31121 this.refreshDelegate();
31126 * Destroys this panel
31128 destroy : function(){
31129 this.el.removeAllListeners();
31130 var tempEl = document.createElement("span");
31131 tempEl.appendChild(this.el.dom);
31132 tempEl.innerHTML = "";
31138 * form - if the content panel contains a form - this is a reference to it.
31139 * @type {Roo.form.Form}
31143 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31144 * This contains a reference to it.
31150 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31160 * @param {Object} cfg Xtype definition of item to add.
31163 addxtype : function(cfg) {
31165 if (cfg.xtype.match(/^Form$/)) {
31168 //if (this.footer) {
31169 // el = this.footer.container.insertSibling(false, 'before');
31171 el = this.el.createChild();
31174 this.form = new Roo.form.Form(cfg);
31177 if ( this.form.allItems.length) {
31178 this.form.render(el.dom);
31182 // should only have one of theses..
31183 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31184 // views.. should not be just added - used named prop 'view''
31186 cfg.el = this.el.appendChild(document.createElement("div"));
31189 var ret = new Roo.factory(cfg);
31191 ret.render && ret.render(false, ''); // render blank..
31200 * @class Roo.GridPanel
31201 * @extends Roo.ContentPanel
31203 * Create a new GridPanel.
31204 * @param {Roo.grid.Grid} grid The grid for this panel
31205 * @param {String/Object} config A string to set only the panel's title, or a config object
31207 Roo.GridPanel = function(grid, config){
31210 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31211 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31213 this.wrapper.dom.appendChild(grid.getGridEl().dom);
31215 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31218 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31220 // xtype created footer. - not sure if will work as we normally have to render first..
31221 if (this.footer && !this.footer.el && this.footer.xtype) {
31223 this.footer.container = this.grid.getView().getFooterPanel(true);
31224 this.footer.dataSource = this.grid.dataSource;
31225 this.footer = Roo.factory(this.footer, Roo);
31229 grid.monitorWindowResize = false; // turn off autosizing
31230 grid.autoHeight = false;
31231 grid.autoWidth = false;
31233 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31236 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31237 getId : function(){
31238 return this.grid.id;
31242 * Returns the grid for this panel
31243 * @return {Roo.grid.Grid}
31245 getGrid : function(){
31249 setSize : function(width, height){
31250 if(!this.ignoreResize(width, height)){
31251 var grid = this.grid;
31252 var size = this.adjustForComponents(width, height);
31253 grid.getGridEl().setSize(size.width, size.height);
31258 beforeSlide : function(){
31259 this.grid.getView().scroller.clip();
31262 afterSlide : function(){
31263 this.grid.getView().scroller.unclip();
31266 destroy : function(){
31267 this.grid.destroy();
31269 Roo.GridPanel.superclass.destroy.call(this);
31275 * @class Roo.NestedLayoutPanel
31276 * @extends Roo.ContentPanel
31278 * Create a new NestedLayoutPanel.
31281 * @param {Roo.BorderLayout} layout The layout for this panel
31282 * @param {String/Object} config A string to set only the title or a config object
31284 Roo.NestedLayoutPanel = function(layout, config)
31286 // construct with only one argument..
31287 /* FIXME - implement nicer consturctors
31288 if (layout.layout) {
31290 layout = config.layout;
31291 delete config.layout;
31293 if (layout.xtype && !layout.getEl) {
31294 // then layout needs constructing..
31295 layout = Roo.factory(layout, Roo);
31300 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31302 layout.monitorWindowResize = false; // turn off autosizing
31303 this.layout = layout;
31304 this.layout.getEl().addClass("x-layout-nested-layout");
31311 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31313 setSize : function(width, height){
31314 if(!this.ignoreResize(width, height)){
31315 var size = this.adjustForComponents(width, height);
31316 var el = this.layout.getEl();
31317 el.setSize(size.width, size.height);
31318 var touch = el.dom.offsetWidth;
31319 this.layout.layout();
31320 // ie requires a double layout on the first pass
31321 if(Roo.isIE && !this.initialized){
31322 this.initialized = true;
31323 this.layout.layout();
31328 // activate all subpanels if not currently active..
31330 setActiveState : function(active){
31331 this.active = active;
31333 this.fireEvent("deactivate", this);
31337 this.fireEvent("activate", this);
31338 // not sure if this should happen before or after..
31339 if (!this.layout) {
31340 return; // should not happen..
31343 for (var r in this.layout.regions) {
31344 reg = this.layout.getRegion(r);
31345 if (reg.getActivePanel()) {
31346 //reg.showPanel(reg.getActivePanel()); // force it to activate..
31347 reg.setActivePanel(reg.getActivePanel());
31350 if (!reg.panels.length) {
31353 reg.showPanel(reg.getPanel(0));
31362 * Returns the nested BorderLayout for this panel
31363 * @return {Roo.BorderLayout}
31365 getLayout : function(){
31366 return this.layout;
31370 * Adds a xtype elements to the layout of the nested panel
31374 xtype : 'ContentPanel',
31381 xtype : 'NestedLayoutPanel',
31387 items : [ ... list of content panels or nested layout panels.. ]
31391 * @param {Object} cfg Xtype definition of item to add.
31393 addxtype : function(cfg) {
31394 return this.layout.addxtype(cfg);
31399 Roo.ScrollPanel = function(el, config, content){
31400 config = config || {};
31401 config.fitToFrame = true;
31402 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31404 this.el.dom.style.overflow = "hidden";
31405 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31406 this.el.removeClass("x-layout-inactive-content");
31407 this.el.on("mousewheel", this.onWheel, this);
31409 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
31410 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
31411 up.unselectable(); down.unselectable();
31412 up.on("click", this.scrollUp, this);
31413 down.on("click", this.scrollDown, this);
31414 up.addClassOnOver("x-scroller-btn-over");
31415 down.addClassOnOver("x-scroller-btn-over");
31416 up.addClassOnClick("x-scroller-btn-click");
31417 down.addClassOnClick("x-scroller-btn-click");
31418 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31420 this.resizeEl = this.el;
31421 this.el = wrap; this.up = up; this.down = down;
31424 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31426 wheelIncrement : 5,
31427 scrollUp : function(){
31428 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31431 scrollDown : function(){
31432 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31435 afterScroll : function(){
31436 var el = this.resizeEl;
31437 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31438 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31439 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31442 setSize : function(){
31443 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31444 this.afterScroll();
31447 onWheel : function(e){
31448 var d = e.getWheelDelta();
31449 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31450 this.afterScroll();
31454 setContent : function(content, loadScripts){
31455 this.resizeEl.update(content, loadScripts);
31469 * @class Roo.TreePanel
31470 * @extends Roo.ContentPanel
31472 * Create a new TreePanel. - defaults to fit/scoll contents.
31473 * @param {String/Object} config A string to set only the panel's title, or a config object
31474 * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31476 Roo.TreePanel = function(config){
31477 var el = config.el;
31478 var tree = config.tree;
31479 delete config.tree;
31480 delete config.el; // hopefull!
31482 // wrapper for IE7 strict & safari scroll issue
31484 var treeEl = el.createChild();
31485 config.resizeEl = treeEl;
31489 Roo.TreePanel.superclass.constructor.call(this, el, config);
31492 this.tree = new Roo.tree.TreePanel(treeEl , tree);
31493 //console.log(tree);
31494 this.on('activate', function()
31496 if (this.tree.rendered) {
31499 //console.log('render tree');
31500 this.tree.render();
31502 // this should not be needed.. - it's actually the 'el' that resizes?
31503 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31505 //this.on('resize', function (cp, w, h) {
31506 // this.tree.innerCt.setWidth(w);
31507 // this.tree.innerCt.setHeight(h);
31508 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
31515 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
31532 * Ext JS Library 1.1.1
31533 * Copyright(c) 2006-2007, Ext JS, LLC.
31535 * Originally Released Under LGPL - original licence link has changed is not relivant.
31538 * <script type="text/javascript">
31543 * @class Roo.ReaderLayout
31544 * @extends Roo.BorderLayout
31545 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
31546 * center region containing two nested regions (a top one for a list view and one for item preview below),
31547 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31548 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31549 * expedites the setup of the overall layout and regions for this common application style.
31552 var reader = new Roo.ReaderLayout();
31553 var CP = Roo.ContentPanel; // shortcut for adding
31555 reader.beginUpdate();
31556 reader.add("north", new CP("north", "North"));
31557 reader.add("west", new CP("west", {title: "West"}));
31558 reader.add("east", new CP("east", {title: "East"}));
31560 reader.regions.listView.add(new CP("listView", "List"));
31561 reader.regions.preview.add(new CP("preview", "Preview"));
31562 reader.endUpdate();
31565 * Create a new ReaderLayout
31566 * @param {Object} config Configuration options
31567 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31568 * document.body if omitted)
31570 Roo.ReaderLayout = function(config, renderTo){
31571 var c = config || {size:{}};
31572 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31573 north: c.north !== false ? Roo.apply({
31577 }, c.north) : false,
31578 west: c.west !== false ? Roo.apply({
31586 margins:{left:5,right:0,bottom:5,top:5},
31587 cmargins:{left:5,right:5,bottom:5,top:5}
31588 }, c.west) : false,
31589 east: c.east !== false ? Roo.apply({
31597 margins:{left:0,right:5,bottom:5,top:5},
31598 cmargins:{left:5,right:5,bottom:5,top:5}
31599 }, c.east) : false,
31600 center: Roo.apply({
31601 tabPosition: 'top',
31605 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31609 this.el.addClass('x-reader');
31611 this.beginUpdate();
31613 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31614 south: c.preview !== false ? Roo.apply({
31621 cmargins:{top:5,left:0, right:0, bottom:0}
31622 }, c.preview) : false,
31623 center: Roo.apply({
31629 this.add('center', new Roo.NestedLayoutPanel(inner,
31630 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31634 this.regions.preview = inner.getRegion('south');
31635 this.regions.listView = inner.getRegion('center');
31638 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31640 * Ext JS Library 1.1.1
31641 * Copyright(c) 2006-2007, Ext JS, LLC.
31643 * Originally Released Under LGPL - original licence link has changed is not relivant.
31646 * <script type="text/javascript">
31650 * @class Roo.grid.Grid
31651 * @extends Roo.util.Observable
31652 * This class represents the primary interface of a component based grid control.
31653 * <br><br>Usage:<pre><code>
31654 var grid = new Roo.grid.Grid("my-container-id", {
31657 selModel: mySelectionModel,
31658 autoSizeColumns: true,
31659 monitorWindowResize: false,
31660 trackMouseOver: true
31665 * <b>Common Problems:</b><br/>
31666 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31667 * element will correct this<br/>
31668 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31669 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31670 * are unpredictable.<br/>
31671 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31672 * grid to calculate dimensions/offsets.<br/>
31674 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31675 * The container MUST have some type of size defined for the grid to fill. The container will be
31676 * automatically set to position relative if it isn't already.
31677 * @param {Object} config A config object that sets properties on this grid.
31679 Roo.grid.Grid = function(container, config){
31680 // initialize the container
31681 this.container = Roo.get(container);
31682 this.container.update("");
31683 this.container.setStyle("overflow", "hidden");
31684 this.container.addClass('x-grid-container');
31686 this.id = this.container.id;
31688 Roo.apply(this, config);
31689 // check and correct shorthanded configs
31691 this.dataSource = this.ds;
31695 this.colModel = this.cm;
31699 this.selModel = this.sm;
31703 if (this.selModel) {
31704 this.selModel = Roo.factory(this.selModel, Roo.grid);
31705 this.sm = this.selModel;
31706 this.sm.xmodule = this.xmodule || false;
31708 if (typeof(this.colModel.config) == 'undefined') {
31709 this.colModel = new Roo.grid.ColumnModel(this.colModel);
31710 this.cm = this.colModel;
31711 this.cm.xmodule = this.xmodule || false;
31713 if (this.dataSource) {
31714 this.dataSource= Roo.factory(this.dataSource, Roo.data);
31715 this.ds = this.dataSource;
31716 this.ds.xmodule = this.xmodule || false;
31723 this.container.setWidth(this.width);
31727 this.container.setHeight(this.height);
31734 * The raw click event for the entire grid.
31735 * @param {Roo.EventObject} e
31740 * The raw dblclick event for the entire grid.
31741 * @param {Roo.EventObject} e
31745 * @event contextmenu
31746 * The raw contextmenu event for the entire grid.
31747 * @param {Roo.EventObject} e
31749 "contextmenu" : true,
31752 * The raw mousedown event for the entire grid.
31753 * @param {Roo.EventObject} e
31755 "mousedown" : true,
31758 * The raw mouseup event for the entire grid.
31759 * @param {Roo.EventObject} e
31764 * The raw mouseover event for the entire grid.
31765 * @param {Roo.EventObject} e
31767 "mouseover" : true,
31770 * The raw mouseout event for the entire grid.
31771 * @param {Roo.EventObject} e
31776 * The raw keypress event for the entire grid.
31777 * @param {Roo.EventObject} e
31782 * The raw keydown event for the entire grid.
31783 * @param {Roo.EventObject} e
31791 * Fires when a cell is clicked
31792 * @param {Grid} this
31793 * @param {Number} rowIndex
31794 * @param {Number} columnIndex
31795 * @param {Roo.EventObject} e
31797 "cellclick" : true,
31799 * @event celldblclick
31800 * Fires when a cell is double clicked
31801 * @param {Grid} this
31802 * @param {Number} rowIndex
31803 * @param {Number} columnIndex
31804 * @param {Roo.EventObject} e
31806 "celldblclick" : true,
31809 * Fires when a row is clicked
31810 * @param {Grid} this
31811 * @param {Number} rowIndex
31812 * @param {Roo.EventObject} e
31816 * @event rowdblclick
31817 * Fires when a row is double clicked
31818 * @param {Grid} this
31819 * @param {Number} rowIndex
31820 * @param {Roo.EventObject} e
31822 "rowdblclick" : true,
31824 * @event headerclick
31825 * Fires when a header is clicked
31826 * @param {Grid} this
31827 * @param {Number} columnIndex
31828 * @param {Roo.EventObject} e
31830 "headerclick" : true,
31832 * @event headerdblclick
31833 * Fires when a header cell is double clicked
31834 * @param {Grid} this
31835 * @param {Number} columnIndex
31836 * @param {Roo.EventObject} e
31838 "headerdblclick" : true,
31840 * @event rowcontextmenu
31841 * Fires when a row is right clicked
31842 * @param {Grid} this
31843 * @param {Number} rowIndex
31844 * @param {Roo.EventObject} e
31846 "rowcontextmenu" : true,
31848 * @event cellcontextmenu
31849 * Fires when a cell is right clicked
31850 * @param {Grid} this
31851 * @param {Number} rowIndex
31852 * @param {Number} cellIndex
31853 * @param {Roo.EventObject} e
31855 "cellcontextmenu" : true,
31857 * @event headercontextmenu
31858 * Fires when a header is right clicked
31859 * @param {Grid} this
31860 * @param {Number} columnIndex
31861 * @param {Roo.EventObject} e
31863 "headercontextmenu" : true,
31865 * @event bodyscroll
31866 * Fires when the body element is scrolled
31867 * @param {Number} scrollLeft
31868 * @param {Number} scrollTop
31870 "bodyscroll" : true,
31872 * @event columnresize
31873 * Fires when the user resizes a column
31874 * @param {Number} columnIndex
31875 * @param {Number} newSize
31877 "columnresize" : true,
31879 * @event columnmove
31880 * Fires when the user moves a column
31881 * @param {Number} oldIndex
31882 * @param {Number} newIndex
31884 "columnmove" : true,
31887 * Fires when row(s) start being dragged
31888 * @param {Grid} this
31889 * @param {Roo.GridDD} dd The drag drop object
31890 * @param {event} e The raw browser event
31892 "startdrag" : true,
31895 * Fires when a drag operation is complete
31896 * @param {Grid} this
31897 * @param {Roo.GridDD} dd The drag drop object
31898 * @param {event} e The raw browser event
31903 * Fires when dragged row(s) are dropped on a valid DD target
31904 * @param {Grid} this
31905 * @param {Roo.GridDD} dd The drag drop object
31906 * @param {String} targetId The target drag drop object
31907 * @param {event} e The raw browser event
31912 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31913 * @param {Grid} this
31914 * @param {Roo.GridDD} dd The drag drop object
31915 * @param {String} targetId The target drag drop object
31916 * @param {event} e The raw browser event
31921 * Fires when the dragged row(s) first cross another DD target while being dragged
31922 * @param {Grid} this
31923 * @param {Roo.GridDD} dd The drag drop object
31924 * @param {String} targetId The target drag drop object
31925 * @param {event} e The raw browser event
31927 "dragenter" : true,
31930 * Fires when the dragged row(s) leave another DD target while being dragged
31931 * @param {Grid} this
31932 * @param {Roo.GridDD} dd The drag drop object
31933 * @param {String} targetId The target drag drop object
31934 * @param {event} e The raw browser event
31939 * Fires when a row is rendered, so you can change add a style to it.
31940 * @param {GridView} gridview The grid view
31941 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
31947 * Fires when the grid is rendered
31948 * @param {Grid} grid
31953 Roo.grid.Grid.superclass.constructor.call(this);
31955 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
31958 * @cfg {String} ddGroup - drag drop group.
31962 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
31964 minColumnWidth : 25,
31967 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
31968 * <b>on initial render.</b> It is more efficient to explicitly size the columns
31969 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
31971 autoSizeColumns : false,
31974 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
31976 autoSizeHeaders : true,
31979 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
31981 monitorWindowResize : true,
31984 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
31985 * rows measured to get a columns size. Default is 0 (all rows).
31987 maxRowsToMeasure : 0,
31990 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
31992 trackMouseOver : true,
31995 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
31999 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32001 enableDragDrop : false,
32004 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32006 enableColumnMove : true,
32009 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32011 enableColumnHide : true,
32014 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32016 enableRowHeightSync : false,
32019 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
32024 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32026 autoHeight : false,
32029 * @cfg {String} autoExpandColumn The id (or dataIndex) of a column in this grid that should expand to fill unused space. This id can not be 0. Default is false.
32031 autoExpandColumn : false,
32034 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32037 autoExpandMin : 50,
32040 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32042 autoExpandMax : 1000,
32045 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32050 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32054 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32064 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32065 * of a fixed width. Default is false.
32068 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32071 * Called once after all setup has been completed and the grid is ready to be rendered.
32072 * @return {Roo.grid.Grid} this
32074 render : function()
32076 var c = this.container;
32077 // try to detect autoHeight/width mode
32078 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32079 this.autoHeight = true;
32081 var view = this.getView();
32084 c.on("click", this.onClick, this);
32085 c.on("dblclick", this.onDblClick, this);
32086 c.on("contextmenu", this.onContextMenu, this);
32087 c.on("keydown", this.onKeyDown, this);
32089 c.on("touchstart", this.onTouchStart, this);
32092 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32094 this.getSelectionModel().init(this);
32099 this.loadMask = new Roo.LoadMask(this.container,
32100 Roo.apply({store:this.dataSource}, this.loadMask));
32104 if (this.toolbar && this.toolbar.xtype) {
32105 this.toolbar.container = this.getView().getHeaderPanel(true);
32106 this.toolbar = new Roo.Toolbar(this.toolbar);
32108 if (this.footer && this.footer.xtype) {
32109 this.footer.dataSource = this.getDataSource();
32110 this.footer.container = this.getView().getFooterPanel(true);
32111 this.footer = Roo.factory(this.footer, Roo);
32113 if (this.dropTarget && this.dropTarget.xtype) {
32114 delete this.dropTarget.xtype;
32115 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32119 this.rendered = true;
32120 this.fireEvent('render', this);
32125 * Reconfigures the grid to use a different Store and Column Model.
32126 * The View will be bound to the new objects and refreshed.
32127 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32128 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32130 reconfigure : function(dataSource, colModel){
32132 this.loadMask.destroy();
32133 this.loadMask = new Roo.LoadMask(this.container,
32134 Roo.apply({store:dataSource}, this.loadMask));
32136 this.view.bind(dataSource, colModel);
32137 this.dataSource = dataSource;
32138 this.colModel = colModel;
32139 this.view.refresh(true);
32143 onKeyDown : function(e){
32144 this.fireEvent("keydown", e);
32148 * Destroy this grid.
32149 * @param {Boolean} removeEl True to remove the element
32151 destroy : function(removeEl, keepListeners){
32153 this.loadMask.destroy();
32155 var c = this.container;
32156 c.removeAllListeners();
32157 this.view.destroy();
32158 this.colModel.purgeListeners();
32159 if(!keepListeners){
32160 this.purgeListeners();
32163 if(removeEl === true){
32169 processEvent : function(name, e){
32170 // does this fire select???
32171 //Roo.log('grid:processEvent ' + name);
32173 if (name != 'touchstart' ) {
32174 this.fireEvent(name, e);
32177 var t = e.getTarget();
32179 var header = v.findHeaderIndex(t);
32180 if(header !== false){
32181 var ename = name == 'touchstart' ? 'click' : name;
32183 this.fireEvent("header" + ename, this, header, e);
32185 var row = v.findRowIndex(t);
32186 var cell = v.findCellIndex(t);
32187 if (name == 'touchstart') {
32188 // first touch is always a click.
32189 // hopefull this happens after selection is updated.?
32192 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32193 var cs = this.selModel.getSelectedCell();
32194 if (row == cs[0] && cell == cs[1]){
32198 if (typeof(this.selModel.getSelections) != 'undefined') {
32199 var cs = this.selModel.getSelections();
32200 var ds = this.dataSource;
32201 if (cs.length == 1 && ds.getAt(row) == cs[0]){
32212 this.fireEvent("row" + name, this, row, e);
32213 if(cell !== false){
32214 this.fireEvent("cell" + name, this, row, cell, e);
32221 onClick : function(e){
32222 this.processEvent("click", e);
32225 onTouchStart : function(e){
32226 this.processEvent("touchstart", e);
32230 onContextMenu : function(e, t){
32231 this.processEvent("contextmenu", e);
32235 onDblClick : function(e){
32236 this.processEvent("dblclick", e);
32240 walkCells : function(row, col, step, fn, scope){
32241 var cm = this.colModel, clen = cm.getColumnCount();
32242 var ds = this.dataSource, rlen = ds.getCount(), first = true;
32254 if(fn.call(scope || this, row, col, cm) === true){
32272 if(fn.call(scope || this, row, col, cm) === true){
32284 getSelections : function(){
32285 return this.selModel.getSelections();
32289 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32290 * but if manual update is required this method will initiate it.
32292 autoSize : function(){
32294 this.view.layout();
32295 if(this.view.adjustForScroll){
32296 this.view.adjustForScroll();
32302 * Returns the grid's underlying element.
32303 * @return {Element} The element
32305 getGridEl : function(){
32306 return this.container;
32309 // private for compatibility, overridden by editor grid
32310 stopEditing : function(){},
32313 * Returns the grid's SelectionModel.
32314 * @return {SelectionModel}
32316 getSelectionModel : function(){
32317 if(!this.selModel){
32318 this.selModel = new Roo.grid.RowSelectionModel();
32320 return this.selModel;
32324 * Returns the grid's DataSource.
32325 * @return {DataSource}
32327 getDataSource : function(){
32328 return this.dataSource;
32332 * Returns the grid's ColumnModel.
32333 * @return {ColumnModel}
32335 getColumnModel : function(){
32336 return this.colModel;
32340 * Returns the grid's GridView object.
32341 * @return {GridView}
32343 getView : function(){
32345 this.view = new Roo.grid.GridView(this.viewConfig);
32350 * Called to get grid's drag proxy text, by default returns this.ddText.
32353 getDragDropText : function(){
32354 var count = this.selModel.getCount();
32355 return String.format(this.ddText, count, count == 1 ? '' : 's');
32359 * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32360 * %0 is replaced with the number of selected rows.
32363 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32365 * Ext JS Library 1.1.1
32366 * Copyright(c) 2006-2007, Ext JS, LLC.
32368 * Originally Released Under LGPL - original licence link has changed is not relivant.
32371 * <script type="text/javascript">
32374 Roo.grid.AbstractGridView = function(){
32378 "beforerowremoved" : true,
32379 "beforerowsinserted" : true,
32380 "beforerefresh" : true,
32381 "rowremoved" : true,
32382 "rowsinserted" : true,
32383 "rowupdated" : true,
32386 Roo.grid.AbstractGridView.superclass.constructor.call(this);
32389 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32390 rowClass : "x-grid-row",
32391 cellClass : "x-grid-cell",
32392 tdClass : "x-grid-td",
32393 hdClass : "x-grid-hd",
32394 splitClass : "x-grid-hd-split",
32396 init: function(grid){
32398 var cid = this.grid.getGridEl().id;
32399 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32400 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32401 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32402 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32405 getColumnRenderers : function(){
32406 var renderers = [];
32407 var cm = this.grid.colModel;
32408 var colCount = cm.getColumnCount();
32409 for(var i = 0; i < colCount; i++){
32410 renderers[i] = cm.getRenderer(i);
32415 getColumnIds : function(){
32417 var cm = this.grid.colModel;
32418 var colCount = cm.getColumnCount();
32419 for(var i = 0; i < colCount; i++){
32420 ids[i] = cm.getColumnId(i);
32425 getDataIndexes : function(){
32426 if(!this.indexMap){
32427 this.indexMap = this.buildIndexMap();
32429 return this.indexMap.colToData;
32432 getColumnIndexByDataIndex : function(dataIndex){
32433 if(!this.indexMap){
32434 this.indexMap = this.buildIndexMap();
32436 return this.indexMap.dataToCol[dataIndex];
32440 * Set a css style for a column dynamically.
32441 * @param {Number} colIndex The index of the column
32442 * @param {String} name The css property name
32443 * @param {String} value The css value
32445 setCSSStyle : function(colIndex, name, value){
32446 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32447 Roo.util.CSS.updateRule(selector, name, value);
32450 generateRules : function(cm){
32451 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32452 Roo.util.CSS.removeStyleSheet(rulesId);
32453 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32454 var cid = cm.getColumnId(i);
32455 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32456 this.tdSelector, cid, " {\n}\n",
32457 this.hdSelector, cid, " {\n}\n",
32458 this.splitSelector, cid, " {\n}\n");
32460 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32464 * Ext JS Library 1.1.1
32465 * Copyright(c) 2006-2007, Ext JS, LLC.
32467 * Originally Released Under LGPL - original licence link has changed is not relivant.
32470 * <script type="text/javascript">
32474 // This is a support class used internally by the Grid components
32475 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32477 this.view = grid.getView();
32478 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32479 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32481 this.setHandleElId(Roo.id(hd));
32482 this.setOuterHandleElId(Roo.id(hd2));
32484 this.scroll = false;
32486 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32488 getDragData : function(e){
32489 var t = Roo.lib.Event.getTarget(e);
32490 var h = this.view.findHeaderCell(t);
32492 return {ddel: h.firstChild, header:h};
32497 onInitDrag : function(e){
32498 this.view.headersDisabled = true;
32499 var clone = this.dragData.ddel.cloneNode(true);
32500 clone.id = Roo.id();
32501 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32502 this.proxy.update(clone);
32506 afterValidDrop : function(){
32508 setTimeout(function(){
32509 v.headersDisabled = false;
32513 afterInvalidDrop : function(){
32515 setTimeout(function(){
32516 v.headersDisabled = false;
32522 * Ext JS Library 1.1.1
32523 * Copyright(c) 2006-2007, Ext JS, LLC.
32525 * Originally Released Under LGPL - original licence link has changed is not relivant.
32528 * <script type="text/javascript">
32531 // This is a support class used internally by the Grid components
32532 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32534 this.view = grid.getView();
32535 // split the proxies so they don't interfere with mouse events
32536 this.proxyTop = Roo.DomHelper.append(document.body, {
32537 cls:"col-move-top", html:" "
32539 this.proxyBottom = Roo.DomHelper.append(document.body, {
32540 cls:"col-move-bottom", html:" "
32542 this.proxyTop.hide = this.proxyBottom.hide = function(){
32543 this.setLeftTop(-100,-100);
32544 this.setStyle("visibility", "hidden");
32546 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32547 // temporarily disabled
32548 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32549 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32551 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32552 proxyOffsets : [-4, -9],
32553 fly: Roo.Element.fly,
32555 getTargetFromEvent : function(e){
32556 var t = Roo.lib.Event.getTarget(e);
32557 var cindex = this.view.findCellIndex(t);
32558 if(cindex !== false){
32559 return this.view.getHeaderCell(cindex);
32564 nextVisible : function(h){
32565 var v = this.view, cm = this.grid.colModel;
32568 if(!cm.isHidden(v.getCellIndex(h))){
32576 prevVisible : function(h){
32577 var v = this.view, cm = this.grid.colModel;
32580 if(!cm.isHidden(v.getCellIndex(h))){
32588 positionIndicator : function(h, n, e){
32589 var x = Roo.lib.Event.getPageX(e);
32590 var r = Roo.lib.Dom.getRegion(n.firstChild);
32591 var px, pt, py = r.top + this.proxyOffsets[1];
32592 if((r.right - x) <= (r.right-r.left)/2){
32593 px = r.right+this.view.borderWidth;
32599 var oldIndex = this.view.getCellIndex(h);
32600 var newIndex = this.view.getCellIndex(n);
32602 if(this.grid.colModel.isFixed(newIndex)){
32606 var locked = this.grid.colModel.isLocked(newIndex);
32611 if(oldIndex < newIndex){
32614 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32617 px += this.proxyOffsets[0];
32618 this.proxyTop.setLeftTop(px, py);
32619 this.proxyTop.show();
32620 if(!this.bottomOffset){
32621 this.bottomOffset = this.view.mainHd.getHeight();
32623 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32624 this.proxyBottom.show();
32628 onNodeEnter : function(n, dd, e, data){
32629 if(data.header != n){
32630 this.positionIndicator(data.header, n, e);
32634 onNodeOver : function(n, dd, e, data){
32635 var result = false;
32636 if(data.header != n){
32637 result = this.positionIndicator(data.header, n, e);
32640 this.proxyTop.hide();
32641 this.proxyBottom.hide();
32643 return result ? this.dropAllowed : this.dropNotAllowed;
32646 onNodeOut : function(n, dd, e, data){
32647 this.proxyTop.hide();
32648 this.proxyBottom.hide();
32651 onNodeDrop : function(n, dd, e, data){
32652 var h = data.header;
32654 var cm = this.grid.colModel;
32655 var x = Roo.lib.Event.getPageX(e);
32656 var r = Roo.lib.Dom.getRegion(n.firstChild);
32657 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32658 var oldIndex = this.view.getCellIndex(h);
32659 var newIndex = this.view.getCellIndex(n);
32660 var locked = cm.isLocked(newIndex);
32664 if(oldIndex < newIndex){
32667 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32670 cm.setLocked(oldIndex, locked, true);
32671 cm.moveColumn(oldIndex, newIndex);
32672 this.grid.fireEvent("columnmove", oldIndex, newIndex);
32680 * Ext JS Library 1.1.1
32681 * Copyright(c) 2006-2007, Ext JS, LLC.
32683 * Originally Released Under LGPL - original licence link has changed is not relivant.
32686 * <script type="text/javascript">
32690 * @class Roo.grid.GridView
32691 * @extends Roo.util.Observable
32694 * @param {Object} config
32696 Roo.grid.GridView = function(config){
32697 Roo.grid.GridView.superclass.constructor.call(this);
32700 Roo.apply(this, config);
32703 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32705 unselectable : 'unselectable="on"',
32706 unselectableCls : 'x-unselectable',
32709 rowClass : "x-grid-row",
32711 cellClass : "x-grid-col",
32713 tdClass : "x-grid-td",
32715 hdClass : "x-grid-hd",
32717 splitClass : "x-grid-split",
32719 sortClasses : ["sort-asc", "sort-desc"],
32721 enableMoveAnim : false,
32725 dh : Roo.DomHelper,
32727 fly : Roo.Element.fly,
32729 css : Roo.util.CSS,
32735 scrollIncrement : 22,
32737 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32739 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32741 bind : function(ds, cm){
32743 this.ds.un("load", this.onLoad, this);
32744 this.ds.un("datachanged", this.onDataChange, this);
32745 this.ds.un("add", this.onAdd, this);
32746 this.ds.un("remove", this.onRemove, this);
32747 this.ds.un("update", this.onUpdate, this);
32748 this.ds.un("clear", this.onClear, this);
32751 ds.on("load", this.onLoad, this);
32752 ds.on("datachanged", this.onDataChange, this);
32753 ds.on("add", this.onAdd, this);
32754 ds.on("remove", this.onRemove, this);
32755 ds.on("update", this.onUpdate, this);
32756 ds.on("clear", this.onClear, this);
32761 this.cm.un("widthchange", this.onColWidthChange, this);
32762 this.cm.un("headerchange", this.onHeaderChange, this);
32763 this.cm.un("hiddenchange", this.onHiddenChange, this);
32764 this.cm.un("columnmoved", this.onColumnMove, this);
32765 this.cm.un("columnlockchange", this.onColumnLock, this);
32768 this.generateRules(cm);
32769 cm.on("widthchange", this.onColWidthChange, this);
32770 cm.on("headerchange", this.onHeaderChange, this);
32771 cm.on("hiddenchange", this.onHiddenChange, this);
32772 cm.on("columnmoved", this.onColumnMove, this);
32773 cm.on("columnlockchange", this.onColumnLock, this);
32778 init: function(grid){
32779 Roo.grid.GridView.superclass.init.call(this, grid);
32781 this.bind(grid.dataSource, grid.colModel);
32783 grid.on("headerclick", this.handleHeaderClick, this);
32785 if(grid.trackMouseOver){
32786 grid.on("mouseover", this.onRowOver, this);
32787 grid.on("mouseout", this.onRowOut, this);
32789 grid.cancelTextSelection = function(){};
32790 this.gridId = grid.id;
32792 var tpls = this.templates || {};
32795 tpls.master = new Roo.Template(
32796 '<div class="x-grid" hidefocus="true">',
32797 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32798 '<div class="x-grid-topbar"></div>',
32799 '<div class="x-grid-scroller"><div></div></div>',
32800 '<div class="x-grid-locked">',
32801 '<div class="x-grid-header">{lockedHeader}</div>',
32802 '<div class="x-grid-body">{lockedBody}</div>',
32804 '<div class="x-grid-viewport">',
32805 '<div class="x-grid-header">{header}</div>',
32806 '<div class="x-grid-body">{body}</div>',
32808 '<div class="x-grid-bottombar"></div>',
32810 '<div class="x-grid-resize-proxy"> </div>',
32813 tpls.master.disableformats = true;
32817 tpls.header = new Roo.Template(
32818 '<table border="0" cellspacing="0" cellpadding="0">',
32819 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32822 tpls.header.disableformats = true;
32824 tpls.header.compile();
32827 tpls.hcell = new Roo.Template(
32828 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32829 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32832 tpls.hcell.disableFormats = true;
32834 tpls.hcell.compile();
32837 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
32838 this.unselectableCls + '" ' + this.unselectable +'> </div>');
32839 tpls.hsplit.disableFormats = true;
32841 tpls.hsplit.compile();
32844 tpls.body = new Roo.Template(
32845 '<table border="0" cellspacing="0" cellpadding="0">',
32846 "<tbody>{rows}</tbody>",
32849 tpls.body.disableFormats = true;
32851 tpls.body.compile();
32854 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32855 tpls.row.disableFormats = true;
32857 tpls.row.compile();
32860 tpls.cell = new Roo.Template(
32861 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32862 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
32863 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
32866 tpls.cell.disableFormats = true;
32868 tpls.cell.compile();
32870 this.templates = tpls;
32873 // remap these for backwards compat
32874 onColWidthChange : function(){
32875 this.updateColumns.apply(this, arguments);
32877 onHeaderChange : function(){
32878 this.updateHeaders.apply(this, arguments);
32880 onHiddenChange : function(){
32881 this.handleHiddenChange.apply(this, arguments);
32883 onColumnMove : function(){
32884 this.handleColumnMove.apply(this, arguments);
32886 onColumnLock : function(){
32887 this.handleLockChange.apply(this, arguments);
32890 onDataChange : function(){
32892 this.updateHeaderSortState();
32895 onClear : function(){
32899 onUpdate : function(ds, record){
32900 this.refreshRow(record);
32903 refreshRow : function(record){
32904 var ds = this.ds, index;
32905 if(typeof record == 'number'){
32907 record = ds.getAt(index);
32909 index = ds.indexOf(record);
32911 this.insertRows(ds, index, index, true);
32912 this.onRemove(ds, record, index+1, true);
32913 this.syncRowHeights(index, index);
32915 this.fireEvent("rowupdated", this, index, record);
32918 onAdd : function(ds, records, index){
32919 this.insertRows(ds, index, index + (records.length-1));
32922 onRemove : function(ds, record, index, isUpdate){
32923 if(isUpdate !== true){
32924 this.fireEvent("beforerowremoved", this, index, record);
32926 var bt = this.getBodyTable(), lt = this.getLockedTable();
32927 if(bt.rows[index]){
32928 bt.firstChild.removeChild(bt.rows[index]);
32930 if(lt.rows[index]){
32931 lt.firstChild.removeChild(lt.rows[index]);
32933 if(isUpdate !== true){
32934 this.stripeRows(index);
32935 this.syncRowHeights(index, index);
32937 this.fireEvent("rowremoved", this, index, record);
32941 onLoad : function(){
32942 this.scrollToTop();
32946 * Scrolls the grid to the top
32948 scrollToTop : function(){
32950 this.scroller.dom.scrollTop = 0;
32956 * Gets a panel in the header of the grid that can be used for toolbars etc.
32957 * After modifying the contents of this panel a call to grid.autoSize() may be
32958 * required to register any changes in size.
32959 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32960 * @return Roo.Element
32962 getHeaderPanel : function(doShow){
32964 this.headerPanel.show();
32966 return this.headerPanel;
32970 * Gets a panel in the footer of the grid that can be used for toolbars etc.
32971 * After modifying the contents of this panel a call to grid.autoSize() may be
32972 * required to register any changes in size.
32973 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
32974 * @return Roo.Element
32976 getFooterPanel : function(doShow){
32978 this.footerPanel.show();
32980 return this.footerPanel;
32983 initElements : function(){
32984 var E = Roo.Element;
32985 var el = this.grid.getGridEl().dom.firstChild;
32986 var cs = el.childNodes;
32988 this.el = new E(el);
32990 this.focusEl = new E(el.firstChild);
32991 this.focusEl.swallowEvent("click", true);
32993 this.headerPanel = new E(cs[1]);
32994 this.headerPanel.enableDisplayMode("block");
32996 this.scroller = new E(cs[2]);
32997 this.scrollSizer = new E(this.scroller.dom.firstChild);
32999 this.lockedWrap = new E(cs[3]);
33000 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33001 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33003 this.mainWrap = new E(cs[4]);
33004 this.mainHd = new E(this.mainWrap.dom.firstChild);
33005 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33007 this.footerPanel = new E(cs[5]);
33008 this.footerPanel.enableDisplayMode("block");
33010 this.resizeProxy = new E(cs[6]);
33012 this.headerSelector = String.format(
33013 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33014 this.lockedHd.id, this.mainHd.id
33017 this.splitterSelector = String.format(
33018 '#{0} div.x-grid-split, #{1} div.x-grid-split',
33019 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33022 idToCssName : function(s)
33024 return s.replace(/[^a-z0-9]+/ig, '-');
33027 getHeaderCell : function(index){
33028 return Roo.DomQuery.select(this.headerSelector)[index];
33031 getHeaderCellMeasure : function(index){
33032 return this.getHeaderCell(index).firstChild;
33035 getHeaderCellText : function(index){
33036 return this.getHeaderCell(index).firstChild.firstChild;
33039 getLockedTable : function(){
33040 return this.lockedBody.dom.firstChild;
33043 getBodyTable : function(){
33044 return this.mainBody.dom.firstChild;
33047 getLockedRow : function(index){
33048 return this.getLockedTable().rows[index];
33051 getRow : function(index){
33052 return this.getBodyTable().rows[index];
33055 getRowComposite : function(index){
33057 this.rowEl = new Roo.CompositeElementLite();
33059 var els = [], lrow, mrow;
33060 if(lrow = this.getLockedRow(index)){
33063 if(mrow = this.getRow(index)){
33066 this.rowEl.elements = els;
33070 * Gets the 'td' of the cell
33072 * @param {Integer} rowIndex row to select
33073 * @param {Integer} colIndex column to select
33077 getCell : function(rowIndex, colIndex){
33078 var locked = this.cm.getLockedCount();
33080 if(colIndex < locked){
33081 source = this.lockedBody.dom.firstChild;
33083 source = this.mainBody.dom.firstChild;
33084 colIndex -= locked;
33086 return source.rows[rowIndex].childNodes[colIndex];
33089 getCellText : function(rowIndex, colIndex){
33090 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33093 getCellBox : function(cell){
33094 var b = this.fly(cell).getBox();
33095 if(Roo.isOpera){ // opera fails to report the Y
33096 b.y = cell.offsetTop + this.mainBody.getY();
33101 getCellIndex : function(cell){
33102 var id = String(cell.className).match(this.cellRE);
33104 return parseInt(id[1], 10);
33109 findHeaderIndex : function(n){
33110 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33111 return r ? this.getCellIndex(r) : false;
33114 findHeaderCell : function(n){
33115 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33116 return r ? r : false;
33119 findRowIndex : function(n){
33123 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33124 return r ? r.rowIndex : false;
33127 findCellIndex : function(node){
33128 var stop = this.el.dom;
33129 while(node && node != stop){
33130 if(this.findRE.test(node.className)){
33131 return this.getCellIndex(node);
33133 node = node.parentNode;
33138 getColumnId : function(index){
33139 return this.cm.getColumnId(index);
33142 getSplitters : function()
33144 if(this.splitterSelector){
33145 return Roo.DomQuery.select(this.splitterSelector);
33151 getSplitter : function(index){
33152 return this.getSplitters()[index];
33155 onRowOver : function(e, t){
33157 if((row = this.findRowIndex(t)) !== false){
33158 this.getRowComposite(row).addClass("x-grid-row-over");
33162 onRowOut : function(e, t){
33164 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33165 this.getRowComposite(row).removeClass("x-grid-row-over");
33169 renderHeaders : function(){
33171 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33172 var cb = [], lb = [], sb = [], lsb = [], p = {};
33173 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33174 p.cellId = "x-grid-hd-0-" + i;
33175 p.splitId = "x-grid-csplit-0-" + i;
33176 p.id = cm.getColumnId(i);
33177 p.value = cm.getColumnHeader(i) || "";
33178 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
33179 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33180 if(!cm.isLocked(i)){
33181 cb[cb.length] = ct.apply(p);
33182 sb[sb.length] = st.apply(p);
33184 lb[lb.length] = ct.apply(p);
33185 lsb[lsb.length] = st.apply(p);
33188 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33189 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33192 updateHeaders : function(){
33193 var html = this.renderHeaders();
33194 this.lockedHd.update(html[0]);
33195 this.mainHd.update(html[1]);
33199 * Focuses the specified row.
33200 * @param {Number} row The row index
33202 focusRow : function(row)
33204 //Roo.log('GridView.focusRow');
33205 var x = this.scroller.dom.scrollLeft;
33206 this.focusCell(row, 0, false);
33207 this.scroller.dom.scrollLeft = x;
33211 * Focuses the specified cell.
33212 * @param {Number} row The row index
33213 * @param {Number} col The column index
33214 * @param {Boolean} hscroll false to disable horizontal scrolling
33216 focusCell : function(row, col, hscroll)
33218 //Roo.log('GridView.focusCell');
33219 var el = this.ensureVisible(row, col, hscroll);
33220 this.focusEl.alignTo(el, "tl-tl");
33222 this.focusEl.focus();
33224 this.focusEl.focus.defer(1, this.focusEl);
33229 * Scrolls the specified cell into view
33230 * @param {Number} row The row index
33231 * @param {Number} col The column index
33232 * @param {Boolean} hscroll false to disable horizontal scrolling
33234 ensureVisible : function(row, col, hscroll)
33236 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33237 //return null; //disable for testing.
33238 if(typeof row != "number"){
33239 row = row.rowIndex;
33241 if(row < 0 && row >= this.ds.getCount()){
33244 col = (col !== undefined ? col : 0);
33245 var cm = this.grid.colModel;
33246 while(cm.isHidden(col)){
33250 var el = this.getCell(row, col);
33254 var c = this.scroller.dom;
33256 var ctop = parseInt(el.offsetTop, 10);
33257 var cleft = parseInt(el.offsetLeft, 10);
33258 var cbot = ctop + el.offsetHeight;
33259 var cright = cleft + el.offsetWidth;
33261 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33262 var stop = parseInt(c.scrollTop, 10);
33263 var sleft = parseInt(c.scrollLeft, 10);
33264 var sbot = stop + ch;
33265 var sright = sleft + c.clientWidth;
33267 Roo.log('GridView.ensureVisible:' +
33269 ' c.clientHeight:' + c.clientHeight +
33270 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33278 c.scrollTop = ctop;
33279 //Roo.log("set scrolltop to ctop DISABLE?");
33280 }else if(cbot > sbot){
33281 //Roo.log("set scrolltop to cbot-ch");
33282 c.scrollTop = cbot-ch;
33285 if(hscroll !== false){
33287 c.scrollLeft = cleft;
33288 }else if(cright > sright){
33289 c.scrollLeft = cright-c.clientWidth;
33296 updateColumns : function(){
33297 this.grid.stopEditing();
33298 var cm = this.grid.colModel, colIds = this.getColumnIds();
33299 //var totalWidth = cm.getTotalWidth();
33301 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33302 //if(cm.isHidden(i)) continue;
33303 var w = cm.getColumnWidth(i);
33304 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33305 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33307 this.updateSplitters();
33310 generateRules : function(cm){
33311 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33312 Roo.util.CSS.removeStyleSheet(rulesId);
33313 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33314 var cid = cm.getColumnId(i);
33316 if(cm.config[i].align){
33317 align = 'text-align:'+cm.config[i].align+';';
33320 if(cm.isHidden(i)){
33321 hidden = 'display:none;';
33323 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33325 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33326 this.hdSelector, cid, " {\n", align, width, "}\n",
33327 this.tdSelector, cid, " {\n",hidden,"\n}\n",
33328 this.splitSelector, cid, " {\n", hidden , "\n}\n");
33330 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33333 updateSplitters : function(){
33334 var cm = this.cm, s = this.getSplitters();
33335 if(s){ // splitters not created yet
33336 var pos = 0, locked = true;
33337 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33338 if(cm.isHidden(i)) {
33341 var w = cm.getColumnWidth(i); // make sure it's a number
33342 if(!cm.isLocked(i) && locked){
33347 s[i].style.left = (pos-this.splitOffset) + "px";
33352 handleHiddenChange : function(colModel, colIndex, hidden){
33354 this.hideColumn(colIndex);
33356 this.unhideColumn(colIndex);
33360 hideColumn : function(colIndex){
33361 var cid = this.getColumnId(colIndex);
33362 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33363 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33365 this.updateHeaders();
33367 this.updateSplitters();
33371 unhideColumn : function(colIndex){
33372 var cid = this.getColumnId(colIndex);
33373 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33374 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33377 this.updateHeaders();
33379 this.updateSplitters();
33383 insertRows : function(dm, firstRow, lastRow, isUpdate){
33384 if(firstRow == 0 && lastRow == dm.getCount()-1){
33388 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33390 var s = this.getScrollState();
33391 var markup = this.renderRows(firstRow, lastRow);
33392 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33393 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33394 this.restoreScroll(s);
33396 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33397 this.syncRowHeights(firstRow, lastRow);
33398 this.stripeRows(firstRow);
33404 bufferRows : function(markup, target, index){
33405 var before = null, trows = target.rows, tbody = target.tBodies[0];
33406 if(index < trows.length){
33407 before = trows[index];
33409 var b = document.createElement("div");
33410 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33411 var rows = b.firstChild.rows;
33412 for(var i = 0, len = rows.length; i < len; i++){
33414 tbody.insertBefore(rows[0], before);
33416 tbody.appendChild(rows[0]);
33423 deleteRows : function(dm, firstRow, lastRow){
33424 if(dm.getRowCount()<1){
33425 this.fireEvent("beforerefresh", this);
33426 this.mainBody.update("");
33427 this.lockedBody.update("");
33428 this.fireEvent("refresh", this);
33430 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33431 var bt = this.getBodyTable();
33432 var tbody = bt.firstChild;
33433 var rows = bt.rows;
33434 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33435 tbody.removeChild(rows[firstRow]);
33437 this.stripeRows(firstRow);
33438 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33442 updateRows : function(dataSource, firstRow, lastRow){
33443 var s = this.getScrollState();
33445 this.restoreScroll(s);
33448 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33452 this.updateHeaderSortState();
33455 getScrollState : function(){
33457 var sb = this.scroller.dom;
33458 return {left: sb.scrollLeft, top: sb.scrollTop};
33461 stripeRows : function(startRow){
33462 if(!this.grid.stripeRows || this.ds.getCount() < 1){
33465 startRow = startRow || 0;
33466 var rows = this.getBodyTable().rows;
33467 var lrows = this.getLockedTable().rows;
33468 var cls = ' x-grid-row-alt ';
33469 for(var i = startRow, len = rows.length; i < len; i++){
33470 var row = rows[i], lrow = lrows[i];
33471 var isAlt = ((i+1) % 2 == 0);
33472 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33473 if(isAlt == hasAlt){
33477 row.className += " x-grid-row-alt";
33479 row.className = row.className.replace("x-grid-row-alt", "");
33482 lrow.className = row.className;
33487 restoreScroll : function(state){
33488 //Roo.log('GridView.restoreScroll');
33489 var sb = this.scroller.dom;
33490 sb.scrollLeft = state.left;
33491 sb.scrollTop = state.top;
33495 syncScroll : function(){
33496 //Roo.log('GridView.syncScroll');
33497 var sb = this.scroller.dom;
33498 var sh = this.mainHd.dom;
33499 var bs = this.mainBody.dom;
33500 var lv = this.lockedBody.dom;
33501 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33502 lv.scrollTop = bs.scrollTop = sb.scrollTop;
33505 handleScroll : function(e){
33507 var sb = this.scroller.dom;
33508 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33512 handleWheel : function(e){
33513 var d = e.getWheelDelta();
33514 this.scroller.dom.scrollTop -= d*22;
33515 // set this here to prevent jumpy scrolling on large tables
33516 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33520 renderRows : function(startRow, endRow){
33521 // pull in all the crap needed to render rows
33522 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33523 var colCount = cm.getColumnCount();
33525 if(ds.getCount() < 1){
33529 // build a map for all the columns
33531 for(var i = 0; i < colCount; i++){
33532 var name = cm.getDataIndex(i);
33534 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33535 renderer : cm.getRenderer(i),
33536 id : cm.getColumnId(i),
33537 locked : cm.isLocked(i),
33538 has_editor : cm.isCellEditable(i)
33542 startRow = startRow || 0;
33543 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33545 // records to render
33546 var rs = ds.getRange(startRow, endRow);
33548 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33551 // As much as I hate to duplicate code, this was branched because FireFox really hates
33552 // [].join("") on strings. The performance difference was substantial enough to
33553 // branch this function
33554 doRender : Roo.isGecko ?
33555 function(cs, rs, ds, startRow, colCount, stripe){
33556 var ts = this.templates, ct = ts.cell, rt = ts.row;
33558 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33560 var hasListener = this.grid.hasListener('rowclass');
33562 for(var j = 0, len = rs.length; j < len; j++){
33563 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33564 for(var i = 0; i < colCount; i++){
33566 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33568 p.css = p.attr = "";
33569 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33570 if(p.value == undefined || p.value === "") {
33571 p.value = " ";
33574 p.css += ' x-grid-editable-cell';
33576 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
33577 p.css += ' x-grid-dirty-cell';
33579 var markup = ct.apply(p);
33587 if(stripe && ((rowIndex+1) % 2 == 0)){
33588 alt.push("x-grid-row-alt")
33591 alt.push( " x-grid-dirty-row");
33594 if(this.getRowClass){
33595 alt.push(this.getRowClass(r, rowIndex));
33601 rowIndex : rowIndex,
33604 this.grid.fireEvent('rowclass', this, rowcfg);
33605 alt.push(rowcfg.rowClass);
33607 rp.alt = alt.join(" ");
33608 lbuf+= rt.apply(rp);
33610 buf+= rt.apply(rp);
33612 return [lbuf, buf];
33614 function(cs, rs, ds, startRow, colCount, stripe){
33615 var ts = this.templates, ct = ts.cell, rt = ts.row;
33617 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33618 var hasListener = this.grid.hasListener('rowclass');
33621 for(var j = 0, len = rs.length; j < len; j++){
33622 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33623 for(var i = 0; i < colCount; i++){
33625 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33627 p.css = p.attr = "";
33628 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33629 if(p.value == undefined || p.value === "") {
33630 p.value = " ";
33634 p.css += ' x-grid-editable-cell';
33636 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33637 p.css += ' x-grid-dirty-cell'
33640 var markup = ct.apply(p);
33642 cb[cb.length] = markup;
33644 lcb[lcb.length] = markup;
33648 if(stripe && ((rowIndex+1) % 2 == 0)){
33649 alt.push( "x-grid-row-alt");
33652 alt.push(" x-grid-dirty-row");
33655 if(this.getRowClass){
33656 alt.push( this.getRowClass(r, rowIndex));
33662 rowIndex : rowIndex,
33665 this.grid.fireEvent('rowclass', this, rowcfg);
33666 alt.push(rowcfg.rowClass);
33669 rp.alt = alt.join(" ");
33670 rp.cells = lcb.join("");
33671 lbuf[lbuf.length] = rt.apply(rp);
33672 rp.cells = cb.join("");
33673 buf[buf.length] = rt.apply(rp);
33675 return [lbuf.join(""), buf.join("")];
33678 renderBody : function(){
33679 var markup = this.renderRows();
33680 var bt = this.templates.body;
33681 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33685 * Refreshes the grid
33686 * @param {Boolean} headersToo
33688 refresh : function(headersToo){
33689 this.fireEvent("beforerefresh", this);
33690 this.grid.stopEditing();
33691 var result = this.renderBody();
33692 this.lockedBody.update(result[0]);
33693 this.mainBody.update(result[1]);
33694 if(headersToo === true){
33695 this.updateHeaders();
33696 this.updateColumns();
33697 this.updateSplitters();
33698 this.updateHeaderSortState();
33700 this.syncRowHeights();
33702 this.fireEvent("refresh", this);
33705 handleColumnMove : function(cm, oldIndex, newIndex){
33706 this.indexMap = null;
33707 var s = this.getScrollState();
33708 this.refresh(true);
33709 this.restoreScroll(s);
33710 this.afterMove(newIndex);
33713 afterMove : function(colIndex){
33714 if(this.enableMoveAnim && Roo.enableFx){
33715 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33717 // if multisort - fix sortOrder, and reload..
33718 if (this.grid.dataSource.multiSort) {
33719 // the we can call sort again..
33720 var dm = this.grid.dataSource;
33721 var cm = this.grid.colModel;
33723 for(var i = 0; i < cm.config.length; i++ ) {
33725 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
33726 continue; // dont' bother, it's not in sort list or being set.
33729 so.push(cm.config[i].dataIndex);
33732 dm.load(dm.lastOptions);
33739 updateCell : function(dm, rowIndex, dataIndex){
33740 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33741 if(typeof colIndex == "undefined"){ // not present in grid
33744 var cm = this.grid.colModel;
33745 var cell = this.getCell(rowIndex, colIndex);
33746 var cellText = this.getCellText(rowIndex, colIndex);
33749 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33750 id : cm.getColumnId(colIndex),
33751 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33753 var renderer = cm.getRenderer(colIndex);
33754 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33755 if(typeof val == "undefined" || val === "") {
33758 cellText.innerHTML = val;
33759 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33760 this.syncRowHeights(rowIndex, rowIndex);
33763 calcColumnWidth : function(colIndex, maxRowsToMeasure){
33765 if(this.grid.autoSizeHeaders){
33766 var h = this.getHeaderCellMeasure(colIndex);
33767 maxWidth = Math.max(maxWidth, h.scrollWidth);
33770 if(this.cm.isLocked(colIndex)){
33771 tb = this.getLockedTable();
33774 tb = this.getBodyTable();
33775 index = colIndex - this.cm.getLockedCount();
33778 var rows = tb.rows;
33779 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33780 for(var i = 0; i < stopIndex; i++){
33781 var cell = rows[i].childNodes[index].firstChild;
33782 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33785 return maxWidth + /*margin for error in IE*/ 5;
33788 * Autofit a column to its content.
33789 * @param {Number} colIndex
33790 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33792 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33793 if(this.cm.isHidden(colIndex)){
33794 return; // can't calc a hidden column
33797 var cid = this.cm.getColumnId(colIndex);
33798 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33799 if(this.grid.autoSizeHeaders){
33800 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33803 var newWidth = this.calcColumnWidth(colIndex);
33804 this.cm.setColumnWidth(colIndex,
33805 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33806 if(!suppressEvent){
33807 this.grid.fireEvent("columnresize", colIndex, newWidth);
33812 * Autofits all columns to their content and then expands to fit any extra space in the grid
33814 autoSizeColumns : function(){
33815 var cm = this.grid.colModel;
33816 var colCount = cm.getColumnCount();
33817 for(var i = 0; i < colCount; i++){
33818 this.autoSizeColumn(i, true, true);
33820 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33823 this.updateColumns();
33829 * Autofits all columns to the grid's width proportionate with their current size
33830 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33832 fitColumns : function(reserveScrollSpace){
33833 var cm = this.grid.colModel;
33834 var colCount = cm.getColumnCount();
33838 for (i = 0; i < colCount; i++){
33839 if(!cm.isHidden(i) && !cm.isFixed(i)){
33840 w = cm.getColumnWidth(i);
33846 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33847 if(reserveScrollSpace){
33850 var frac = (avail - cm.getTotalWidth())/width;
33851 while (cols.length){
33854 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33856 this.updateColumns();
33860 onRowSelect : function(rowIndex){
33861 var row = this.getRowComposite(rowIndex);
33862 row.addClass("x-grid-row-selected");
33865 onRowDeselect : function(rowIndex){
33866 var row = this.getRowComposite(rowIndex);
33867 row.removeClass("x-grid-row-selected");
33870 onCellSelect : function(row, col){
33871 var cell = this.getCell(row, col);
33873 Roo.fly(cell).addClass("x-grid-cell-selected");
33877 onCellDeselect : function(row, col){
33878 var cell = this.getCell(row, col);
33880 Roo.fly(cell).removeClass("x-grid-cell-selected");
33884 updateHeaderSortState : function(){
33886 // sort state can be single { field: xxx, direction : yyy}
33887 // or { xxx=>ASC , yyy : DESC ..... }
33890 if (!this.ds.multiSort) {
33891 var state = this.ds.getSortState();
33895 mstate[state.field] = state.direction;
33896 // FIXME... - this is not used here.. but might be elsewhere..
33897 this.sortState = state;
33900 mstate = this.ds.sortToggle;
33902 //remove existing sort classes..
33904 var sc = this.sortClasses;
33905 var hds = this.el.select(this.headerSelector).removeClass(sc);
33907 for(var f in mstate) {
33909 var sortColumn = this.cm.findColumnIndex(f);
33911 if(sortColumn != -1){
33912 var sortDir = mstate[f];
33913 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33922 handleHeaderClick : function(g, index,e){
33924 Roo.log("header click");
33927 // touch events on header are handled by context
33928 this.handleHdCtx(g,index,e);
33933 if(this.headersDisabled){
33936 var dm = g.dataSource, cm = g.colModel;
33937 if(!cm.isSortable(index)){
33942 if (dm.multiSort) {
33943 // update the sortOrder
33945 for(var i = 0; i < cm.config.length; i++ ) {
33947 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
33948 continue; // dont' bother, it's not in sort list or being set.
33951 so.push(cm.config[i].dataIndex);
33957 dm.sort(cm.getDataIndex(index));
33961 destroy : function(){
33963 this.colMenu.removeAll();
33964 Roo.menu.MenuMgr.unregister(this.colMenu);
33965 this.colMenu.getEl().remove();
33966 delete this.colMenu;
33969 this.hmenu.removeAll();
33970 Roo.menu.MenuMgr.unregister(this.hmenu);
33971 this.hmenu.getEl().remove();
33974 if(this.grid.enableColumnMove){
33975 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33977 for(var dd in dds){
33978 if(!dds[dd].config.isTarget && dds[dd].dragElId){
33979 var elid = dds[dd].dragElId;
33981 Roo.get(elid).remove();
33982 } else if(dds[dd].config.isTarget){
33983 dds[dd].proxyTop.remove();
33984 dds[dd].proxyBottom.remove();
33987 if(Roo.dd.DDM.locationCache[dd]){
33988 delete Roo.dd.DDM.locationCache[dd];
33991 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33994 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
33995 this.bind(null, null);
33996 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
33999 handleLockChange : function(){
34000 this.refresh(true);
34003 onDenyColumnLock : function(){
34007 onDenyColumnHide : function(){
34011 handleHdMenuClick : function(item){
34012 var index = this.hdCtxIndex;
34013 var cm = this.cm, ds = this.ds;
34016 ds.sort(cm.getDataIndex(index), "ASC");
34019 ds.sort(cm.getDataIndex(index), "DESC");
34022 var lc = cm.getLockedCount();
34023 if(cm.getColumnCount(true) <= lc+1){
34024 this.onDenyColumnLock();
34028 cm.setLocked(index, true, true);
34029 cm.moveColumn(index, lc);
34030 this.grid.fireEvent("columnmove", index, lc);
34032 cm.setLocked(index, true);
34036 var lc = cm.getLockedCount();
34037 if((lc-1) != index){
34038 cm.setLocked(index, false, true);
34039 cm.moveColumn(index, lc-1);
34040 this.grid.fireEvent("columnmove", index, lc-1);
34042 cm.setLocked(index, false);
34045 case 'wider': // used to expand cols on touch..
34047 var cw = cm.getColumnWidth(index);
34048 cw += (item.id == 'wider' ? 1 : -1) * 50;
34049 cw = Math.max(0, cw);
34050 cw = Math.min(cw,4000);
34051 cm.setColumnWidth(index, cw);
34055 index = cm.getIndexById(item.id.substr(4));
34057 if(item.checked && cm.getColumnCount(true) <= 1){
34058 this.onDenyColumnHide();
34061 cm.setHidden(index, item.checked);
34067 beforeColMenuShow : function(){
34068 var cm = this.cm, colCount = cm.getColumnCount();
34069 this.colMenu.removeAll();
34070 for(var i = 0; i < colCount; i++){
34071 this.colMenu.add(new Roo.menu.CheckItem({
34072 id: "col-"+cm.getColumnId(i),
34073 text: cm.getColumnHeader(i),
34074 checked: !cm.isHidden(i),
34080 handleHdCtx : function(g, index, e){
34082 var hd = this.getHeaderCell(index);
34083 this.hdCtxIndex = index;
34084 var ms = this.hmenu.items, cm = this.cm;
34085 ms.get("asc").setDisabled(!cm.isSortable(index));
34086 ms.get("desc").setDisabled(!cm.isSortable(index));
34087 if(this.grid.enableColLock !== false){
34088 ms.get("lock").setDisabled(cm.isLocked(index));
34089 ms.get("unlock").setDisabled(!cm.isLocked(index));
34091 this.hmenu.show(hd, "tl-bl");
34094 handleHdOver : function(e){
34095 var hd = this.findHeaderCell(e.getTarget());
34096 if(hd && !this.headersDisabled){
34097 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34098 this.fly(hd).addClass("x-grid-hd-over");
34103 handleHdOut : function(e){
34104 var hd = this.findHeaderCell(e.getTarget());
34106 this.fly(hd).removeClass("x-grid-hd-over");
34110 handleSplitDblClick : function(e, t){
34111 var i = this.getCellIndex(t);
34112 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34113 this.autoSizeColumn(i, true);
34118 render : function(){
34121 var colCount = cm.getColumnCount();
34123 if(this.grid.monitorWindowResize === true){
34124 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34126 var header = this.renderHeaders();
34127 var body = this.templates.body.apply({rows:""});
34128 var html = this.templates.master.apply({
34131 lockedHeader: header[0],
34135 //this.updateColumns();
34137 this.grid.getGridEl().dom.innerHTML = html;
34139 this.initElements();
34141 // a kludge to fix the random scolling effect in webkit
34142 this.el.on("scroll", function() {
34143 this.el.dom.scrollTop=0; // hopefully not recursive..
34146 this.scroller.on("scroll", this.handleScroll, this);
34147 this.lockedBody.on("mousewheel", this.handleWheel, this);
34148 this.mainBody.on("mousewheel", this.handleWheel, this);
34150 this.mainHd.on("mouseover", this.handleHdOver, this);
34151 this.mainHd.on("mouseout", this.handleHdOut, this);
34152 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34153 {delegate: "."+this.splitClass});
34155 this.lockedHd.on("mouseover", this.handleHdOver, this);
34156 this.lockedHd.on("mouseout", this.handleHdOut, this);
34157 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34158 {delegate: "."+this.splitClass});
34160 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34161 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34164 this.updateSplitters();
34166 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34167 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34168 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34171 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34172 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34174 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34175 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34177 if(this.grid.enableColLock !== false){
34178 this.hmenu.add('-',
34179 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34180 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34184 this.hmenu.add('-',
34185 {id:"wider", text: this.columnsWiderText},
34186 {id:"narrow", text: this.columnsNarrowText }
34192 if(this.grid.enableColumnHide !== false){
34194 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34195 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34196 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34198 this.hmenu.add('-',
34199 {id:"columns", text: this.columnsText, menu: this.colMenu}
34202 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34204 this.grid.on("headercontextmenu", this.handleHdCtx, this);
34207 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34208 this.dd = new Roo.grid.GridDragZone(this.grid, {
34209 ddGroup : this.grid.ddGroup || 'GridDD'
34215 for(var i = 0; i < colCount; i++){
34216 if(cm.isHidden(i)){
34217 this.hideColumn(i);
34219 if(cm.config[i].align){
34220 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34221 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34225 this.updateHeaderSortState();
34227 this.beforeInitialResize();
34230 // two part rendering gives faster view to the user
34231 this.renderPhase2.defer(1, this);
34234 renderPhase2 : function(){
34235 // render the rows now
34237 if(this.grid.autoSizeColumns){
34238 this.autoSizeColumns();
34242 beforeInitialResize : function(){
34246 onColumnSplitterMoved : function(i, w){
34247 this.userResized = true;
34248 var cm = this.grid.colModel;
34249 cm.setColumnWidth(i, w, true);
34250 var cid = cm.getColumnId(i);
34251 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34252 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34253 this.updateSplitters();
34255 this.grid.fireEvent("columnresize", i, w);
34258 syncRowHeights : function(startIndex, endIndex){
34259 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34260 startIndex = startIndex || 0;
34261 var mrows = this.getBodyTable().rows;
34262 var lrows = this.getLockedTable().rows;
34263 var len = mrows.length-1;
34264 endIndex = Math.min(endIndex || len, len);
34265 for(var i = startIndex; i <= endIndex; i++){
34266 var m = mrows[i], l = lrows[i];
34267 var h = Math.max(m.offsetHeight, l.offsetHeight);
34268 m.style.height = l.style.height = h + "px";
34273 layout : function(initialRender, is2ndPass){
34275 var auto = g.autoHeight;
34276 var scrollOffset = 16;
34277 var c = g.getGridEl(), cm = this.cm,
34278 expandCol = g.autoExpandColumn,
34280 //c.beginMeasure();
34282 if(!c.dom.offsetWidth){ // display:none?
34284 this.lockedWrap.show();
34285 this.mainWrap.show();
34290 var hasLock = this.cm.isLocked(0);
34292 var tbh = this.headerPanel.getHeight();
34293 var bbh = this.footerPanel.getHeight();
34296 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34297 var newHeight = ch + c.getBorderWidth("tb");
34299 newHeight = Math.min(g.maxHeight, newHeight);
34301 c.setHeight(newHeight);
34305 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34308 var s = this.scroller;
34310 var csize = c.getSize(true);
34312 this.el.setSize(csize.width, csize.height);
34314 this.headerPanel.setWidth(csize.width);
34315 this.footerPanel.setWidth(csize.width);
34317 var hdHeight = this.mainHd.getHeight();
34318 var vw = csize.width;
34319 var vh = csize.height - (tbh + bbh);
34323 var bt = this.getBodyTable();
34325 if(cm.getLockedCount() == cm.config.length){
34326 bt = this.getLockedTable();
34329 var ltWidth = hasLock ?
34330 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34332 var scrollHeight = bt.offsetHeight;
34333 var scrollWidth = ltWidth + bt.offsetWidth;
34334 var vscroll = false, hscroll = false;
34336 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34338 var lw = this.lockedWrap, mw = this.mainWrap;
34339 var lb = this.lockedBody, mb = this.mainBody;
34341 setTimeout(function(){
34342 var t = s.dom.offsetTop;
34343 var w = s.dom.clientWidth,
34344 h = s.dom.clientHeight;
34347 lw.setSize(ltWidth, h);
34349 mw.setLeftTop(ltWidth, t);
34350 mw.setSize(w-ltWidth, h);
34352 lb.setHeight(h-hdHeight);
34353 mb.setHeight(h-hdHeight);
34355 if(is2ndPass !== true && !gv.userResized && expandCol){
34356 // high speed resize without full column calculation
34358 var ci = cm.getIndexById(expandCol);
34360 ci = cm.findColumnIndex(expandCol);
34362 ci = Math.max(0, ci); // make sure it's got at least the first col.
34363 var expandId = cm.getColumnId(ci);
34364 var tw = cm.getTotalWidth(false);
34365 var currentWidth = cm.getColumnWidth(ci);
34366 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34367 if(currentWidth != cw){
34368 cm.setColumnWidth(ci, cw, true);
34369 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34370 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34371 gv.updateSplitters();
34372 gv.layout(false, true);
34384 onWindowResize : function(){
34385 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34391 appendFooter : function(parentEl){
34395 sortAscText : "Sort Ascending",
34396 sortDescText : "Sort Descending",
34397 lockText : "Lock Column",
34398 unlockText : "Unlock Column",
34399 columnsText : "Columns",
34401 columnsWiderText : "Wider",
34402 columnsNarrowText : "Thinner"
34406 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34407 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34408 this.proxy.el.addClass('x-grid3-col-dd');
34411 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34412 handleMouseDown : function(e){
34416 callHandleMouseDown : function(e){
34417 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34422 * Ext JS Library 1.1.1
34423 * Copyright(c) 2006-2007, Ext JS, LLC.
34425 * Originally Released Under LGPL - original licence link has changed is not relivant.
34428 * <script type="text/javascript">
34432 // This is a support class used internally by the Grid components
34433 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34435 this.view = grid.getView();
34436 this.proxy = this.view.resizeProxy;
34437 Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34438 "gridSplitters" + this.grid.getGridEl().id, {
34439 dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34441 this.setHandleElId(Roo.id(hd));
34442 this.setOuterHandleElId(Roo.id(hd2));
34443 this.scroll = false;
34445 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34446 fly: Roo.Element.fly,
34448 b4StartDrag : function(x, y){
34449 this.view.headersDisabled = true;
34450 this.proxy.setHeight(this.view.mainWrap.getHeight());
34451 var w = this.cm.getColumnWidth(this.cellIndex);
34452 var minw = Math.max(w-this.grid.minColumnWidth, 0);
34453 this.resetConstraints();
34454 this.setXConstraint(minw, 1000);
34455 this.setYConstraint(0, 0);
34456 this.minX = x - minw;
34457 this.maxX = x + 1000;
34459 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34463 handleMouseDown : function(e){
34464 ev = Roo.EventObject.setEvent(e);
34465 var t = this.fly(ev.getTarget());
34466 if(t.hasClass("x-grid-split")){
34467 this.cellIndex = this.view.getCellIndex(t.dom);
34468 this.split = t.dom;
34469 this.cm = this.grid.colModel;
34470 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34471 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34476 endDrag : function(e){
34477 this.view.headersDisabled = false;
34478 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34479 var diff = endX - this.startPos;
34480 this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34483 autoOffset : function(){
34484 this.setDelta(0,0);
34488 * Ext JS Library 1.1.1
34489 * Copyright(c) 2006-2007, Ext JS, LLC.
34491 * Originally Released Under LGPL - original licence link has changed is not relivant.
34494 * <script type="text/javascript">
34498 // This is a support class used internally by the Grid components
34499 Roo.grid.GridDragZone = function(grid, config){
34500 this.view = grid.getView();
34501 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34502 if(this.view.lockedBody){
34503 this.setHandleElId(Roo.id(this.view.mainBody.dom));
34504 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34506 this.scroll = false;
34508 this.ddel = document.createElement('div');
34509 this.ddel.className = 'x-grid-dd-wrap';
34512 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34513 ddGroup : "GridDD",
34515 getDragData : function(e){
34516 var t = Roo.lib.Event.getTarget(e);
34517 var rowIndex = this.view.findRowIndex(t);
34518 var sm = this.grid.selModel;
34520 //Roo.log(rowIndex);
34522 if (sm.getSelectedCell) {
34523 // cell selection..
34524 if (!sm.getSelectedCell()) {
34527 if (rowIndex != sm.getSelectedCell()[0]) {
34533 if(rowIndex !== false){
34538 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
34540 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34543 if (e.hasModifier()){
34544 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34547 Roo.log("getDragData");
34552 rowIndex: rowIndex,
34553 selections:sm.getSelections ? sm.getSelections() : (
34554 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
34561 onInitDrag : function(e){
34562 var data = this.dragData;
34563 this.ddel.innerHTML = this.grid.getDragDropText();
34564 this.proxy.update(this.ddel);
34565 // fire start drag?
34568 afterRepair : function(){
34569 this.dragging = false;
34572 getRepairXY : function(e, data){
34576 onEndDrag : function(data, e){
34580 onValidDrop : function(dd, e, id){
34585 beforeInvalidDrop : function(e, id){
34590 * Ext JS Library 1.1.1
34591 * Copyright(c) 2006-2007, Ext JS, LLC.
34593 * Originally Released Under LGPL - original licence link has changed is not relivant.
34596 * <script type="text/javascript">
34601 * @class Roo.grid.ColumnModel
34602 * @extends Roo.util.Observable
34603 * This is the default implementation of a ColumnModel used by the Grid. It defines
34604 * the columns in the grid.
34607 var colModel = new Roo.grid.ColumnModel([
34608 {header: "Ticker", width: 60, sortable: true, locked: true},
34609 {header: "Company Name", width: 150, sortable: true},
34610 {header: "Market Cap.", width: 100, sortable: true},
34611 {header: "$ Sales", width: 100, sortable: true, renderer: money},
34612 {header: "Employees", width: 100, sortable: true, resizable: false}
34617 * The config options listed for this class are options which may appear in each
34618 * individual column definition.
34619 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34621 * @param {Object} config An Array of column config objects. See this class's
34622 * config objects for details.
34624 Roo.grid.ColumnModel = function(config){
34626 * The config passed into the constructor
34628 this.config = config;
34631 // if no id, create one
34632 // if the column does not have a dataIndex mapping,
34633 // map it to the order it is in the config
34634 for(var i = 0, len = config.length; i < len; i++){
34636 if(typeof c.dataIndex == "undefined"){
34639 if(typeof c.renderer == "string"){
34640 c.renderer = Roo.util.Format[c.renderer];
34642 if(typeof c.id == "undefined"){
34645 if(c.editor && c.editor.xtype){
34646 c.editor = Roo.factory(c.editor, Roo.grid);
34648 if(c.editor && c.editor.isFormField){
34649 c.editor = new Roo.grid.GridEditor(c.editor);
34651 this.lookup[c.id] = c;
34655 * The width of columns which have no width specified (defaults to 100)
34658 this.defaultWidth = 100;
34661 * Default sortable of columns which have no sortable specified (defaults to false)
34664 this.defaultSortable = false;
34668 * @event widthchange
34669 * Fires when the width of a column changes.
34670 * @param {ColumnModel} this
34671 * @param {Number} columnIndex The column index
34672 * @param {Number} newWidth The new width
34674 "widthchange": true,
34676 * @event headerchange
34677 * Fires when the text of a header changes.
34678 * @param {ColumnModel} this
34679 * @param {Number} columnIndex The column index
34680 * @param {Number} newText The new header text
34682 "headerchange": true,
34684 * @event hiddenchange
34685 * Fires when a column is hidden or "unhidden".
34686 * @param {ColumnModel} this
34687 * @param {Number} columnIndex The column index
34688 * @param {Boolean} hidden true if hidden, false otherwise
34690 "hiddenchange": true,
34692 * @event columnmoved
34693 * Fires when a column is moved.
34694 * @param {ColumnModel} this
34695 * @param {Number} oldIndex
34696 * @param {Number} newIndex
34698 "columnmoved" : true,
34700 * @event columlockchange
34701 * Fires when a column's locked state is changed
34702 * @param {ColumnModel} this
34703 * @param {Number} colIndex
34704 * @param {Boolean} locked true if locked
34706 "columnlockchange" : true
34708 Roo.grid.ColumnModel.superclass.constructor.call(this);
34710 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34712 * @cfg {String} header The header text to display in the Grid view.
34715 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34716 * {@link Roo.data.Record} definition from which to draw the column's value. If not
34717 * specified, the column's index is used as an index into the Record's data Array.
34720 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34721 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34724 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34725 * Defaults to the value of the {@link #defaultSortable} property.
34726 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34729 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
34732 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
34735 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34738 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34741 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34742 * given the cell's data value. See {@link #setRenderer}. If not specified, the
34743 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
34744 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
34747 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
34750 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
34753 * @cfg {String} cursor (Optional)
34756 * @cfg {String} tooltip (Optional)
34759 * @cfg {Number} xs (Optional)
34762 * @cfg {Number} sm (Optional)
34765 * @cfg {Number} md (Optional)
34768 * @cfg {Number} lg (Optional)
34771 * Returns the id of the column at the specified index.
34772 * @param {Number} index The column index
34773 * @return {String} the id
34775 getColumnId : function(index){
34776 return this.config[index].id;
34780 * Returns the column for a specified id.
34781 * @param {String} id The column id
34782 * @return {Object} the column
34784 getColumnById : function(id){
34785 return this.lookup[id];
34790 * Returns the column for a specified dataIndex.
34791 * @param {String} dataIndex The column dataIndex
34792 * @return {Object|Boolean} the column or false if not found
34794 getColumnByDataIndex: function(dataIndex){
34795 var index = this.findColumnIndex(dataIndex);
34796 return index > -1 ? this.config[index] : false;
34800 * Returns the index for a specified column id.
34801 * @param {String} id The column id
34802 * @return {Number} the index, or -1 if not found
34804 getIndexById : function(id){
34805 for(var i = 0, len = this.config.length; i < len; i++){
34806 if(this.config[i].id == id){
34814 * Returns the index for a specified column dataIndex.
34815 * @param {String} dataIndex The column dataIndex
34816 * @return {Number} the index, or -1 if not found
34819 findColumnIndex : function(dataIndex){
34820 for(var i = 0, len = this.config.length; i < len; i++){
34821 if(this.config[i].dataIndex == dataIndex){
34829 moveColumn : function(oldIndex, newIndex){
34830 var c = this.config[oldIndex];
34831 this.config.splice(oldIndex, 1);
34832 this.config.splice(newIndex, 0, c);
34833 this.dataMap = null;
34834 this.fireEvent("columnmoved", this, oldIndex, newIndex);
34837 isLocked : function(colIndex){
34838 return this.config[colIndex].locked === true;
34841 setLocked : function(colIndex, value, suppressEvent){
34842 if(this.isLocked(colIndex) == value){
34845 this.config[colIndex].locked = value;
34846 if(!suppressEvent){
34847 this.fireEvent("columnlockchange", this, colIndex, value);
34851 getTotalLockedWidth : function(){
34852 var totalWidth = 0;
34853 for(var i = 0; i < this.config.length; i++){
34854 if(this.isLocked(i) && !this.isHidden(i)){
34855 this.totalWidth += this.getColumnWidth(i);
34861 getLockedCount : function(){
34862 for(var i = 0, len = this.config.length; i < len; i++){
34863 if(!this.isLocked(i)){
34868 return this.config.length;
34872 * Returns the number of columns.
34875 getColumnCount : function(visibleOnly){
34876 if(visibleOnly === true){
34878 for(var i = 0, len = this.config.length; i < len; i++){
34879 if(!this.isHidden(i)){
34885 return this.config.length;
34889 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34890 * @param {Function} fn
34891 * @param {Object} scope (optional)
34892 * @return {Array} result
34894 getColumnsBy : function(fn, scope){
34896 for(var i = 0, len = this.config.length; i < len; i++){
34897 var c = this.config[i];
34898 if(fn.call(scope||this, c, i) === true){
34906 * Returns true if the specified column is sortable.
34907 * @param {Number} col The column index
34908 * @return {Boolean}
34910 isSortable : function(col){
34911 if(typeof this.config[col].sortable == "undefined"){
34912 return this.defaultSortable;
34914 return this.config[col].sortable;
34918 * Returns the rendering (formatting) function defined for the column.
34919 * @param {Number} col The column index.
34920 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34922 getRenderer : function(col){
34923 if(!this.config[col].renderer){
34924 return Roo.grid.ColumnModel.defaultRenderer;
34926 return this.config[col].renderer;
34930 * Sets the rendering (formatting) function for a column.
34931 * @param {Number} col The column index
34932 * @param {Function} fn The function to use to process the cell's raw data
34933 * to return HTML markup for the grid view. The render function is called with
34934 * the following parameters:<ul>
34935 * <li>Data value.</li>
34936 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34937 * <li>css A CSS style string to apply to the table cell.</li>
34938 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34939 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34940 * <li>Row index</li>
34941 * <li>Column index</li>
34942 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34944 setRenderer : function(col, fn){
34945 this.config[col].renderer = fn;
34949 * Returns the width for the specified column.
34950 * @param {Number} col The column index
34953 getColumnWidth : function(col){
34954 return this.config[col].width * 1 || this.defaultWidth;
34958 * Sets the width for a column.
34959 * @param {Number} col The column index
34960 * @param {Number} width The new width
34962 setColumnWidth : function(col, width, suppressEvent){
34963 this.config[col].width = width;
34964 this.totalWidth = null;
34965 if(!suppressEvent){
34966 this.fireEvent("widthchange", this, col, width);
34971 * Returns the total width of all columns.
34972 * @param {Boolean} includeHidden True to include hidden column widths
34975 getTotalWidth : function(includeHidden){
34976 if(!this.totalWidth){
34977 this.totalWidth = 0;
34978 for(var i = 0, len = this.config.length; i < len; i++){
34979 if(includeHidden || !this.isHidden(i)){
34980 this.totalWidth += this.getColumnWidth(i);
34984 return this.totalWidth;
34988 * Returns the header for the specified column.
34989 * @param {Number} col The column index
34992 getColumnHeader : function(col){
34993 return this.config[col].header;
34997 * Sets the header for a column.
34998 * @param {Number} col The column index
34999 * @param {String} header The new header
35001 setColumnHeader : function(col, header){
35002 this.config[col].header = header;
35003 this.fireEvent("headerchange", this, col, header);
35007 * Returns the tooltip for the specified column.
35008 * @param {Number} col The column index
35011 getColumnTooltip : function(col){
35012 return this.config[col].tooltip;
35015 * Sets the tooltip for a column.
35016 * @param {Number} col The column index
35017 * @param {String} tooltip The new tooltip
35019 setColumnTooltip : function(col, tooltip){
35020 this.config[col].tooltip = tooltip;
35024 * Returns the dataIndex for the specified column.
35025 * @param {Number} col The column index
35028 getDataIndex : function(col){
35029 return this.config[col].dataIndex;
35033 * Sets the dataIndex for a column.
35034 * @param {Number} col The column index
35035 * @param {Number} dataIndex The new dataIndex
35037 setDataIndex : function(col, dataIndex){
35038 this.config[col].dataIndex = dataIndex;
35044 * Returns true if the cell is editable.
35045 * @param {Number} colIndex The column index
35046 * @param {Number} rowIndex The row index - this is nto actually used..?
35047 * @return {Boolean}
35049 isCellEditable : function(colIndex, rowIndex){
35050 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35054 * Returns the editor defined for the cell/column.
35055 * return false or null to disable editing.
35056 * @param {Number} colIndex The column index
35057 * @param {Number} rowIndex The row index
35060 getCellEditor : function(colIndex, rowIndex){
35061 return this.config[colIndex].editor;
35065 * Sets if a column is editable.
35066 * @param {Number} col The column index
35067 * @param {Boolean} editable True if the column is editable
35069 setEditable : function(col, editable){
35070 this.config[col].editable = editable;
35075 * Returns true if the column is hidden.
35076 * @param {Number} colIndex The column index
35077 * @return {Boolean}
35079 isHidden : function(colIndex){
35080 return this.config[colIndex].hidden;
35085 * Returns true if the column width cannot be changed
35087 isFixed : function(colIndex){
35088 return this.config[colIndex].fixed;
35092 * Returns true if the column can be resized
35093 * @return {Boolean}
35095 isResizable : function(colIndex){
35096 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35099 * Sets if a column is hidden.
35100 * @param {Number} colIndex The column index
35101 * @param {Boolean} hidden True if the column is hidden
35103 setHidden : function(colIndex, hidden){
35104 this.config[colIndex].hidden = hidden;
35105 this.totalWidth = null;
35106 this.fireEvent("hiddenchange", this, colIndex, hidden);
35110 * Sets the editor for a column.
35111 * @param {Number} col The column index
35112 * @param {Object} editor The editor object
35114 setEditor : function(col, editor){
35115 this.config[col].editor = editor;
35119 Roo.grid.ColumnModel.defaultRenderer = function(value)
35121 if(typeof value == "object") {
35124 if(typeof value == "string" && value.length < 1){
35128 return String.format("{0}", value);
35131 // Alias for backwards compatibility
35132 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35135 * Ext JS Library 1.1.1
35136 * Copyright(c) 2006-2007, Ext JS, LLC.
35138 * Originally Released Under LGPL - original licence link has changed is not relivant.
35141 * <script type="text/javascript">
35145 * @class Roo.grid.AbstractSelectionModel
35146 * @extends Roo.util.Observable
35147 * Abstract base class for grid SelectionModels. It provides the interface that should be
35148 * implemented by descendant classes. This class should not be directly instantiated.
35151 Roo.grid.AbstractSelectionModel = function(){
35152 this.locked = false;
35153 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35156 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
35157 /** @ignore Called by the grid automatically. Do not call directly. */
35158 init : function(grid){
35164 * Locks the selections.
35167 this.locked = true;
35171 * Unlocks the selections.
35173 unlock : function(){
35174 this.locked = false;
35178 * Returns true if the selections are locked.
35179 * @return {Boolean}
35181 isLocked : function(){
35182 return this.locked;
35186 * Ext JS Library 1.1.1
35187 * Copyright(c) 2006-2007, Ext JS, LLC.
35189 * Originally Released Under LGPL - original licence link has changed is not relivant.
35192 * <script type="text/javascript">
35195 * @extends Roo.grid.AbstractSelectionModel
35196 * @class Roo.grid.RowSelectionModel
35197 * The default SelectionModel used by {@link Roo.grid.Grid}.
35198 * It supports multiple selections and keyboard selection/navigation.
35200 * @param {Object} config
35202 Roo.grid.RowSelectionModel = function(config){
35203 Roo.apply(this, config);
35204 this.selections = new Roo.util.MixedCollection(false, function(o){
35209 this.lastActive = false;
35213 * @event selectionchange
35214 * Fires when the selection changes
35215 * @param {SelectionModel} this
35217 "selectionchange" : true,
35219 * @event afterselectionchange
35220 * Fires after the selection changes (eg. by key press or clicking)
35221 * @param {SelectionModel} this
35223 "afterselectionchange" : true,
35225 * @event beforerowselect
35226 * Fires when a row is selected being selected, return false to cancel.
35227 * @param {SelectionModel} this
35228 * @param {Number} rowIndex The selected index
35229 * @param {Boolean} keepExisting False if other selections will be cleared
35231 "beforerowselect" : true,
35234 * Fires when a row is selected.
35235 * @param {SelectionModel} this
35236 * @param {Number} rowIndex The selected index
35237 * @param {Roo.data.Record} r The record
35239 "rowselect" : true,
35241 * @event rowdeselect
35242 * Fires when a row is deselected.
35243 * @param {SelectionModel} this
35244 * @param {Number} rowIndex The selected index
35246 "rowdeselect" : true
35248 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35249 this.locked = false;
35252 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
35254 * @cfg {Boolean} singleSelect
35255 * True to allow selection of only one row at a time (defaults to false)
35257 singleSelect : false,
35260 initEvents : function(){
35262 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35263 this.grid.on("mousedown", this.handleMouseDown, this);
35264 }else{ // allow click to work like normal
35265 this.grid.on("rowclick", this.handleDragableRowClick, this);
35268 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35269 "up" : function(e){
35271 this.selectPrevious(e.shiftKey);
35272 }else if(this.last !== false && this.lastActive !== false){
35273 var last = this.last;
35274 this.selectRange(this.last, this.lastActive-1);
35275 this.grid.getView().focusRow(this.lastActive);
35276 if(last !== false){
35280 this.selectFirstRow();
35282 this.fireEvent("afterselectionchange", this);
35284 "down" : function(e){
35286 this.selectNext(e.shiftKey);
35287 }else if(this.last !== false && this.lastActive !== false){
35288 var last = this.last;
35289 this.selectRange(this.last, this.lastActive+1);
35290 this.grid.getView().focusRow(this.lastActive);
35291 if(last !== false){
35295 this.selectFirstRow();
35297 this.fireEvent("afterselectionchange", this);
35302 var view = this.grid.view;
35303 view.on("refresh", this.onRefresh, this);
35304 view.on("rowupdated", this.onRowUpdated, this);
35305 view.on("rowremoved", this.onRemove, this);
35309 onRefresh : function(){
35310 var ds = this.grid.dataSource, i, v = this.grid.view;
35311 var s = this.selections;
35312 s.each(function(r){
35313 if((i = ds.indexOfId(r.id)) != -1){
35315 s.add(ds.getAt(i)); // updating the selection relate data
35323 onRemove : function(v, index, r){
35324 this.selections.remove(r);
35328 onRowUpdated : function(v, index, r){
35329 if(this.isSelected(r)){
35330 v.onRowSelect(index);
35336 * @param {Array} records The records to select
35337 * @param {Boolean} keepExisting (optional) True to keep existing selections
35339 selectRecords : function(records, keepExisting){
35341 this.clearSelections();
35343 var ds = this.grid.dataSource;
35344 for(var i = 0, len = records.length; i < len; i++){
35345 this.selectRow(ds.indexOf(records[i]), true);
35350 * Gets the number of selected rows.
35353 getCount : function(){
35354 return this.selections.length;
35358 * Selects the first row in the grid.
35360 selectFirstRow : function(){
35365 * Select the last row.
35366 * @param {Boolean} keepExisting (optional) True to keep existing selections
35368 selectLastRow : function(keepExisting){
35369 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35373 * Selects the row immediately following the last selected row.
35374 * @param {Boolean} keepExisting (optional) True to keep existing selections
35376 selectNext : function(keepExisting){
35377 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35378 this.selectRow(this.last+1, keepExisting);
35379 this.grid.getView().focusRow(this.last);
35384 * Selects the row that precedes the last selected row.
35385 * @param {Boolean} keepExisting (optional) True to keep existing selections
35387 selectPrevious : function(keepExisting){
35389 this.selectRow(this.last-1, keepExisting);
35390 this.grid.getView().focusRow(this.last);
35395 * Returns the selected records
35396 * @return {Array} Array of selected records
35398 getSelections : function(){
35399 return [].concat(this.selections.items);
35403 * Returns the first selected record.
35406 getSelected : function(){
35407 return this.selections.itemAt(0);
35412 * Clears all selections.
35414 clearSelections : function(fast){
35419 var ds = this.grid.dataSource;
35420 var s = this.selections;
35421 s.each(function(r){
35422 this.deselectRow(ds.indexOfId(r.id));
35426 this.selections.clear();
35433 * Selects all rows.
35435 selectAll : function(){
35439 this.selections.clear();
35440 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35441 this.selectRow(i, true);
35446 * Returns True if there is a selection.
35447 * @return {Boolean}
35449 hasSelection : function(){
35450 return this.selections.length > 0;
35454 * Returns True if the specified row is selected.
35455 * @param {Number/Record} record The record or index of the record to check
35456 * @return {Boolean}
35458 isSelected : function(index){
35459 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35460 return (r && this.selections.key(r.id) ? true : false);
35464 * Returns True if the specified record id is selected.
35465 * @param {String} id The id of record to check
35466 * @return {Boolean}
35468 isIdSelected : function(id){
35469 return (this.selections.key(id) ? true : false);
35473 handleMouseDown : function(e, t){
35474 var view = this.grid.getView(), rowIndex;
35475 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35478 if(e.shiftKey && this.last !== false){
35479 var last = this.last;
35480 this.selectRange(last, rowIndex, e.ctrlKey);
35481 this.last = last; // reset the last
35482 view.focusRow(rowIndex);
35484 var isSelected = this.isSelected(rowIndex);
35485 if(e.button !== 0 && isSelected){
35486 view.focusRow(rowIndex);
35487 }else if(e.ctrlKey && isSelected){
35488 this.deselectRow(rowIndex);
35489 }else if(!isSelected){
35490 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35491 view.focusRow(rowIndex);
35494 this.fireEvent("afterselectionchange", this);
35497 handleDragableRowClick : function(grid, rowIndex, e)
35499 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35500 this.selectRow(rowIndex, false);
35501 grid.view.focusRow(rowIndex);
35502 this.fireEvent("afterselectionchange", this);
35507 * Selects multiple rows.
35508 * @param {Array} rows Array of the indexes of the row to select
35509 * @param {Boolean} keepExisting (optional) True to keep existing selections
35511 selectRows : function(rows, keepExisting){
35513 this.clearSelections();
35515 for(var i = 0, len = rows.length; i < len; i++){
35516 this.selectRow(rows[i], true);
35521 * Selects a range of rows. All rows in between startRow and endRow are also selected.
35522 * @param {Number} startRow The index of the first row in the range
35523 * @param {Number} endRow The index of the last row in the range
35524 * @param {Boolean} keepExisting (optional) True to retain existing selections
35526 selectRange : function(startRow, endRow, keepExisting){
35531 this.clearSelections();
35533 if(startRow <= endRow){
35534 for(var i = startRow; i <= endRow; i++){
35535 this.selectRow(i, true);
35538 for(var i = startRow; i >= endRow; i--){
35539 this.selectRow(i, true);
35545 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35546 * @param {Number} startRow The index of the first row in the range
35547 * @param {Number} endRow The index of the last row in the range
35549 deselectRange : function(startRow, endRow, preventViewNotify){
35553 for(var i = startRow; i <= endRow; i++){
35554 this.deselectRow(i, preventViewNotify);
35560 * @param {Number} row The index of the row to select
35561 * @param {Boolean} keepExisting (optional) True to keep existing selections
35563 selectRow : function(index, keepExisting, preventViewNotify){
35564 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
35567 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35568 if(!keepExisting || this.singleSelect){
35569 this.clearSelections();
35571 var r = this.grid.dataSource.getAt(index);
35572 this.selections.add(r);
35573 this.last = this.lastActive = index;
35574 if(!preventViewNotify){
35575 this.grid.getView().onRowSelect(index);
35577 this.fireEvent("rowselect", this, index, r);
35578 this.fireEvent("selectionchange", this);
35584 * @param {Number} row The index of the row to deselect
35586 deselectRow : function(index, preventViewNotify){
35590 if(this.last == index){
35593 if(this.lastActive == index){
35594 this.lastActive = false;
35596 var r = this.grid.dataSource.getAt(index);
35597 this.selections.remove(r);
35598 if(!preventViewNotify){
35599 this.grid.getView().onRowDeselect(index);
35601 this.fireEvent("rowdeselect", this, index);
35602 this.fireEvent("selectionchange", this);
35606 restoreLast : function(){
35608 this.last = this._last;
35613 acceptsNav : function(row, col, cm){
35614 return !cm.isHidden(col) && cm.isCellEditable(col, row);
35618 onEditorKey : function(field, e){
35619 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35624 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35626 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35628 }else if(k == e.ENTER && !e.ctrlKey){
35632 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35634 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35636 }else if(k == e.ESC){
35640 g.startEditing(newCell[0], newCell[1]);
35645 * Ext JS Library 1.1.1
35646 * Copyright(c) 2006-2007, Ext JS, LLC.
35648 * Originally Released Under LGPL - original licence link has changed is not relivant.
35651 * <script type="text/javascript">
35654 * @class Roo.grid.CellSelectionModel
35655 * @extends Roo.grid.AbstractSelectionModel
35656 * This class provides the basic implementation for cell selection in a grid.
35658 * @param {Object} config The object containing the configuration of this model.
35659 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
35661 Roo.grid.CellSelectionModel = function(config){
35662 Roo.apply(this, config);
35664 this.selection = null;
35668 * @event beforerowselect
35669 * Fires before a cell is selected.
35670 * @param {SelectionModel} this
35671 * @param {Number} rowIndex The selected row index
35672 * @param {Number} colIndex The selected cell index
35674 "beforecellselect" : true,
35676 * @event cellselect
35677 * Fires when a cell is selected.
35678 * @param {SelectionModel} this
35679 * @param {Number} rowIndex The selected row index
35680 * @param {Number} colIndex The selected cell index
35682 "cellselect" : true,
35684 * @event selectionchange
35685 * Fires when the active selection changes.
35686 * @param {SelectionModel} this
35687 * @param {Object} selection null for no selection or an object (o) with two properties
35689 <li>o.record: the record object for the row the selection is in</li>
35690 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35693 "selectionchange" : true,
35696 * Fires when the tab (or enter) was pressed on the last editable cell
35697 * You can use this to trigger add new row.
35698 * @param {SelectionModel} this
35702 * @event beforeeditnext
35703 * Fires before the next editable sell is made active
35704 * You can use this to skip to another cell or fire the tabend
35705 * if you set cell to false
35706 * @param {Object} eventdata object : { cell : [ row, col ] }
35708 "beforeeditnext" : true
35710 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35713 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
35715 enter_is_tab: false,
35718 initEvents : function(){
35719 this.grid.on("mousedown", this.handleMouseDown, this);
35720 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35721 var view = this.grid.view;
35722 view.on("refresh", this.onViewChange, this);
35723 view.on("rowupdated", this.onRowUpdated, this);
35724 view.on("beforerowremoved", this.clearSelections, this);
35725 view.on("beforerowsinserted", this.clearSelections, this);
35726 if(this.grid.isEditor){
35727 this.grid.on("beforeedit", this.beforeEdit, this);
35732 beforeEdit : function(e){
35733 this.select(e.row, e.column, false, true, e.record);
35737 onRowUpdated : function(v, index, r){
35738 if(this.selection && this.selection.record == r){
35739 v.onCellSelect(index, this.selection.cell[1]);
35744 onViewChange : function(){
35745 this.clearSelections(true);
35749 * Returns the currently selected cell,.
35750 * @return {Array} The selected cell (row, column) or null if none selected.
35752 getSelectedCell : function(){
35753 return this.selection ? this.selection.cell : null;
35757 * Clears all selections.
35758 * @param {Boolean} true to prevent the gridview from being notified about the change.
35760 clearSelections : function(preventNotify){
35761 var s = this.selection;
35763 if(preventNotify !== true){
35764 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35766 this.selection = null;
35767 this.fireEvent("selectionchange", this, null);
35772 * Returns true if there is a selection.
35773 * @return {Boolean}
35775 hasSelection : function(){
35776 return this.selection ? true : false;
35780 handleMouseDown : function(e, t){
35781 var v = this.grid.getView();
35782 if(this.isLocked()){
35785 var row = v.findRowIndex(t);
35786 var cell = v.findCellIndex(t);
35787 if(row !== false && cell !== false){
35788 this.select(row, cell);
35794 * @param {Number} rowIndex
35795 * @param {Number} collIndex
35797 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35798 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35799 this.clearSelections();
35800 r = r || this.grid.dataSource.getAt(rowIndex);
35803 cell : [rowIndex, colIndex]
35805 if(!preventViewNotify){
35806 var v = this.grid.getView();
35807 v.onCellSelect(rowIndex, colIndex);
35808 if(preventFocus !== true){
35809 v.focusCell(rowIndex, colIndex);
35812 this.fireEvent("cellselect", this, rowIndex, colIndex);
35813 this.fireEvent("selectionchange", this, this.selection);
35818 isSelectable : function(rowIndex, colIndex, cm){
35819 return !cm.isHidden(colIndex);
35823 handleKeyDown : function(e){
35824 //Roo.log('Cell Sel Model handleKeyDown');
35825 if(!e.isNavKeyPress()){
35828 var g = this.grid, s = this.selection;
35831 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
35833 this.select(cell[0], cell[1]);
35838 var walk = function(row, col, step){
35839 return g.walkCells(row, col, step, sm.isSelectable, sm);
35841 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35848 // handled by onEditorKey
35849 if (g.isEditor && g.editing) {
35853 newCell = walk(r, c-1, -1);
35855 newCell = walk(r, c+1, 1);
35860 newCell = walk(r+1, c, 1);
35864 newCell = walk(r-1, c, -1);
35868 newCell = walk(r, c+1, 1);
35872 newCell = walk(r, c-1, -1);
35877 if(g.isEditor && !g.editing){
35878 g.startEditing(r, c);
35887 this.select(newCell[0], newCell[1]);
35893 acceptsNav : function(row, col, cm){
35894 return !cm.isHidden(col) && cm.isCellEditable(col, row);
35898 * @param {Number} field (not used) - as it's normally used as a listener
35899 * @param {Number} e - event - fake it by using
35901 * var e = Roo.EventObjectImpl.prototype;
35902 * e.keyCode = e.TAB
35906 onEditorKey : function(field, e){
35908 var k = e.getKey(),
35911 ed = g.activeEditor,
35913 ///Roo.log('onEditorKey' + k);
35916 if (this.enter_is_tab && k == e.ENTER) {
35922 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35924 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35930 } else if(k == e.ENTER && !e.ctrlKey){
35933 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35935 } else if(k == e.ESC){
35940 var ecall = { cell : newCell, forward : forward };
35941 this.fireEvent('beforeeditnext', ecall );
35942 newCell = ecall.cell;
35943 forward = ecall.forward;
35947 //Roo.log('next cell after edit');
35948 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
35949 } else if (forward) {
35950 // tabbed past last
35951 this.fireEvent.defer(100, this, ['tabend',this]);
35956 * Ext JS Library 1.1.1
35957 * Copyright(c) 2006-2007, Ext JS, LLC.
35959 * Originally Released Under LGPL - original licence link has changed is not relivant.
35962 * <script type="text/javascript">
35966 * @class Roo.grid.EditorGrid
35967 * @extends Roo.grid.Grid
35968 * Class for creating and editable grid.
35969 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35970 * The container MUST have some type of size defined for the grid to fill. The container will be
35971 * automatically set to position relative if it isn't already.
35972 * @param {Object} dataSource The data model to bind to
35973 * @param {Object} colModel The column model with info about this grid's columns
35975 Roo.grid.EditorGrid = function(container, config){
35976 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
35977 this.getGridEl().addClass("xedit-grid");
35979 if(!this.selModel){
35980 this.selModel = new Roo.grid.CellSelectionModel();
35983 this.activeEditor = null;
35987 * @event beforeedit
35988 * Fires before cell editing is triggered. The edit event object has the following properties <br />
35989 * <ul style="padding:5px;padding-left:16px;">
35990 * <li>grid - This grid</li>
35991 * <li>record - The record being edited</li>
35992 * <li>field - The field name being edited</li>
35993 * <li>value - The value for the field being edited.</li>
35994 * <li>row - The grid row index</li>
35995 * <li>column - The grid column index</li>
35996 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35998 * @param {Object} e An edit event (see above for description)
36000 "beforeedit" : true,
36003 * Fires after a cell is edited. <br />
36004 * <ul style="padding:5px;padding-left:16px;">
36005 * <li>grid - This grid</li>
36006 * <li>record - The record being edited</li>
36007 * <li>field - The field name being edited</li>
36008 * <li>value - The value being set</li>
36009 * <li>originalValue - The original value for the field, before the edit.</li>
36010 * <li>row - The grid row index</li>
36011 * <li>column - The grid column index</li>
36013 * @param {Object} e An edit event (see above for description)
36015 "afteredit" : true,
36017 * @event validateedit
36018 * Fires after a cell is edited, but before the value is set in the record.
36019 * You can use this to modify the value being set in the field, Return false
36020 * to cancel the change. The edit event object has the following properties <br />
36021 * <ul style="padding:5px;padding-left:16px;">
36022 * <li>editor - This editor</li>
36023 * <li>grid - This grid</li>
36024 * <li>record - The record being edited</li>
36025 * <li>field - The field name being edited</li>
36026 * <li>value - The value being set</li>
36027 * <li>originalValue - The original value for the field, before the edit.</li>
36028 * <li>row - The grid row index</li>
36029 * <li>column - The grid column index</li>
36030 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36032 * @param {Object} e An edit event (see above for description)
36034 "validateedit" : true
36036 this.on("bodyscroll", this.stopEditing, this);
36037 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
36040 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36042 * @cfg {Number} clicksToEdit
36043 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36050 trackMouseOver: false, // causes very odd FF errors
36052 onCellDblClick : function(g, row, col){
36053 this.startEditing(row, col);
36056 onEditComplete : function(ed, value, startValue){
36057 this.editing = false;
36058 this.activeEditor = null;
36059 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36061 var field = this.colModel.getDataIndex(ed.col);
36066 originalValue: startValue,
36073 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36076 if(String(value) !== String(startValue)){
36078 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36079 r.set(field, e.value);
36080 // if we are dealing with a combo box..
36081 // then we also set the 'name' colum to be the displayField
36082 if (ed.field.displayField && ed.field.name) {
36083 r.set(ed.field.name, ed.field.el.dom.value);
36086 delete e.cancel; //?? why!!!
36087 this.fireEvent("afteredit", e);
36090 this.fireEvent("afteredit", e); // always fire it!
36092 this.view.focusCell(ed.row, ed.col);
36096 * Starts editing the specified for the specified row/column
36097 * @param {Number} rowIndex
36098 * @param {Number} colIndex
36100 startEditing : function(row, col){
36101 this.stopEditing();
36102 if(this.colModel.isCellEditable(col, row)){
36103 this.view.ensureVisible(row, col, true);
36105 var r = this.dataSource.getAt(row);
36106 var field = this.colModel.getDataIndex(col);
36107 var cell = Roo.get(this.view.getCell(row,col));
36112 value: r.data[field],
36117 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36118 this.editing = true;
36119 var ed = this.colModel.getCellEditor(col, row);
36125 ed.render(ed.parentEl || document.body);
36131 (function(){ // complex but required for focus issues in safari, ie and opera
36135 ed.on("complete", this.onEditComplete, this, {single: true});
36136 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
36137 this.activeEditor = ed;
36138 var v = r.data[field];
36139 ed.startEdit(this.view.getCell(row, col), v);
36140 // combo's with 'displayField and name set
36141 if (ed.field.displayField && ed.field.name) {
36142 ed.field.el.dom.value = r.data[ed.field.name];
36146 }).defer(50, this);
36152 * Stops any active editing
36154 stopEditing : function(){
36155 if(this.activeEditor){
36156 this.activeEditor.completeEdit();
36158 this.activeEditor = null;
36162 * Called to get grid's drag proxy text, by default returns this.ddText.
36165 getDragDropText : function(){
36166 var count = this.selModel.getSelectedCell() ? 1 : 0;
36167 return String.format(this.ddText, count, count == 1 ? '' : 's');
36172 * Ext JS Library 1.1.1
36173 * Copyright(c) 2006-2007, Ext JS, LLC.
36175 * Originally Released Under LGPL - original licence link has changed is not relivant.
36178 * <script type="text/javascript">
36181 // private - not really -- you end up using it !
36182 // This is a support class used internally by the Grid components
36185 * @class Roo.grid.GridEditor
36186 * @extends Roo.Editor
36187 * Class for creating and editable grid elements.
36188 * @param {Object} config any settings (must include field)
36190 Roo.grid.GridEditor = function(field, config){
36191 if (!config && field.field) {
36193 field = Roo.factory(config.field, Roo.form);
36195 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36196 field.monitorTab = false;
36199 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36202 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36205 alignment: "tl-tl",
36208 cls: "x-small-editor x-grid-editor",
36213 * Ext JS Library 1.1.1
36214 * Copyright(c) 2006-2007, Ext JS, LLC.
36216 * Originally Released Under LGPL - original licence link has changed is not relivant.
36219 * <script type="text/javascript">
36224 Roo.grid.PropertyRecord = Roo.data.Record.create([
36225 {name:'name',type:'string'}, 'value'
36229 Roo.grid.PropertyStore = function(grid, source){
36231 this.store = new Roo.data.Store({
36232 recordType : Roo.grid.PropertyRecord
36234 this.store.on('update', this.onUpdate, this);
36236 this.setSource(source);
36238 Roo.grid.PropertyStore.superclass.constructor.call(this);
36243 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36244 setSource : function(o){
36246 this.store.removeAll();
36249 if(this.isEditableValue(o[k])){
36250 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36253 this.store.loadRecords({records: data}, {}, true);
36256 onUpdate : function(ds, record, type){
36257 if(type == Roo.data.Record.EDIT){
36258 var v = record.data['value'];
36259 var oldValue = record.modified['value'];
36260 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36261 this.source[record.id] = v;
36263 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36270 getProperty : function(row){
36271 return this.store.getAt(row);
36274 isEditableValue: function(val){
36275 if(val && val instanceof Date){
36277 }else if(typeof val == 'object' || typeof val == 'function'){
36283 setValue : function(prop, value){
36284 this.source[prop] = value;
36285 this.store.getById(prop).set('value', value);
36288 getSource : function(){
36289 return this.source;
36293 Roo.grid.PropertyColumnModel = function(grid, store){
36296 g.PropertyColumnModel.superclass.constructor.call(this, [
36297 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36298 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36300 this.store = store;
36301 this.bselect = Roo.DomHelper.append(document.body, {
36302 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36303 {tag: 'option', value: 'true', html: 'true'},
36304 {tag: 'option', value: 'false', html: 'false'}
36307 Roo.id(this.bselect);
36310 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36311 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36312 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36313 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36314 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36316 this.renderCellDelegate = this.renderCell.createDelegate(this);
36317 this.renderPropDelegate = this.renderProp.createDelegate(this);
36320 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36324 valueText : 'Value',
36326 dateFormat : 'm/j/Y',
36329 renderDate : function(dateVal){
36330 return dateVal.dateFormat(this.dateFormat);
36333 renderBool : function(bVal){
36334 return bVal ? 'true' : 'false';
36337 isCellEditable : function(colIndex, rowIndex){
36338 return colIndex == 1;
36341 getRenderer : function(col){
36343 this.renderCellDelegate : this.renderPropDelegate;
36346 renderProp : function(v){
36347 return this.getPropertyName(v);
36350 renderCell : function(val){
36352 if(val instanceof Date){
36353 rv = this.renderDate(val);
36354 }else if(typeof val == 'boolean'){
36355 rv = this.renderBool(val);
36357 return Roo.util.Format.htmlEncode(rv);
36360 getPropertyName : function(name){
36361 var pn = this.grid.propertyNames;
36362 return pn && pn[name] ? pn[name] : name;
36365 getCellEditor : function(colIndex, rowIndex){
36366 var p = this.store.getProperty(rowIndex);
36367 var n = p.data['name'], val = p.data['value'];
36369 if(typeof(this.grid.customEditors[n]) == 'string'){
36370 return this.editors[this.grid.customEditors[n]];
36372 if(typeof(this.grid.customEditors[n]) != 'undefined'){
36373 return this.grid.customEditors[n];
36375 if(val instanceof Date){
36376 return this.editors['date'];
36377 }else if(typeof val == 'number'){
36378 return this.editors['number'];
36379 }else if(typeof val == 'boolean'){
36380 return this.editors['boolean'];
36382 return this.editors['string'];
36388 * @class Roo.grid.PropertyGrid
36389 * @extends Roo.grid.EditorGrid
36390 * This class represents the interface of a component based property grid control.
36391 * <br><br>Usage:<pre><code>
36392 var grid = new Roo.grid.PropertyGrid("my-container-id", {
36400 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36401 * The container MUST have some type of size defined for the grid to fill. The container will be
36402 * automatically set to position relative if it isn't already.
36403 * @param {Object} config A config object that sets properties on this grid.
36405 Roo.grid.PropertyGrid = function(container, config){
36406 config = config || {};
36407 var store = new Roo.grid.PropertyStore(this);
36408 this.store = store;
36409 var cm = new Roo.grid.PropertyColumnModel(this, store);
36410 store.store.sort('name', 'ASC');
36411 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36414 enableColLock:false,
36415 enableColumnMove:false,
36417 trackMouseOver: false,
36420 this.getGridEl().addClass('x-props-grid');
36421 this.lastEditRow = null;
36422 this.on('columnresize', this.onColumnResize, this);
36425 * @event beforepropertychange
36426 * Fires before a property changes (return false to stop?)
36427 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36428 * @param {String} id Record Id
36429 * @param {String} newval New Value
36430 * @param {String} oldval Old Value
36432 "beforepropertychange": true,
36434 * @event propertychange
36435 * Fires after a property changes
36436 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36437 * @param {String} id Record Id
36438 * @param {String} newval New Value
36439 * @param {String} oldval Old Value
36441 "propertychange": true
36443 this.customEditors = this.customEditors || {};
36445 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36448 * @cfg {Object} customEditors map of colnames=> custom editors.
36449 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36450 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36451 * false disables editing of the field.
36455 * @cfg {Object} propertyNames map of property Names to their displayed value
36458 render : function(){
36459 Roo.grid.PropertyGrid.superclass.render.call(this);
36460 this.autoSize.defer(100, this);
36463 autoSize : function(){
36464 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36466 this.view.fitColumns();
36470 onColumnResize : function(){
36471 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36475 * Sets the data for the Grid
36476 * accepts a Key => Value object of all the elements avaiable.
36477 * @param {Object} data to appear in grid.
36479 setSource : function(source){
36480 this.store.setSource(source);
36484 * Gets all the data from the grid.
36485 * @return {Object} data data stored in grid
36487 getSource : function(){
36488 return this.store.getSource();
36497 * @class Roo.grid.Calendar
36498 * @extends Roo.util.Grid
36499 * This class extends the Grid to provide a calendar widget
36500 * <br><br>Usage:<pre><code>
36501 var grid = new Roo.grid.Calendar("my-container-id", {
36504 selModel: mySelectionModel,
36505 autoSizeColumns: true,
36506 monitorWindowResize: false,
36507 trackMouseOver: true
36508 eventstore : real data store..
36514 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36515 * The container MUST have some type of size defined for the grid to fill. The container will be
36516 * automatically set to position relative if it isn't already.
36517 * @param {Object} config A config object that sets properties on this grid.
36519 Roo.grid.Calendar = function(container, config){
36520 // initialize the container
36521 this.container = Roo.get(container);
36522 this.container.update("");
36523 this.container.setStyle("overflow", "hidden");
36524 this.container.addClass('x-grid-container');
36526 this.id = this.container.id;
36528 Roo.apply(this, config);
36529 // check and correct shorthanded configs
36533 for (var r = 0;r < 6;r++) {
36536 for (var c =0;c < 7;c++) {
36540 if (this.eventStore) {
36541 this.eventStore= Roo.factory(this.eventStore, Roo.data);
36542 this.eventStore.on('load',this.onLoad, this);
36543 this.eventStore.on('beforeload',this.clearEvents, this);
36547 this.dataSource = new Roo.data.Store({
36548 proxy: new Roo.data.MemoryProxy(rows),
36549 reader: new Roo.data.ArrayReader({}, [
36550 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
36553 this.dataSource.load();
36554 this.ds = this.dataSource;
36555 this.ds.xmodule = this.xmodule || false;
36558 var cellRender = function(v,x,r)
36560 return String.format(
36561 '<div class="fc-day fc-widget-content"><div>' +
36562 '<div class="fc-event-container"></div>' +
36563 '<div class="fc-day-number">{0}</div>'+
36565 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
36566 '</div></div>', v);
36571 this.colModel = new Roo.grid.ColumnModel( [
36573 xtype: 'ColumnModel',
36575 dataIndex : 'weekday0',
36577 renderer : cellRender
36580 xtype: 'ColumnModel',
36582 dataIndex : 'weekday1',
36584 renderer : cellRender
36587 xtype: 'ColumnModel',
36589 dataIndex : 'weekday2',
36590 header : 'Tuesday',
36591 renderer : cellRender
36594 xtype: 'ColumnModel',
36596 dataIndex : 'weekday3',
36597 header : 'Wednesday',
36598 renderer : cellRender
36601 xtype: 'ColumnModel',
36603 dataIndex : 'weekday4',
36604 header : 'Thursday',
36605 renderer : cellRender
36608 xtype: 'ColumnModel',
36610 dataIndex : 'weekday5',
36612 renderer : cellRender
36615 xtype: 'ColumnModel',
36617 dataIndex : 'weekday6',
36618 header : 'Saturday',
36619 renderer : cellRender
36622 this.cm = this.colModel;
36623 this.cm.xmodule = this.xmodule || false;
36627 //this.selModel = new Roo.grid.CellSelectionModel();
36628 //this.sm = this.selModel;
36629 //this.selModel.init(this);
36633 this.container.setWidth(this.width);
36637 this.container.setHeight(this.height);
36644 * The raw click event for the entire grid.
36645 * @param {Roo.EventObject} e
36650 * The raw dblclick event for the entire grid.
36651 * @param {Roo.EventObject} e
36655 * @event contextmenu
36656 * The raw contextmenu event for the entire grid.
36657 * @param {Roo.EventObject} e
36659 "contextmenu" : true,
36662 * The raw mousedown event for the entire grid.
36663 * @param {Roo.EventObject} e
36665 "mousedown" : true,
36668 * The raw mouseup event for the entire grid.
36669 * @param {Roo.EventObject} e
36674 * The raw mouseover event for the entire grid.
36675 * @param {Roo.EventObject} e
36677 "mouseover" : true,
36680 * The raw mouseout event for the entire grid.
36681 * @param {Roo.EventObject} e
36686 * The raw keypress event for the entire grid.
36687 * @param {Roo.EventObject} e
36692 * The raw keydown event for the entire grid.
36693 * @param {Roo.EventObject} e
36701 * Fires when a cell is clicked
36702 * @param {Grid} this
36703 * @param {Number} rowIndex
36704 * @param {Number} columnIndex
36705 * @param {Roo.EventObject} e
36707 "cellclick" : true,
36709 * @event celldblclick
36710 * Fires when a cell is double clicked
36711 * @param {Grid} this
36712 * @param {Number} rowIndex
36713 * @param {Number} columnIndex
36714 * @param {Roo.EventObject} e
36716 "celldblclick" : true,
36719 * Fires when a row is clicked
36720 * @param {Grid} this
36721 * @param {Number} rowIndex
36722 * @param {Roo.EventObject} e
36726 * @event rowdblclick
36727 * Fires when a row is double clicked
36728 * @param {Grid} this
36729 * @param {Number} rowIndex
36730 * @param {Roo.EventObject} e
36732 "rowdblclick" : true,
36734 * @event headerclick
36735 * Fires when a header is clicked
36736 * @param {Grid} this
36737 * @param {Number} columnIndex
36738 * @param {Roo.EventObject} e
36740 "headerclick" : true,
36742 * @event headerdblclick
36743 * Fires when a header cell is double clicked
36744 * @param {Grid} this
36745 * @param {Number} columnIndex
36746 * @param {Roo.EventObject} e
36748 "headerdblclick" : true,
36750 * @event rowcontextmenu
36751 * Fires when a row is right clicked
36752 * @param {Grid} this
36753 * @param {Number} rowIndex
36754 * @param {Roo.EventObject} e
36756 "rowcontextmenu" : true,
36758 * @event cellcontextmenu
36759 * Fires when a cell is right clicked
36760 * @param {Grid} this
36761 * @param {Number} rowIndex
36762 * @param {Number} cellIndex
36763 * @param {Roo.EventObject} e
36765 "cellcontextmenu" : true,
36767 * @event headercontextmenu
36768 * Fires when a header is right clicked
36769 * @param {Grid} this
36770 * @param {Number} columnIndex
36771 * @param {Roo.EventObject} e
36773 "headercontextmenu" : true,
36775 * @event bodyscroll
36776 * Fires when the body element is scrolled
36777 * @param {Number} scrollLeft
36778 * @param {Number} scrollTop
36780 "bodyscroll" : true,
36782 * @event columnresize
36783 * Fires when the user resizes a column
36784 * @param {Number} columnIndex
36785 * @param {Number} newSize
36787 "columnresize" : true,
36789 * @event columnmove
36790 * Fires when the user moves a column
36791 * @param {Number} oldIndex
36792 * @param {Number} newIndex
36794 "columnmove" : true,
36797 * Fires when row(s) start being dragged
36798 * @param {Grid} this
36799 * @param {Roo.GridDD} dd The drag drop object
36800 * @param {event} e The raw browser event
36802 "startdrag" : true,
36805 * Fires when a drag operation is complete
36806 * @param {Grid} this
36807 * @param {Roo.GridDD} dd The drag drop object
36808 * @param {event} e The raw browser event
36813 * Fires when dragged row(s) are dropped on a valid DD target
36814 * @param {Grid} this
36815 * @param {Roo.GridDD} dd The drag drop object
36816 * @param {String} targetId The target drag drop object
36817 * @param {event} e The raw browser event
36822 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36823 * @param {Grid} this
36824 * @param {Roo.GridDD} dd The drag drop object
36825 * @param {String} targetId The target drag drop object
36826 * @param {event} e The raw browser event
36831 * Fires when the dragged row(s) first cross another DD target while being dragged
36832 * @param {Grid} this
36833 * @param {Roo.GridDD} dd The drag drop object
36834 * @param {String} targetId The target drag drop object
36835 * @param {event} e The raw browser event
36837 "dragenter" : true,
36840 * Fires when the dragged row(s) leave another DD target while being dragged
36841 * @param {Grid} this
36842 * @param {Roo.GridDD} dd The drag drop object
36843 * @param {String} targetId The target drag drop object
36844 * @param {event} e The raw browser event
36849 * Fires when a row is rendered, so you can change add a style to it.
36850 * @param {GridView} gridview The grid view
36851 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
36857 * Fires when the grid is rendered
36858 * @param {Grid} grid
36863 * Fires when a date is selected
36864 * @param {DatePicker} this
36865 * @param {Date} date The selected date
36869 * @event monthchange
36870 * Fires when the displayed month changes
36871 * @param {DatePicker} this
36872 * @param {Date} date The selected month
36874 'monthchange': true,
36876 * @event evententer
36877 * Fires when mouse over an event
36878 * @param {Calendar} this
36879 * @param {event} Event
36881 'evententer': true,
36883 * @event eventleave
36884 * Fires when the mouse leaves an
36885 * @param {Calendar} this
36888 'eventleave': true,
36890 * @event eventclick
36891 * Fires when the mouse click an
36892 * @param {Calendar} this
36895 'eventclick': true,
36897 * @event eventrender
36898 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
36899 * @param {Calendar} this
36900 * @param {data} data to be modified
36902 'eventrender': true
36906 Roo.grid.Grid.superclass.constructor.call(this);
36907 this.on('render', function() {
36908 this.view.el.addClass('x-grid-cal');
36910 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
36914 if (!Roo.grid.Calendar.style) {
36915 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
36918 '.x-grid-cal .x-grid-col' : {
36919 height: 'auto !important',
36920 'vertical-align': 'top'
36922 '.x-grid-cal .fc-event-hori' : {
36933 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
36935 * @cfg {Store} eventStore The store that loads events.
36940 activeDate : false,
36943 monitorWindowResize : false,
36946 resizeColumns : function() {
36947 var col = (this.view.el.getWidth() / 7) - 3;
36948 // loop through cols, and setWidth
36949 for(var i =0 ; i < 7 ; i++){
36950 this.cm.setColumnWidth(i, col);
36953 setDate :function(date) {
36955 Roo.log('setDate?');
36957 this.resizeColumns();
36958 var vd = this.activeDate;
36959 this.activeDate = date;
36960 // if(vd && this.el){
36961 // var t = date.getTime();
36962 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
36963 // Roo.log('using add remove');
36965 // this.fireEvent('monthchange', this, date);
36967 // this.cells.removeClass("fc-state-highlight");
36968 // this.cells.each(function(c){
36969 // if(c.dateValue == t){
36970 // c.addClass("fc-state-highlight");
36971 // setTimeout(function(){
36972 // try{c.dom.firstChild.focus();}catch(e){}
36982 var days = date.getDaysInMonth();
36984 var firstOfMonth = date.getFirstDateOfMonth();
36985 var startingPos = firstOfMonth.getDay()-this.startDay;
36987 if(startingPos < this.startDay){
36991 var pm = date.add(Date.MONTH, -1);
36992 var prevStart = pm.getDaysInMonth()-startingPos;
36996 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
36998 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
36999 //this.cells.addClassOnOver('fc-state-hover');
37001 var cells = this.cells.elements;
37002 var textEls = this.textNodes;
37004 //Roo.each(cells, function(cell){
37005 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37008 days += startingPos;
37010 // convert everything to numbers so it's fast
37011 var day = 86400000;
37012 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37015 //Roo.log(prevStart);
37017 var today = new Date().clearTime().getTime();
37018 var sel = date.clearTime().getTime();
37019 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37020 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37021 var ddMatch = this.disabledDatesRE;
37022 var ddText = this.disabledDatesText;
37023 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37024 var ddaysText = this.disabledDaysText;
37025 var format = this.format;
37027 var setCellClass = function(cal, cell){
37029 //Roo.log('set Cell Class');
37031 var t = d.getTime();
37036 cell.dateValue = t;
37038 cell.className += " fc-today";
37039 cell.className += " fc-state-highlight";
37040 cell.title = cal.todayText;
37043 // disable highlight in other month..
37044 cell.className += " fc-state-highlight";
37049 //cell.className = " fc-state-disabled";
37050 cell.title = cal.minText;
37054 //cell.className = " fc-state-disabled";
37055 cell.title = cal.maxText;
37059 if(ddays.indexOf(d.getDay()) != -1){
37060 // cell.title = ddaysText;
37061 // cell.className = " fc-state-disabled";
37064 if(ddMatch && format){
37065 var fvalue = d.dateFormat(format);
37066 if(ddMatch.test(fvalue)){
37067 cell.title = ddText.replace("%0", fvalue);
37068 cell.className = " fc-state-disabled";
37072 if (!cell.initialClassName) {
37073 cell.initialClassName = cell.dom.className;
37076 cell.dom.className = cell.initialClassName + ' ' + cell.className;
37081 for(; i < startingPos; i++) {
37082 cells[i].dayName = (++prevStart);
37083 Roo.log(textEls[i]);
37084 d.setDate(d.getDate()+1);
37086 //cells[i].className = "fc-past fc-other-month";
37087 setCellClass(this, cells[i]);
37092 for(; i < days; i++){
37093 intDay = i - startingPos + 1;
37094 cells[i].dayName = (intDay);
37095 d.setDate(d.getDate()+1);
37097 cells[i].className = ''; // "x-date-active";
37098 setCellClass(this, cells[i]);
37102 for(; i < 42; i++) {
37103 //textEls[i].innerHTML = (++extraDays);
37105 d.setDate(d.getDate()+1);
37106 cells[i].dayName = (++extraDays);
37107 cells[i].className = "fc-future fc-other-month";
37108 setCellClass(this, cells[i]);
37111 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37113 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37115 // this will cause all the cells to mis
37118 for (var r = 0;r < 6;r++) {
37119 for (var c =0;c < 7;c++) {
37120 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37124 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37125 for(i=0;i<cells.length;i++) {
37127 this.cells.elements[i].dayName = cells[i].dayName ;
37128 this.cells.elements[i].className = cells[i].className;
37129 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37130 this.cells.elements[i].title = cells[i].title ;
37131 this.cells.elements[i].dateValue = cells[i].dateValue ;
37137 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37138 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37140 ////if(totalRows != 6){
37141 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37142 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37145 this.fireEvent('monthchange', this, date);
37150 * Returns the grid's SelectionModel.
37151 * @return {SelectionModel}
37153 getSelectionModel : function(){
37154 if(!this.selModel){
37155 this.selModel = new Roo.grid.CellSelectionModel();
37157 return this.selModel;
37161 this.eventStore.load()
37167 findCell : function(dt) {
37168 dt = dt.clearTime().getTime();
37170 this.cells.each(function(c){
37171 //Roo.log("check " +c.dateValue + '?=' + dt);
37172 if(c.dateValue == dt){
37182 findCells : function(rec) {
37183 var s = rec.data.start_dt.clone().clearTime().getTime();
37185 var e= rec.data.end_dt.clone().clearTime().getTime();
37188 this.cells.each(function(c){
37189 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37191 if(c.dateValue > e){
37194 if(c.dateValue < s){
37203 findBestRow: function(cells)
37207 for (var i =0 ; i < cells.length;i++) {
37208 ret = Math.max(cells[i].rows || 0,ret);
37215 addItem : function(rec)
37217 // look for vertical location slot in
37218 var cells = this.findCells(rec);
37220 rec.row = this.findBestRow(cells);
37222 // work out the location.
37226 for(var i =0; i < cells.length; i++) {
37234 if (crow.start.getY() == cells[i].getY()) {
37236 crow.end = cells[i];
37252 for (var i = 0; i < cells.length;i++) {
37253 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37260 clearEvents: function() {
37262 if (!this.eventStore.getCount()) {
37265 // reset number of rows in cells.
37266 Roo.each(this.cells.elements, function(c){
37270 this.eventStore.each(function(e) {
37271 this.clearEvent(e);
37276 clearEvent : function(ev)
37279 Roo.each(ev.els, function(el) {
37280 el.un('mouseenter' ,this.onEventEnter, this);
37281 el.un('mouseleave' ,this.onEventLeave, this);
37289 renderEvent : function(ev,ctr) {
37291 ctr = this.view.el.select('.fc-event-container',true).first();
37295 this.clearEvent(ev);
37301 var cells = ev.cells;
37302 var rows = ev.rows;
37303 this.fireEvent('eventrender', this, ev);
37305 for(var i =0; i < rows.length; i++) {
37309 cls += ' fc-event-start';
37311 if ((i+1) == rows.length) {
37312 cls += ' fc-event-end';
37315 //Roo.log(ev.data);
37316 // how many rows should it span..
37317 var cg = this.eventTmpl.append(ctr,Roo.apply({
37320 }, ev.data) , true);
37323 cg.on('mouseenter' ,this.onEventEnter, this, ev);
37324 cg.on('mouseleave' ,this.onEventLeave, this, ev);
37325 cg.on('click', this.onEventClick, this, ev);
37329 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37330 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37333 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
37334 cg.setWidth(ebox.right - sbox.x -2);
37338 renderEvents: function()
37340 // first make sure there is enough space..
37342 if (!this.eventTmpl) {
37343 this.eventTmpl = new Roo.Template(
37344 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
37345 '<div class="fc-event-inner">' +
37346 '<span class="fc-event-time">{time}</span>' +
37347 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37349 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
37357 this.cells.each(function(c) {
37358 //Roo.log(c.select('.fc-day-content div',true).first());
37359 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37362 var ctr = this.view.el.select('.fc-event-container',true).first();
37365 this.eventStore.each(function(ev){
37367 this.renderEvent(ev);
37371 this.view.layout();
37375 onEventEnter: function (e, el,event,d) {
37376 this.fireEvent('evententer', this, el, event);
37379 onEventLeave: function (e, el,event,d) {
37380 this.fireEvent('eventleave', this, el, event);
37383 onEventClick: function (e, el,event,d) {
37384 this.fireEvent('eventclick', this, el, event);
37387 onMonthChange: function () {
37391 onLoad: function () {
37393 //Roo.log('calendar onload');
37395 if(this.eventStore.getCount() > 0){
37399 this.eventStore.each(function(d){
37404 if (typeof(add.end_dt) == 'undefined') {
37405 Roo.log("Missing End time in calendar data: ");
37409 if (typeof(add.start_dt) == 'undefined') {
37410 Roo.log("Missing Start time in calendar data: ");
37414 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37415 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37416 add.id = add.id || d.id;
37417 add.title = add.title || '??';
37425 this.renderEvents();
37435 render : function ()
37439 if (!this.view.el.hasClass('course-timesheet')) {
37440 this.view.el.addClass('course-timesheet');
37442 if (this.tsStyle) {
37447 Roo.log(_this.grid.view.el.getWidth());
37450 this.tsStyle = Roo.util.CSS.createStyleSheet({
37451 '.course-timesheet .x-grid-row' : {
37454 '.x-grid-row td' : {
37455 'vertical-align' : 0
37457 '.course-edit-link' : {
37459 'text-overflow' : 'ellipsis',
37460 'overflow' : 'hidden',
37461 'white-space' : 'nowrap',
37462 'cursor' : 'pointer'
37467 '.de-act-sup-link' : {
37468 'color' : 'purple',
37469 'text-decoration' : 'line-through'
37473 'text-decoration' : 'line-through'
37475 '.course-timesheet .course-highlight' : {
37476 'border-top-style': 'dashed !important',
37477 'border-bottom-bottom': 'dashed !important'
37479 '.course-timesheet .course-item' : {
37480 'font-family' : 'tahoma, arial, helvetica',
37481 'font-size' : '11px',
37482 'overflow' : 'hidden',
37483 'padding-left' : '10px',
37484 'padding-right' : '10px',
37485 'padding-top' : '10px'
37493 monitorWindowResize : false,
37494 cellrenderer : function(v,x,r)
37499 xtype: 'CellSelectionModel',
37506 beforeload : function (_self, options)
37508 options.params = options.params || {};
37509 options.params._month = _this.monthField.getValue();
37510 options.params.limit = 9999;
37511 options.params['sort'] = 'when_dt';
37512 options.params['dir'] = 'ASC';
37513 this.proxy.loadResponse = this.loadResponse;
37515 //this.addColumns();
37517 load : function (_self, records, options)
37519 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37520 // if you click on the translation.. you can edit it...
37521 var el = Roo.get(this);
37522 var id = el.dom.getAttribute('data-id');
37523 var d = el.dom.getAttribute('data-date');
37524 var t = el.dom.getAttribute('data-time');
37525 //var id = this.child('span').dom.textContent;
37528 Pman.Dialog.CourseCalendar.show({
37532 productitem_active : id ? 1 : 0
37534 _this.grid.ds.load({});
37539 _this.panel.fireEvent('resize', [ '', '' ]);
37542 loadResponse : function(o, success, response){
37543 // this is overridden on before load..
37545 Roo.log("our code?");
37546 //Roo.log(success);
37547 //Roo.log(response)
37548 delete this.activeRequest;
37550 this.fireEvent("loadexception", this, o, response);
37551 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37556 result = o.reader.read(response);
37558 Roo.log("load exception?");
37559 this.fireEvent("loadexception", this, o, response, e);
37560 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37563 Roo.log("ready...");
37564 // loop through result.records;
37565 // and set this.tdate[date] = [] << array of records..
37567 Roo.each(result.records, function(r){
37569 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
37570 _this.tdata[r.data.when_dt.format('j')] = [];
37572 _this.tdata[r.data.when_dt.format('j')].push(r.data);
37575 //Roo.log(_this.tdata);
37577 result.records = [];
37578 result.totalRecords = 6;
37580 // let's generate some duumy records for the rows.
37581 //var st = _this.dateField.getValue();
37583 // work out monday..
37584 //st = st.add(Date.DAY, -1 * st.format('w'));
37586 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37588 var firstOfMonth = date.getFirstDayOfMonth();
37589 var days = date.getDaysInMonth();
37591 var firstAdded = false;
37592 for (var i = 0; i < result.totalRecords ; i++) {
37593 //var d= st.add(Date.DAY, i);
37596 for(var w = 0 ; w < 7 ; w++){
37597 if(!firstAdded && firstOfMonth != w){
37604 var dd = (d > 0 && d < 10) ? "0"+d : d;
37605 row['weekday'+w] = String.format(
37606 '<span style="font-size: 16px;"><b>{0}</b></span>'+
37607 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
37609 date.format('Y-m-')+dd
37612 if(typeof(_this.tdata[d]) != 'undefined'){
37613 Roo.each(_this.tdata[d], function(r){
37617 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
37618 if(r.parent_id*1>0){
37619 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
37622 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
37623 deactive = 'de-act-link';
37626 row['weekday'+w] += String.format(
37627 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
37629 r.product_id_name, //1
37630 r.when_dt.format('h:ia'), //2
37640 // only do this if something added..
37642 result.records.push(_this.grid.dataSource.reader.newRow(row));
37646 // push it twice. (second one with an hour..
37650 this.fireEvent("load", this, o, o.request.arg);
37651 o.request.callback.call(o.request.scope, result, o.request.arg, true);
37653 sortInfo : {field: 'when_dt', direction : 'ASC' },
37655 xtype: 'HttpProxy',
37658 url : baseURL + '/Roo/Shop_course.php'
37661 xtype: 'JsonReader',
37678 'name': 'parent_id',
37682 'name': 'product_id',
37686 'name': 'productitem_id',
37704 click : function (_self, e)
37706 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37707 sd.setMonth(sd.getMonth()-1);
37708 _this.monthField.setValue(sd.format('Y-m-d'));
37709 _this.grid.ds.load({});
37715 xtype: 'Separator',
37719 xtype: 'MonthField',
37722 render : function (_self)
37724 _this.monthField = _self;
37725 // _this.monthField.set today
37727 select : function (combo, date)
37729 _this.grid.ds.load({});
37732 value : (function() { return new Date(); })()
37735 xtype: 'Separator',
37741 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
37751 click : function (_self, e)
37753 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37754 sd.setMonth(sd.getMonth()+1);
37755 _this.monthField.setValue(sd.format('Y-m-d'));
37756 _this.grid.ds.load({});
37769 * Ext JS Library 1.1.1
37770 * Copyright(c) 2006-2007, Ext JS, LLC.
37772 * Originally Released Under LGPL - original licence link has changed is not relivant.
37775 * <script type="text/javascript">
37779 * @class Roo.LoadMask
37780 * A simple utility class for generically masking elements while loading data. If the element being masked has
37781 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37782 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
37783 * element's UpdateManager load indicator and will be destroyed after the initial load.
37785 * Create a new LoadMask
37786 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37787 * @param {Object} config The config object
37789 Roo.LoadMask = function(el, config){
37790 this.el = Roo.get(el);
37791 Roo.apply(this, config);
37793 this.store.on('beforeload', this.onBeforeLoad, this);
37794 this.store.on('load', this.onLoad, this);
37795 this.store.on('loadexception', this.onLoadException, this);
37796 this.removeMask = false;
37798 var um = this.el.getUpdateManager();
37799 um.showLoadIndicator = false; // disable the default indicator
37800 um.on('beforeupdate', this.onBeforeLoad, this);
37801 um.on('update', this.onLoad, this);
37802 um.on('failure', this.onLoad, this);
37803 this.removeMask = true;
37807 Roo.LoadMask.prototype = {
37809 * @cfg {Boolean} removeMask
37810 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37811 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
37814 * @cfg {String} msg
37815 * The text to display in a centered loading message box (defaults to 'Loading...')
37817 msg : 'Loading...',
37819 * @cfg {String} msgCls
37820 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37822 msgCls : 'x-mask-loading',
37825 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37831 * Disables the mask to prevent it from being displayed
37833 disable : function(){
37834 this.disabled = true;
37838 * Enables the mask so that it can be displayed
37840 enable : function(){
37841 this.disabled = false;
37844 onLoadException : function()
37846 Roo.log(arguments);
37848 if (typeof(arguments[3]) != 'undefined') {
37849 Roo.MessageBox.alert("Error loading",arguments[3]);
37853 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37854 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37861 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37864 onLoad : function()
37866 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37870 onBeforeLoad : function(){
37871 if(!this.disabled){
37872 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
37877 destroy : function(){
37879 this.store.un('beforeload', this.onBeforeLoad, this);
37880 this.store.un('load', this.onLoad, this);
37881 this.store.un('loadexception', this.onLoadException, this);
37883 var um = this.el.getUpdateManager();
37884 um.un('beforeupdate', this.onBeforeLoad, this);
37885 um.un('update', this.onLoad, this);
37886 um.un('failure', this.onLoad, this);
37891 * Ext JS Library 1.1.1
37892 * Copyright(c) 2006-2007, Ext JS, LLC.
37894 * Originally Released Under LGPL - original licence link has changed is not relivant.
37897 * <script type="text/javascript">
37902 * @class Roo.XTemplate
37903 * @extends Roo.Template
37904 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
37906 var t = new Roo.XTemplate(
37907 '<select name="{name}">',
37908 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
37912 // then append, applying the master template values
37915 * Supported features:
37920 {a_variable} - output encoded.
37921 {a_variable.format:("Y-m-d")} - call a method on the variable
37922 {a_variable:raw} - unencoded output
37923 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
37924 {a_variable:this.method_on_template(...)} - call a method on the template object.
37929 <tpl for="a_variable or condition.."></tpl>
37930 <tpl if="a_variable or condition"></tpl>
37931 <tpl exec="some javascript"></tpl>
37932 <tpl name="named_template"></tpl> (experimental)
37934 <tpl for="."></tpl> - just iterate the property..
37935 <tpl for=".."></tpl> - iterates with the parent (probably the template)
37939 Roo.XTemplate = function()
37941 Roo.XTemplate.superclass.constructor.apply(this, arguments);
37948 Roo.extend(Roo.XTemplate, Roo.Template, {
37951 * The various sub templates
37956 * basic tag replacing syntax
37959 * // you can fake an object call by doing this
37963 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
37966 * compile the template
37968 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
37971 compile: function()
37975 s = ['<tpl>', s, '</tpl>'].join('');
37977 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
37978 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
37979 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
37980 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
37981 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
37986 while(true == !!(m = s.match(re))){
37987 var forMatch = m[0].match(nameRe),
37988 ifMatch = m[0].match(ifRe),
37989 execMatch = m[0].match(execRe),
37990 namedMatch = m[0].match(namedRe),
37995 name = forMatch && forMatch[1] ? forMatch[1] : '';
37998 // if - puts fn into test..
37999 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38001 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38006 // exec - calls a function... returns empty if true is returned.
38007 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38009 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38017 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38018 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38019 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38022 var uid = namedMatch ? namedMatch[1] : id;
38026 id: namedMatch ? namedMatch[1] : id,
38033 s = s.replace(m[0], '');
38035 s = s.replace(m[0], '{xtpl'+ id + '}');
38040 for(var i = tpls.length-1; i >= 0; --i){
38041 this.compileTpl(tpls[i]);
38042 this.tpls[tpls[i].id] = tpls[i];
38044 this.master = tpls[tpls.length-1];
38048 * same as applyTemplate, except it's done to one of the subTemplates
38049 * when using named templates, you can do:
38051 * var str = pl.applySubTemplate('your-name', values);
38054 * @param {Number} id of the template
38055 * @param {Object} values to apply to template
38056 * @param {Object} parent (normaly the instance of this object)
38058 applySubTemplate : function(id, values, parent)
38062 var t = this.tpls[id];
38066 if(t.test && !t.test.call(this, values, parent)){
38070 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38071 Roo.log(e.toString());
38077 if(t.exec && t.exec.call(this, values, parent)){
38081 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38082 Roo.log(e.toString());
38087 var vs = t.target ? t.target.call(this, values, parent) : values;
38088 parent = t.target ? values : parent;
38089 if(t.target && vs instanceof Array){
38091 for(var i = 0, len = vs.length; i < len; i++){
38092 buf[buf.length] = t.compiled.call(this, vs[i], parent);
38094 return buf.join('');
38096 return t.compiled.call(this, vs, parent);
38098 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38099 Roo.log(e.toString());
38100 Roo.log(t.compiled);
38105 compileTpl : function(tpl)
38107 var fm = Roo.util.Format;
38108 var useF = this.disableFormats !== true;
38109 var sep = Roo.isGecko ? "+" : ",";
38110 var undef = function(str) {
38111 Roo.log("Property not found :" + str);
38115 var fn = function(m, name, format, args)
38117 //Roo.log(arguments);
38118 args = args ? args.replace(/\\'/g,"'") : args;
38119 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38120 if (typeof(format) == 'undefined') {
38121 format= 'htmlEncode';
38123 if (format == 'raw' ) {
38127 if(name.substr(0, 4) == 'xtpl'){
38128 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38131 // build an array of options to determine if value is undefined..
38133 // basically get 'xxxx.yyyy' then do
38134 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38135 // (function () { Roo.log("Property not found"); return ''; })() :
38140 Roo.each(name.split('.'), function(st) {
38141 lookfor += (lookfor.length ? '.': '') + st;
38142 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
38145 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38148 if(format && useF){
38150 args = args ? ',' + args : "";
38152 if(format.substr(0, 5) != "this."){
38153 format = "fm." + format + '(';
38155 format = 'this.call("'+ format.substr(5) + '", ';
38159 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
38163 // called with xxyx.yuu:(test,test)
38165 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
38167 // raw.. - :raw modifier..
38168 return "'"+ sep + udef_st + name + ")"+sep+"'";
38172 // branched to use + in gecko and [].join() in others
38174 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
38175 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38178 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
38179 body.push(tpl.body.replace(/(\r\n|\n)/g,
38180 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38181 body.push("'].join('');};};");
38182 body = body.join('');
38185 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38187 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
38193 applyTemplate : function(values){
38194 return this.master.compiled.call(this, values, {});
38195 //var s = this.subs;
38198 apply : function(){
38199 return this.applyTemplate.apply(this, arguments);
38204 Roo.XTemplate.from = function(el){
38205 el = Roo.getDom(el);
38206 return new Roo.XTemplate(el.value || el.innerHTML);