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 [required] 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.DataReader} reader [required] 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);
571 if(this.pruneModifiedRecords){
572 this.modified.remove(record);
574 this.fireEvent("remove", this, record, index);
578 * Remove all Records from the Store and fires the clear event.
580 removeAll : function(){
582 if(this.pruneModifiedRecords){
585 this.fireEvent("clear", this);
589 * Inserts Records to the Store at the given index and fires the add event.
590 * @param {Number} index The start index at which to insert the passed Records.
591 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
593 insert : function(index, records){
594 records = [].concat(records);
595 for(var i = 0, len = records.length; i < len; i++){
596 this.data.insert(index, records[i]);
597 records[i].join(this);
599 this.fireEvent("add", this, records, index);
603 * Get the index within the cache of the passed Record.
604 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605 * @return {Number} The index of the passed Record. Returns -1 if not found.
607 indexOf : function(record){
608 return this.data.indexOf(record);
612 * Get the index within the cache of the Record with the passed id.
613 * @param {String} id The id of the Record to find.
614 * @return {Number} The index of the Record. Returns -1 if not found.
616 indexOfId : function(id){
617 return this.data.indexOfKey(id);
621 * Get the Record with the specified id.
622 * @param {String} id The id of the Record to find.
623 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
625 getById : function(id){
626 return this.data.key(id);
630 * Get the Record at the specified index.
631 * @param {Number} index The index of the Record to find.
632 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
634 getAt : function(index){
635 return this.data.itemAt(index);
639 * Returns a range of Records between specified indices.
640 * @param {Number} startIndex (optional) The starting index (defaults to 0)
641 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642 * @return {Roo.data.Record[]} An array of Records
644 getRange : function(start, end){
645 return this.data.getRange(start, end);
649 storeOptions : function(o){
650 o = Roo.apply({}, o);
653 this.lastOptions = o;
657 * Loads the Record cache from the configured Proxy using the configured Reader.
659 * If using remote paging, then the first load call must specify the <em>start</em>
660 * and <em>limit</em> properties in the options.params property to establish the initial
661 * position within the dataset, and the number of Records to cache on each read from the Proxy.
663 * <strong>It is important to note that for remote data sources, loading is asynchronous,
664 * and this call will return before the new data has been loaded. Perform any post-processing
665 * in a callback function, or in a "load" event handler.</strong>
667 * @param {Object} options An object containing properties which control loading options:<ul>
668 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670 * passed the following arguments:<ul>
671 * <li>r : Roo.data.Record[]</li>
672 * <li>options: Options object from the load call</li>
673 * <li>success: Boolean success indicator</li></ul></li>
674 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
678 load : function(options){
679 options = options || {};
680 if(this.fireEvent("beforeload", this, options) !== false){
681 this.storeOptions(options);
682 var p = Roo.apply(options.params || {}, this.baseParams);
683 // if meta was not loaded from remote source.. try requesting it.
684 if (!this.reader.metaFromRemote) {
687 if(this.sortInfo && this.remoteSort){
688 var pn = this.paramNames;
689 p[pn["sort"]] = this.sortInfo.field;
690 p[pn["dir"]] = this.sortInfo.direction;
692 if (this.multiSort) {
693 var pn = this.paramNames;
694 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
697 this.proxy.load(p, this.reader, this.loadRecords, this, options);
702 * Reloads the Record cache from the configured Proxy using the configured Reader and
703 * the options from the last load operation performed.
704 * @param {Object} options (optional) An object containing properties which may override the options
705 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706 * the most recently used options are reused).
708 reload : function(options){
709 this.load(Roo.applyIf(options||{}, this.lastOptions));
713 // Called as a callback by the Reader during a load operation.
714 loadRecords : function(o, options, success){
717 if(success !== false){
718 this.fireEvent("load", this, [], options, o);
720 if(options.callback){
721 options.callback.call(options.scope || this, [], options, false);
725 // if data returned failure - throw an exception.
726 if (o.success === false) {
727 // show a message if no listener is registered.
728 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
729 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
731 // loadmask wil be hooked into this..
732 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
735 var r = o.records, t = o.totalRecords || r.length;
737 this.fireEvent("beforeloadadd", this, r, options, o);
739 if(!options || options.add !== true){
740 if(this.pruneModifiedRecords){
743 for(var i = 0, len = r.length; i < len; i++){
747 this.data = this.snapshot;
748 delete this.snapshot;
752 this.totalLength = t;
754 this.fireEvent("datachanged", this);
756 this.totalLength = Math.max(t, this.data.length+r.length);
760 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
762 var e = new Roo.data.Record({});
764 e.set(this.parent.displayField, this.parent.emptyTitle);
765 e.set(this.parent.valueField, '');
770 this.fireEvent("load", this, r, options, o);
771 if(options.callback){
772 options.callback.call(options.scope || this, r, options, true);
778 * Loads data from a passed data block. A Reader which understands the format of the data
779 * must have been configured in the constructor.
780 * @param {Object} data The data block from which to read the Records. The format of the data expected
781 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
782 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
784 loadData : function(o, append){
785 var r = this.reader.readRecords(o);
786 this.loadRecords(r, {add: append}, true);
790 * using 'cn' the nested child reader read the child array into it's child stores.
791 * @param {Object} rec The record with a 'children array
793 loadDataFromChildren : function(rec)
795 this.loadData(this.reader.toLoadData(rec));
800 * Gets the number of cached records.
802 * <em>If using paging, this may not be the total size of the dataset. If the data object
803 * used by the Reader contains the dataset size, then the getTotalCount() function returns
804 * the data set size</em>
806 getCount : function(){
807 return this.data.length || 0;
811 * Gets the total number of records in the dataset as returned by the server.
813 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
814 * the dataset size</em>
816 getTotalCount : function(){
817 return this.totalLength || 0;
821 * Returns the sort state of the Store as an object with two properties:
823 field {String} The name of the field by which the Records are sorted
824 direction {String} The sort order, "ASC" or "DESC"
827 getSortState : function(){
828 return this.sortInfo;
832 applySort : function(){
833 if(this.sortInfo && !this.remoteSort){
834 var s = this.sortInfo, f = s.field;
835 var st = this.fields.get(f).sortType;
836 var fn = function(r1, r2){
837 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
838 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
840 this.data.sort(s.direction, fn);
841 if(this.snapshot && this.snapshot != this.data){
842 this.snapshot.sort(s.direction, fn);
848 * Sets the default sort column and order to be used by the next load operation.
849 * @param {String} fieldName The name of the field to sort by.
850 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
852 setDefaultSort : function(field, dir){
853 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
858 * If remote sorting is used, the sort is performed on the server, and the cache is
859 * reloaded. If local sorting is used, the cache is sorted internally.
860 * @param {String} fieldName The name of the field to sort by.
861 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
863 sort : function(fieldName, dir){
864 var f = this.fields.get(fieldName);
866 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
868 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
869 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
874 this.sortToggle[f.name] = dir;
875 this.sortInfo = {field: f.name, direction: dir};
876 if(!this.remoteSort){
878 this.fireEvent("datachanged", this);
880 this.load(this.lastOptions);
885 * Calls the specified function for each of the Records in the cache.
886 * @param {Function} fn The function to call. The Record is passed as the first parameter.
887 * Returning <em>false</em> aborts and exits the iteration.
888 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
890 each : function(fn, scope){
891 this.data.each(fn, scope);
895 * Gets all records modified since the last commit. Modified records are persisted across load operations
896 * (e.g., during paging).
897 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
899 getModifiedRecords : function(){
900 return this.modified;
904 createFilterFn : function(property, value, anyMatch){
905 if(!value.exec){ // not a regex
906 value = String(value);
907 if(value.length == 0){
910 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
913 return value.test(r.data[property]);
918 * Sums the value of <i>property</i> for each record between start and end and returns the result.
919 * @param {String} property A field on your records
920 * @param {Number} start The record index to start at (defaults to 0)
921 * @param {Number} end The last record index to include (defaults to length - 1)
922 * @return {Number} The sum
924 sum : function(property, start, end){
925 var rs = this.data.items, v = 0;
927 end = (end || end === 0) ? end : rs.length-1;
929 for(var i = start; i <= end; i++){
930 v += (rs[i].data[property] || 0);
936 * Filter the records by a specified property.
937 * @param {String} field A field on your records
938 * @param {String/RegExp} value Either a string that the field
939 * should start with or a RegExp to test against the field
940 * @param {Boolean} anyMatch True to match any part not just the beginning
942 filter : function(property, value, anyMatch){
943 var fn = this.createFilterFn(property, value, anyMatch);
944 return fn ? this.filterBy(fn) : this.clearFilter();
948 * Filter by a function. The specified function will be called with each
949 * record in this data source. If the function returns true the record is included,
950 * otherwise it is filtered.
951 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
952 * @param {Object} scope (optional) The scope of the function (defaults to this)
954 filterBy : function(fn, scope){
955 this.snapshot = this.snapshot || this.data;
956 this.data = this.queryBy(fn, scope||this);
957 this.fireEvent("datachanged", this);
961 * Query the records by a specified property.
962 * @param {String} field A field on your records
963 * @param {String/RegExp} value Either a string that the field
964 * should start with or a RegExp to test against the field
965 * @param {Boolean} anyMatch True to match any part not just the beginning
966 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
968 query : function(property, value, anyMatch){
969 var fn = this.createFilterFn(property, value, anyMatch);
970 return fn ? this.queryBy(fn) : this.data.clone();
974 * Query by a function. The specified function will be called with each
975 * record in this data source. If the function returns true the record is included
977 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
978 * @param {Object} scope (optional) The scope of the function (defaults to this)
979 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
981 queryBy : function(fn, scope){
982 var data = this.snapshot || this.data;
983 return data.filterBy(fn, scope||this);
987 * Collects unique values for a particular dataIndex from this store.
988 * @param {String} dataIndex The property to collect
989 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
990 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
991 * @return {Array} An array of the unique values
993 collect : function(dataIndex, allowNull, bypassFilter){
994 var d = (bypassFilter === true && this.snapshot) ?
995 this.snapshot.items : this.data.items;
996 var v, sv, r = [], l = {};
997 for(var i = 0, len = d.length; i < len; i++){
998 v = d[i].data[dataIndex];
1000 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1009 * Revert to a view of the Record cache with no filtering applied.
1010 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1012 clearFilter : function(suppressEvent){
1013 if(this.snapshot && this.snapshot != this.data){
1014 this.data = this.snapshot;
1015 delete this.snapshot;
1016 if(suppressEvent !== true){
1017 this.fireEvent("datachanged", this);
1023 afterEdit : function(record){
1024 if(this.modified.indexOf(record) == -1){
1025 this.modified.push(record);
1027 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1031 afterReject : function(record){
1032 this.modified.remove(record);
1033 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1037 afterCommit : function(record){
1038 this.modified.remove(record);
1039 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1043 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1044 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1046 commitChanges : function(){
1047 var m = this.modified.slice(0);
1049 for(var i = 0, len = m.length; i < len; i++){
1055 * Cancel outstanding changes on all changed records.
1057 rejectChanges : function(){
1058 var m = this.modified.slice(0);
1060 for(var i = 0, len = m.length; i < len; i++){
1065 onMetaChange : function(meta, rtype, o){
1066 this.recordType = rtype;
1067 this.fields = rtype.prototype.fields;
1068 delete this.snapshot;
1069 this.sortInfo = meta.sortInfo || this.sortInfo;
1071 this.fireEvent('metachange', this, this.reader.meta);
1074 moveIndex : function(data, type)
1076 var index = this.indexOf(data);
1078 var newIndex = index + type;
1082 this.insert(newIndex, data);
1087 * Ext JS Library 1.1.1
1088 * Copyright(c) 2006-2007, Ext JS, LLC.
1090 * Originally Released Under LGPL - original licence link has changed is not relivant.
1093 * <script type="text/javascript">
1097 * @class Roo.data.SimpleStore
1098 * @extends Roo.data.Store
1099 * Small helper class to make creating Stores from Array data easier.
1100 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1101 * @cfg {Array} fields An array of field definition objects, or field name strings.
1102 * @cfg {Object} an existing reader (eg. copied from another store)
1103 * @cfg {Array} data The multi-dimensional array of data
1104 * @cfg {Roo.data.DataProxy} proxy [not-required]
1105 * @cfg {Roo.data.Reader} reader [not-required]
1107 * @param {Object} config
1109 Roo.data.SimpleStore = function(config)
1111 Roo.data.SimpleStore.superclass.constructor.call(this, {
1113 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1116 Roo.data.Record.create(config.fields)
1118 proxy : new Roo.data.MemoryProxy(config.data)
1122 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1124 * Ext JS Library 1.1.1
1125 * Copyright(c) 2006-2007, Ext JS, LLC.
1127 * Originally Released Under LGPL - original licence link has changed is not relivant.
1130 * <script type="text/javascript">
1135 * @extends Roo.data.Store
1136 * @class Roo.data.JsonStore
1137 * Small helper class to make creating Stores for JSON data easier. <br/>
1139 var store = new Roo.data.JsonStore({
1140 url: 'get-images.php',
1142 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1145 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1146 * JsonReader and HttpProxy (unless inline data is provided).</b>
1147 * @cfg {Array} fields An array of field definition objects, or field name strings.
1149 * @param {Object} config
1151 Roo.data.JsonStore = function(c){
1152 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1153 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1154 reader: new Roo.data.JsonReader(c, c.fields)
1157 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1159 * Ext JS Library 1.1.1
1160 * Copyright(c) 2006-2007, Ext JS, LLC.
1162 * Originally Released Under LGPL - original licence link has changed is not relivant.
1165 * <script type="text/javascript">
1169 Roo.data.Field = function(config){
1170 if(typeof config == "string"){
1171 config = {name: config};
1173 Roo.apply(this, config);
1179 var st = Roo.data.SortTypes;
1180 // named sortTypes are supported, here we look them up
1181 if(typeof this.sortType == "string"){
1182 this.sortType = st[this.sortType];
1185 // set default sortType for strings and dates
1189 this.sortType = st.asUCString;
1192 this.sortType = st.asDate;
1195 this.sortType = st.none;
1200 var stripRe = /[\$,%]/g;
1202 // prebuilt conversion function for this field, instead of
1203 // switching every time we're reading a value
1205 var cv, dateFormat = this.dateFormat;
1210 cv = function(v){ return v; };
1213 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1217 return v !== undefined && v !== null && v !== '' ?
1218 parseInt(String(v).replace(stripRe, ""), 10) : '';
1223 return v !== undefined && v !== null && v !== '' ?
1224 parseFloat(String(v).replace(stripRe, ""), 10) : '';
1229 cv = function(v){ return v === true || v === "true" || v == 1; };
1236 if(v instanceof Date){
1240 if(dateFormat == "timestamp"){
1241 return new Date(v*1000);
1243 return Date.parseDate(v, dateFormat);
1245 var parsed = Date.parse(v);
1246 return parsed ? new Date(parsed) : null;
1255 Roo.data.Field.prototype = {
1263 * Ext JS Library 1.1.1
1264 * Copyright(c) 2006-2007, Ext JS, LLC.
1266 * Originally Released Under LGPL - original licence link has changed is not relivant.
1269 * <script type="text/javascript">
1272 // Base class for reading structured data from a data source. This class is intended to be
1273 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1276 * @class Roo.data.DataReader
1278 * Base class for reading structured data from a data source. This class is intended to be
1279 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1282 Roo.data.DataReader = function(meta, recordType){
1286 this.recordType = recordType instanceof Array ?
1287 Roo.data.Record.create(recordType) : recordType;
1290 Roo.data.DataReader.prototype = {
1293 readerType : 'Data',
1295 * Create an empty record
1296 * @param {Object} data (optional) - overlay some values
1297 * @return {Roo.data.Record} record created.
1299 newRow : function(d) {
1301 this.recordType.prototype.fields.each(function(c) {
1303 case 'int' : da[c.name] = 0; break;
1304 case 'date' : da[c.name] = new Date(); break;
1305 case 'float' : da[c.name] = 0.0; break;
1306 case 'boolean' : da[c.name] = false; break;
1307 default : da[c.name] = ""; break;
1311 return new this.recordType(Roo.apply(da, d));
1317 * Ext JS Library 1.1.1
1318 * Copyright(c) 2006-2007, Ext JS, LLC.
1320 * Originally Released Under LGPL - original licence link has changed is not relivant.
1323 * <script type="text/javascript">
1327 * @class Roo.data.DataProxy
1328 * @extends Roo.util.Observable
1330 * This class is an abstract base class for implementations which provide retrieval of
1331 * unformatted data objects.<br>
1333 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1334 * (of the appropriate type which knows how to parse the data object) to provide a block of
1335 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1337 * Custom implementations must implement the load method as described in
1338 * {@link Roo.data.HttpProxy#load}.
1340 Roo.data.DataProxy = function(){
1344 * Fires before a network request is made to retrieve a data object.
1345 * @param {Object} This DataProxy object.
1346 * @param {Object} params The params parameter to the load function.
1351 * Fires before the load method's callback is called.
1352 * @param {Object} This DataProxy object.
1353 * @param {Object} o The data object.
1354 * @param {Object} arg The callback argument object passed to the load function.
1358 * @event loadexception
1359 * Fires if an Exception occurs during data retrieval.
1360 * @param {Object} This DataProxy object.
1361 * @param {Object} o The data object.
1362 * @param {Object} arg The callback argument object passed to the load function.
1363 * @param {Object} e The Exception.
1365 loadexception : true
1367 Roo.data.DataProxy.superclass.constructor.call(this);
1370 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1373 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1377 * Ext JS Library 1.1.1
1378 * Copyright(c) 2006-2007, Ext JS, LLC.
1380 * Originally Released Under LGPL - original licence link has changed is not relivant.
1383 * <script type="text/javascript">
1386 * @class Roo.data.MemoryProxy
1387 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1388 * to the Reader when its load method is called.
1390 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1392 Roo.data.MemoryProxy = function(data){
1396 Roo.data.MemoryProxy.superclass.constructor.call(this);
1400 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1403 * Load data from the requested source (in this case an in-memory
1404 * data object passed to the constructor), read the data object into
1405 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1406 * process that block using the passed callback.
1407 * @param {Object} params This parameter is not used by the MemoryProxy class.
1408 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1409 * object into a block of Roo.data.Records.
1410 * @param {Function} callback The function into which to pass the block of Roo.data.records.
1411 * The function must be passed <ul>
1412 * <li>The Record block object</li>
1413 * <li>The "arg" argument from the load function</li>
1414 * <li>A boolean success indicator</li>
1416 * @param {Object} scope The scope in which to call the callback
1417 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1419 load : function(params, reader, callback, scope, arg){
1420 params = params || {};
1423 result = reader.readRecords(params.data ? params.data :this.data);
1425 this.fireEvent("loadexception", this, arg, null, e);
1426 callback.call(scope, null, arg, false);
1429 callback.call(scope, result, arg, true);
1433 update : function(params, records){
1438 * Ext JS Library 1.1.1
1439 * Copyright(c) 2006-2007, Ext JS, LLC.
1441 * Originally Released Under LGPL - original licence link has changed is not relivant.
1444 * <script type="text/javascript">
1447 * @class Roo.data.HttpProxy
1448 * @extends Roo.data.DataProxy
1449 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1450 * configured to reference a certain URL.<br><br>
1452 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1453 * from which the running page was served.<br><br>
1455 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1457 * Be aware that to enable the browser to parse an XML document, the server must set
1458 * the Content-Type header in the HTTP response to "text/xml".
1460 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1461 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
1462 * will be used to make the request.
1464 Roo.data.HttpProxy = function(conn){
1465 Roo.data.HttpProxy.superclass.constructor.call(this);
1466 // is conn a conn config or a real conn?
1468 this.useAjax = !conn || !conn.events;
1472 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1473 // thse are take from connection...
1476 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1479 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1480 * extra parameters to each request made by this object. (defaults to undefined)
1483 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1484 * to each request made by this object. (defaults to undefined)
1487 * @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)
1490 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1493 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1499 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1503 * Return the {@link Roo.data.Connection} object being used by this Proxy.
1504 * @return {Connection} The Connection object. This object may be used to subscribe to events on
1505 * a finer-grained basis than the DataProxy events.
1507 getConnection : function(){
1508 return this.useAjax ? Roo.Ajax : this.conn;
1512 * Load data from the configured {@link Roo.data.Connection}, read the data object into
1513 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1514 * process that block using the passed callback.
1515 * @param {Object} params An object containing properties which are to be used as HTTP parameters
1516 * for the request to the remote server.
1517 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1518 * object into a block of Roo.data.Records.
1519 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1520 * The function must be passed <ul>
1521 * <li>The Record block object</li>
1522 * <li>The "arg" argument from the load function</li>
1523 * <li>A boolean success indicator</li>
1525 * @param {Object} scope The scope in which to call the callback
1526 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1528 load : function(params, reader, callback, scope, arg){
1529 if(this.fireEvent("beforeload", this, params) !== false){
1531 params : params || {},
1533 callback : callback,
1538 callback : this.loadResponse,
1542 Roo.applyIf(o, this.conn);
1543 if(this.activeRequest){
1544 Roo.Ajax.abort(this.activeRequest);
1546 this.activeRequest = Roo.Ajax.request(o);
1548 this.conn.request(o);
1551 callback.call(scope||this, null, arg, false);
1556 loadResponse : function(o, success, response){
1557 delete this.activeRequest;
1559 this.fireEvent("loadexception", this, o, response);
1560 o.request.callback.call(o.request.scope, null, o.request.arg, false);
1565 result = o.reader.read(response);
1567 this.fireEvent("loadexception", this, o, response, e);
1568 o.request.callback.call(o.request.scope, {
1571 errorMsg : response.responseText
1574 }, o.request.arg, false);
1578 this.fireEvent("load", this, o, o.request.arg);
1579 o.request.callback.call(o.request.scope, result, o.request.arg, true);
1583 update : function(dataSet){
1588 updateResponse : function(dataSet){
1593 * Ext JS Library 1.1.1
1594 * Copyright(c) 2006-2007, Ext JS, LLC.
1596 * Originally Released Under LGPL - original licence link has changed is not relivant.
1599 * <script type="text/javascript">
1603 * @class Roo.data.ScriptTagProxy
1604 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1605 * other than the originating domain of the running page.<br><br>
1607 * <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
1608 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1610 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1611 * source code that is used as the source inside a <script> tag.<br><br>
1613 * In order for the browser to process the returned data, the server must wrap the data object
1614 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1615 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1616 * depending on whether the callback name was passed:
1619 boolean scriptTag = false;
1620 String cb = request.getParameter("callback");
1623 response.setContentType("text/javascript");
1625 response.setContentType("application/x-json");
1627 Writer out = response.getWriter();
1629 out.write(cb + "(");
1631 out.print(dataBlock.toJsonString());
1638 * @param {Object} config A configuration object.
1640 Roo.data.ScriptTagProxy = function(config){
1641 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1642 Roo.apply(this, config);
1643 this.head = document.getElementsByTagName("head")[0];
1646 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1648 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1650 * @cfg {String} url The URL from which to request the data object.
1653 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1657 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1658 * the server the name of the callback function set up by the load call to process the returned data object.
1659 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1660 * javascript output which calls this named function passing the data object as its only parameter.
1662 callbackParam : "callback",
1664 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1665 * name to the request.
1670 * Load data from the configured URL, read the data object into
1671 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1672 * process that block using the passed callback.
1673 * @param {Object} params An object containing properties which are to be used as HTTP parameters
1674 * for the request to the remote server.
1675 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1676 * object into a block of Roo.data.Records.
1677 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1678 * The function must be passed <ul>
1679 * <li>The Record block object</li>
1680 * <li>The "arg" argument from the load function</li>
1681 * <li>A boolean success indicator</li>
1683 * @param {Object} scope The scope in which to call the callback
1684 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1686 load : function(params, reader, callback, scope, arg){
1687 if(this.fireEvent("beforeload", this, params) !== false){
1689 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1692 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1694 url += "&_dc=" + (new Date().getTime());
1696 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1699 cb : "stcCallback"+transId,
1700 scriptId : "stcScript"+transId,
1704 callback : callback,
1710 window[trans.cb] = function(o){
1711 conn.handleResponse(o, trans);
1714 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1716 if(this.autoAbort !== false){
1720 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1722 var script = document.createElement("script");
1723 script.setAttribute("src", url);
1724 script.setAttribute("type", "text/javascript");
1725 script.setAttribute("id", trans.scriptId);
1726 this.head.appendChild(script);
1730 callback.call(scope||this, null, arg, false);
1735 isLoading : function(){
1736 return this.trans ? true : false;
1740 * Abort the current server request.
1743 if(this.isLoading()){
1744 this.destroyTrans(this.trans);
1749 destroyTrans : function(trans, isLoaded){
1750 this.head.removeChild(document.getElementById(trans.scriptId));
1751 clearTimeout(trans.timeoutId);
1753 window[trans.cb] = undefined;
1755 delete window[trans.cb];
1758 // if hasn't been loaded, wait for load to remove it to prevent script error
1759 window[trans.cb] = function(){
1760 window[trans.cb] = undefined;
1762 delete window[trans.cb];
1769 handleResponse : function(o, trans){
1771 this.destroyTrans(trans, true);
1774 result = trans.reader.readRecords(o);
1776 this.fireEvent("loadexception", this, o, trans.arg, e);
1777 trans.callback.call(trans.scope||window, null, trans.arg, false);
1780 this.fireEvent("load", this, o, trans.arg);
1781 trans.callback.call(trans.scope||window, result, trans.arg, true);
1785 handleFailure : function(trans){
1787 this.destroyTrans(trans, false);
1788 this.fireEvent("loadexception", this, null, trans.arg);
1789 trans.callback.call(trans.scope||window, null, trans.arg, false);
1793 * Ext JS Library 1.1.1
1794 * Copyright(c) 2006-2007, Ext JS, LLC.
1796 * Originally Released Under LGPL - original licence link has changed is not relivant.
1799 * <script type="text/javascript">
1803 * @class Roo.data.JsonReader
1804 * @extends Roo.data.DataReader
1805 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1806 * based on mappings in a provided Roo.data.Record constructor.
1808 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1809 * in the reply previously.
1814 var RecordDef = Roo.data.Record.create([
1815 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
1816 {name: 'occupation'} // This field will use "occupation" as the mapping.
1818 var myReader = new Roo.data.JsonReader({
1819 totalProperty: "results", // The property which contains the total dataset size (optional)
1820 root: "rows", // The property which contains an Array of row objects
1821 id: "id" // The property within each row object that provides an ID for the record (optional)
1825 * This would consume a JSON file like this:
1827 { 'results': 2, 'rows': [
1828 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1829 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1832 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1833 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1834 * paged from the remote server.
1835 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1836 * @cfg {String} root name of the property which contains the Array of row objects.
1837 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1838 * @cfg {Array} fields Array of field definition objects
1840 * Create a new JsonReader
1841 * @param {Object} meta Metadata configuration options
1842 * @param {Object} recordType Either an Array of field definition objects,
1843 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1845 Roo.data.JsonReader = function(meta, recordType){
1848 // set some defaults:
1850 totalProperty: 'total',
1851 successProperty : 'success',
1856 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1858 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1860 readerType : 'Json',
1863 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
1864 * Used by Store query builder to append _requestMeta to params.
1867 metaFromRemote : false,
1869 * This method is only used by a DataProxy which has retrieved data from a remote server.
1870 * @param {Object} response The XHR object which contains the JSON data in its responseText.
1871 * @return {Object} data A data block which is used by an Roo.data.Store object as
1872 * a cache of Roo.data.Records.
1874 read : function(response){
1875 var json = response.responseText;
1877 var o = /* eval:var:o */ eval("("+json+")");
1879 throw {message: "JsonReader.read: Json object not found"};
1885 this.metaFromRemote = true;
1886 this.meta = o.metaData;
1887 this.recordType = Roo.data.Record.create(o.metaData.fields);
1888 this.onMetaChange(this.meta, this.recordType, o);
1890 return this.readRecords(o);
1893 // private function a store will implement
1894 onMetaChange : function(meta, recordType, o){
1901 simpleAccess: function(obj, subsc) {
1908 getJsonAccessor: function(){
1910 return function(expr) {
1912 return(re.test(expr))
1913 ? new Function("obj", "return obj." + expr)
1923 * Create a data block containing Roo.data.Records from an XML document.
1924 * @param {Object} o An object which contains an Array of row objects in the property specified
1925 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1926 * which contains the total size of the dataset.
1927 * @return {Object} data A data block which is used by an Roo.data.Store object as
1928 * a cache of Roo.data.Records.
1930 readRecords : function(o){
1932 * After any data loads, the raw JSON data is available for further custom processing.
1936 var s = this.meta, Record = this.recordType,
1937 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1939 // Generate extraction functions for the totalProperty, the root, the id, and for each field
1941 if(s.totalProperty) {
1942 this.getTotal = this.getJsonAccessor(s.totalProperty);
1944 if(s.successProperty) {
1945 this.getSuccess = this.getJsonAccessor(s.successProperty);
1947 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1949 var g = this.getJsonAccessor(s.id);
1950 this.getId = function(rec) {
1952 return (r === undefined || r === "") ? null : r;
1955 this.getId = function(){return null;};
1958 for(var jj = 0; jj < fl; jj++){
1960 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1961 this.ef[jj] = this.getJsonAccessor(map);
1965 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1966 if(s.totalProperty){
1967 var vt = parseInt(this.getTotal(o), 10);
1972 if(s.successProperty){
1973 var vs = this.getSuccess(o);
1974 if(vs === false || vs === 'false'){
1979 for(var i = 0; i < c; i++){
1982 var id = this.getId(n);
1983 for(var j = 0; j < fl; j++){
1985 var v = this.ef[j](n);
1987 Roo.log('missing convert for ' + f.name);
1991 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1993 var record = new Record(values, id);
1995 records[i] = record;
2001 totalRecords : totalRecords
2004 // used when loading children.. @see loadDataFromChildren
2005 toLoadData: function(rec)
2007 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2008 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2009 return { data : data, total : data.length };
2014 * Ext JS Library 1.1.1
2015 * Copyright(c) 2006-2007, Ext JS, LLC.
2017 * Originally Released Under LGPL - original licence link has changed is not relivant.
2020 * <script type="text/javascript">
2024 * @class Roo.data.XmlReader
2025 * @extends Roo.data.DataReader
2026 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2027 * based on mappings in a provided Roo.data.Record constructor.<br><br>
2029 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2030 * header in the HTTP response must be set to "text/xml".</em>
2034 var RecordDef = Roo.data.Record.create([
2035 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
2036 {name: 'occupation'} // This field will use "occupation" as the mapping.
2038 var myReader = new Roo.data.XmlReader({
2039 totalRecords: "results", // The element which contains the total dataset size (optional)
2040 record: "row", // The repeated element which contains row information
2041 id: "id" // The element within the row that provides an ID for the record (optional)
2045 * This would consume an XML file like this:
2049 <results>2</results>
2052 <name>Bill</name>
2053 <occupation>Gardener</occupation>
2057 <name>Ben</name>
2058 <occupation>Horticulturalist</occupation>
2062 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2063 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2064 * paged from the remote server.
2065 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2066 * @cfg {String} success The DomQuery path to the success attribute used by forms.
2067 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2068 * a record identifier value.
2070 * Create a new XmlReader
2071 * @param {Object} meta Metadata configuration options
2072 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
2073 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2074 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
2076 Roo.data.XmlReader = function(meta, recordType){
2078 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2080 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2085 * This method is only used by a DataProxy which has retrieved data from a remote server.
2086 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
2087 * to contain a method called 'responseXML' that returns an XML document object.
2088 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2089 * a cache of Roo.data.Records.
2091 read : function(response){
2092 var doc = response.responseXML;
2094 throw {message: "XmlReader.read: XML Document not available"};
2096 return this.readRecords(doc);
2100 * Create a data block containing Roo.data.Records from an XML document.
2101 * @param {Object} doc A parsed XML document.
2102 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2103 * a cache of Roo.data.Records.
2105 readRecords : function(doc){
2107 * After any data loads/reads, the raw XML Document is available for further custom processing.
2111 var root = doc.documentElement || doc;
2112 var q = Roo.DomQuery;
2113 var recordType = this.recordType, fields = recordType.prototype.fields;
2114 var sid = this.meta.id;
2115 var totalRecords = 0, success = true;
2116 if(this.meta.totalRecords){
2117 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2120 if(this.meta.success){
2121 var sv = q.selectValue(this.meta.success, root, true);
2122 success = sv !== false && sv !== 'false';
2125 var ns = q.select(this.meta.record, root);
2126 for(var i = 0, len = ns.length; i < len; i++) {
2129 var id = sid ? q.selectValue(sid, n) : undefined;
2130 for(var j = 0, jlen = fields.length; j < jlen; j++){
2131 var f = fields.items[j];
2132 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2136 var record = new recordType(values, id);
2138 records[records.length] = record;
2144 totalRecords : totalRecords || records.length
2149 * Ext JS Library 1.1.1
2150 * Copyright(c) 2006-2007, Ext JS, LLC.
2152 * Originally Released Under LGPL - original licence link has changed is not relivant.
2155 * <script type="text/javascript">
2159 * @class Roo.data.ArrayReader
2160 * @extends Roo.data.DataReader
2161 * Data reader class to create an Array of Roo.data.Record objects from an Array.
2162 * Each element of that Array represents a row of data fields. The
2163 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2164 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2168 var RecordDef = Roo.data.Record.create([
2169 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
2170 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
2172 var myReader = new Roo.data.ArrayReader({
2173 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
2177 * This would consume an Array like this:
2179 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2183 * Create a new JsonReader
2184 * @param {Object} meta Metadata configuration options.
2185 * @param {Object|Array} recordType Either an Array of field definition objects
2187 * @cfg {Array} fields Array of field definition objects
2188 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2189 * as specified to {@link Roo.data.Record#create},
2190 * or an {@link Roo.data.Record} object
2193 * created using {@link Roo.data.Record#create}.
2195 Roo.data.ArrayReader = function(meta, recordType)
2197 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2200 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2203 * Create a data block containing Roo.data.Records from an XML document.
2204 * @param {Object} o An Array of row objects which represents the dataset.
2205 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2206 * a cache of Roo.data.Records.
2208 readRecords : function(o)
2210 var sid = this.meta ? this.meta.id : null;
2211 var recordType = this.recordType, fields = recordType.prototype.fields;
2214 for(var i = 0; i < root.length; i++){
2217 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2218 for(var j = 0, jlen = fields.length; j < jlen; j++){
2219 var f = fields.items[j];
2220 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2221 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2225 var record = new recordType(values, id);
2227 records[records.length] = record;
2231 totalRecords : records.length
2234 // used when loading children.. @see loadDataFromChildren
2235 toLoadData: function(rec)
2237 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2238 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2245 * Ext JS Library 1.1.1
2246 * Copyright(c) 2006-2007, Ext JS, LLC.
2248 * Originally Released Under LGPL - original licence link has changed is not relivant.
2251 * <script type="text/javascript">
2256 * @class Roo.data.Tree
2257 * @extends Roo.util.Observable
2258 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2259 * in the tree have most standard DOM functionality.
2261 * @param {Node} root (optional) The root node
2263 Roo.data.Tree = function(root){
2266 * The root node for this tree
2271 this.setRootNode(root);
2276 * Fires when a new child node is appended to a node in this tree.
2277 * @param {Tree} tree The owner tree
2278 * @param {Node} parent The parent node
2279 * @param {Node} node The newly appended node
2280 * @param {Number} index The index of the newly appended node
2285 * Fires when a child node is removed from a node in this tree.
2286 * @param {Tree} tree The owner tree
2287 * @param {Node} parent The parent node
2288 * @param {Node} node The child node removed
2293 * Fires when a node is moved to a new location in the tree
2294 * @param {Tree} tree The owner tree
2295 * @param {Node} node The node moved
2296 * @param {Node} oldParent The old parent of this node
2297 * @param {Node} newParent The new parent of this node
2298 * @param {Number} index The index it was moved to
2303 * Fires when a new child node is inserted in a node in this tree.
2304 * @param {Tree} tree The owner tree
2305 * @param {Node} parent The parent node
2306 * @param {Node} node The child node inserted
2307 * @param {Node} refNode The child node the node was inserted before
2311 * @event beforeappend
2312 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2313 * @param {Tree} tree The owner tree
2314 * @param {Node} parent The parent node
2315 * @param {Node} node The child node to be appended
2317 "beforeappend" : true,
2319 * @event beforeremove
2320 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2321 * @param {Tree} tree The owner tree
2322 * @param {Node} parent The parent node
2323 * @param {Node} node The child node to be removed
2325 "beforeremove" : true,
2328 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2329 * @param {Tree} tree The owner tree
2330 * @param {Node} node The node being moved
2331 * @param {Node} oldParent The parent of the node
2332 * @param {Node} newParent The new parent the node is moving to
2333 * @param {Number} index The index it is being moved to
2335 "beforemove" : true,
2337 * @event beforeinsert
2338 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2339 * @param {Tree} tree The owner tree
2340 * @param {Node} parent The parent node
2341 * @param {Node} node The child node to be inserted
2342 * @param {Node} refNode The child node the node is being inserted before
2344 "beforeinsert" : true
2347 Roo.data.Tree.superclass.constructor.call(this);
2350 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2353 proxyNodeEvent : function(){
2354 return this.fireEvent.apply(this, arguments);
2358 * Returns the root node for this tree.
2361 getRootNode : function(){
2366 * Sets the root node for this tree.
2367 * @param {Node} node
2370 setRootNode : function(node){
2372 node.ownerTree = this;
2374 this.registerNode(node);
2379 * Gets a node in this tree by its id.
2380 * @param {String} id
2383 getNodeById : function(id){
2384 return this.nodeHash[id];
2387 registerNode : function(node){
2388 this.nodeHash[node.id] = node;
2391 unregisterNode : function(node){
2392 delete this.nodeHash[node.id];
2395 toString : function(){
2396 return "[Tree"+(this.id?" "+this.id:"")+"]";
2401 * @class Roo.data.Node
2402 * @extends Roo.util.Observable
2403 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2404 * @cfg {String} id The id for this node. If one is not specified, one is generated.
2406 * @param {Object} attributes The attributes/config for the node
2408 Roo.data.Node = function(attributes){
2410 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2413 this.attributes = attributes || {};
2414 this.leaf = this.attributes.leaf;
2416 * The node id. @type String
2418 this.id = this.attributes.id;
2420 this.id = Roo.id(null, "ynode-");
2421 this.attributes.id = this.id;
2426 * All child nodes of this node. @type Array
2428 this.childNodes = [];
2429 if(!this.childNodes.indexOf){ // indexOf is a must
2430 this.childNodes.indexOf = function(o){
2431 for(var i = 0, len = this.length; i < len; i++){
2440 * The parent node for this node. @type Node
2442 this.parentNode = null;
2444 * The first direct child node of this node, or null if this node has no child nodes. @type Node
2446 this.firstChild = null;
2448 * The last direct child node of this node, or null if this node has no child nodes. @type Node
2450 this.lastChild = null;
2452 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2454 this.previousSibling = null;
2456 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2458 this.nextSibling = null;
2463 * Fires when a new child node is appended
2464 * @param {Tree} tree The owner tree
2465 * @param {Node} this This node
2466 * @param {Node} node The newly appended node
2467 * @param {Number} index The index of the newly appended node
2472 * Fires when a child node is removed
2473 * @param {Tree} tree The owner tree
2474 * @param {Node} this This node
2475 * @param {Node} node The removed node
2480 * Fires when this node is moved to a new location in the tree
2481 * @param {Tree} tree The owner tree
2482 * @param {Node} this This node
2483 * @param {Node} oldParent The old parent of this node
2484 * @param {Node} newParent The new parent of this node
2485 * @param {Number} index The index it was moved to
2490 * Fires when a new child node is inserted.
2491 * @param {Tree} tree The owner tree
2492 * @param {Node} this This node
2493 * @param {Node} node The child node inserted
2494 * @param {Node} refNode The child node the node was inserted before
2498 * @event beforeappend
2499 * Fires before a new child is appended, return false to cancel the append.
2500 * @param {Tree} tree The owner tree
2501 * @param {Node} this This node
2502 * @param {Node} node The child node to be appended
2504 "beforeappend" : true,
2506 * @event beforeremove
2507 * Fires before a child is removed, return false to cancel the remove.
2508 * @param {Tree} tree The owner tree
2509 * @param {Node} this This node
2510 * @param {Node} node The child node to be removed
2512 "beforeremove" : true,
2515 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2516 * @param {Tree} tree The owner tree
2517 * @param {Node} this This node
2518 * @param {Node} oldParent The parent of this node
2519 * @param {Node} newParent The new parent this node is moving to
2520 * @param {Number} index The index it is being moved to
2522 "beforemove" : true,
2524 * @event beforeinsert
2525 * Fires before a new child is inserted, return false to cancel the insert.
2526 * @param {Tree} tree The owner tree
2527 * @param {Node} this This node
2528 * @param {Node} node The child node to be inserted
2529 * @param {Node} refNode The child node the node is being inserted before
2531 "beforeinsert" : true
2533 this.listeners = this.attributes.listeners;
2534 Roo.data.Node.superclass.constructor.call(this);
2537 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2538 fireEvent : function(evtName){
2539 // first do standard event for this node
2540 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2543 // then bubble it up to the tree if the event wasn't cancelled
2544 var ot = this.getOwnerTree();
2546 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2554 * Returns true if this node is a leaf
2557 isLeaf : function(){
2558 return this.leaf === true;
2562 setFirstChild : function(node){
2563 this.firstChild = node;
2567 setLastChild : function(node){
2568 this.lastChild = node;
2573 * Returns true if this node is the last child of its parent
2576 isLast : function(){
2577 return (!this.parentNode ? true : this.parentNode.lastChild == this);
2581 * Returns true if this node is the first child of its parent
2584 isFirst : function(){
2585 return (!this.parentNode ? true : this.parentNode.firstChild == this);
2588 hasChildNodes : function(){
2589 return !this.isLeaf() && this.childNodes.length > 0;
2593 * Insert node(s) as the last child node of this node.
2594 * @param {Node/Array} node The node or Array of nodes to append
2595 * @return {Node} The appended node if single append, or null if an array was passed
2597 appendChild : function(node){
2599 if(node instanceof Array){
2601 }else if(arguments.length > 1){
2605 // if passed an array or multiple args do them one by one
2607 for(var i = 0, len = multi.length; i < len; i++) {
2608 this.appendChild(multi[i]);
2611 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2614 var index = this.childNodes.length;
2615 var oldParent = node.parentNode;
2616 // it's a move, make sure we move it cleanly
2618 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2621 oldParent.removeChild(node);
2624 index = this.childNodes.length;
2626 this.setFirstChild(node);
2628 this.childNodes.push(node);
2629 node.parentNode = this;
2630 var ps = this.childNodes[index-1];
2632 node.previousSibling = ps;
2633 ps.nextSibling = node;
2635 node.previousSibling = null;
2637 node.nextSibling = null;
2638 this.setLastChild(node);
2639 node.setOwnerTree(this.getOwnerTree());
2640 this.fireEvent("append", this.ownerTree, this, node, index);
2641 if(this.ownerTree) {
2642 this.ownerTree.fireEvent("appendnode", this, node, index);
2645 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2652 * Removes a child node from this node.
2653 * @param {Node} node The node to remove
2654 * @return {Node} The removed node
2656 removeChild : function(node){
2657 var index = this.childNodes.indexOf(node);
2661 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2665 // remove it from childNodes collection
2666 this.childNodes.splice(index, 1);
2669 if(node.previousSibling){
2670 node.previousSibling.nextSibling = node.nextSibling;
2672 if(node.nextSibling){
2673 node.nextSibling.previousSibling = node.previousSibling;
2676 // update child refs
2677 if(this.firstChild == node){
2678 this.setFirstChild(node.nextSibling);
2680 if(this.lastChild == node){
2681 this.setLastChild(node.previousSibling);
2684 node.setOwnerTree(null);
2685 // clear any references from the node
2686 node.parentNode = null;
2687 node.previousSibling = null;
2688 node.nextSibling = null;
2689 this.fireEvent("remove", this.ownerTree, this, node);
2694 * Inserts the first node before the second node in this nodes childNodes collection.
2695 * @param {Node} node The node to insert
2696 * @param {Node} refNode The node to insert before (if null the node is appended)
2697 * @return {Node} The inserted node
2699 insertBefore : function(node, refNode){
2700 if(!refNode){ // like standard Dom, refNode can be null for append
2701 return this.appendChild(node);
2704 if(node == refNode){
2708 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2711 var index = this.childNodes.indexOf(refNode);
2712 var oldParent = node.parentNode;
2713 var refIndex = index;
2715 // when moving internally, indexes will change after remove
2716 if(oldParent == this && this.childNodes.indexOf(node) < index){
2720 // it's a move, make sure we move it cleanly
2722 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2725 oldParent.removeChild(node);
2728 this.setFirstChild(node);
2730 this.childNodes.splice(refIndex, 0, node);
2731 node.parentNode = this;
2732 var ps = this.childNodes[refIndex-1];
2734 node.previousSibling = ps;
2735 ps.nextSibling = node;
2737 node.previousSibling = null;
2739 node.nextSibling = refNode;
2740 refNode.previousSibling = node;
2741 node.setOwnerTree(this.getOwnerTree());
2742 this.fireEvent("insert", this.ownerTree, this, node, refNode);
2744 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2750 * Returns the child node at the specified index.
2751 * @param {Number} index
2754 item : function(index){
2755 return this.childNodes[index];
2759 * Replaces one child node in this node with another.
2760 * @param {Node} newChild The replacement node
2761 * @param {Node} oldChild The node to replace
2762 * @return {Node} The replaced node
2764 replaceChild : function(newChild, oldChild){
2765 this.insertBefore(newChild, oldChild);
2766 this.removeChild(oldChild);
2771 * Returns the index of a child node
2772 * @param {Node} node
2773 * @return {Number} The index of the node or -1 if it was not found
2775 indexOf : function(child){
2776 return this.childNodes.indexOf(child);
2780 * Returns the tree this node is in.
2783 getOwnerTree : function(){
2784 // if it doesn't have one, look for one
2785 if(!this.ownerTree){
2789 this.ownerTree = p.ownerTree;
2795 return this.ownerTree;
2799 * Returns depth of this node (the root node has a depth of 0)
2802 getDepth : function(){
2805 while(p.parentNode){
2813 setOwnerTree : function(tree){
2814 // if it's move, we need to update everyone
2815 if(tree != this.ownerTree){
2817 this.ownerTree.unregisterNode(this);
2819 this.ownerTree = tree;
2820 var cs = this.childNodes;
2821 for(var i = 0, len = cs.length; i < len; i++) {
2822 cs[i].setOwnerTree(tree);
2825 tree.registerNode(this);
2831 * Returns the path for this node. The path can be used to expand or select this node programmatically.
2832 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2833 * @return {String} The path
2835 getPath : function(attr){
2836 attr = attr || "id";
2837 var p = this.parentNode;
2838 var b = [this.attributes[attr]];
2840 b.unshift(p.attributes[attr]);
2843 var sep = this.getOwnerTree().pathSeparator;
2844 return sep + b.join(sep);
2848 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2849 * function call will be the scope provided or the current node. The arguments to the function
2850 * will be the args provided or the current node. If the function returns false at any point,
2851 * the bubble is stopped.
2852 * @param {Function} fn The function to call
2853 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2854 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2856 bubble : function(fn, scope, args){
2859 if(fn.call(scope || p, args || p) === false){
2867 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2868 * function call will be the scope provided or the current node. The arguments to the function
2869 * will be the args provided or the current node. If the function returns false at any point,
2870 * the cascade is stopped on that branch.
2871 * @param {Function} fn The function to call
2872 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2873 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2875 cascade : function(fn, scope, args){
2876 if(fn.call(scope || this, args || this) !== false){
2877 var cs = this.childNodes;
2878 for(var i = 0, len = cs.length; i < len; i++) {
2879 cs[i].cascade(fn, scope, args);
2885 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2886 * function call will be the scope provided or the current node. The arguments to the function
2887 * will be the args provided or the current node. If the function returns false at any point,
2888 * the iteration stops.
2889 * @param {Function} fn The function to call
2890 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2891 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2893 eachChild : function(fn, scope, args){
2894 var cs = this.childNodes;
2895 for(var i = 0, len = cs.length; i < len; i++) {
2896 if(fn.call(scope || this, args || cs[i]) === false){
2903 * Finds the first child that has the attribute with the specified value.
2904 * @param {String} attribute The attribute name
2905 * @param {Mixed} value The value to search for
2906 * @return {Node} The found child or null if none was found
2908 findChild : function(attribute, value){
2909 var cs = this.childNodes;
2910 for(var i = 0, len = cs.length; i < len; i++) {
2911 if(cs[i].attributes[attribute] == value){
2919 * Finds the first child by a custom function. The child matches if the function passed
2921 * @param {Function} fn
2922 * @param {Object} scope (optional)
2923 * @return {Node} The found child or null if none was found
2925 findChildBy : function(fn, scope){
2926 var cs = this.childNodes;
2927 for(var i = 0, len = cs.length; i < len; i++) {
2928 if(fn.call(scope||cs[i], cs[i]) === true){
2936 * Sorts this nodes children using the supplied sort function
2937 * @param {Function} fn
2938 * @param {Object} scope (optional)
2940 sort : function(fn, scope){
2941 var cs = this.childNodes;
2942 var len = cs.length;
2944 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2946 for(var i = 0; i < len; i++){
2948 n.previousSibling = cs[i-1];
2949 n.nextSibling = cs[i+1];
2951 this.setFirstChild(n);
2954 this.setLastChild(n);
2961 * Returns true if this node is an ancestor (at any point) of the passed node.
2962 * @param {Node} node
2965 contains : function(node){
2966 return node.isAncestor(this);
2970 * Returns true if the passed node is an ancestor (at any point) of this node.
2971 * @param {Node} node
2974 isAncestor : function(node){
2975 var p = this.parentNode;
2985 toString : function(){
2986 return "[Node"+(this.id?" "+this.id:"")+"]";
2990 * Ext JS Library 1.1.1
2991 * Copyright(c) 2006-2007, Ext JS, LLC.
2993 * Originally Released Under LGPL - original licence link has changed is not relivant.
2996 * <script type="text/javascript">
3002 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
3003 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
3004 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3006 * Create a new Shadow
3007 * @param {Object} config The config object
3009 Roo.Shadow = function(config){
3010 Roo.apply(this, config);
3011 if(typeof this.mode != "string"){
3012 this.mode = this.defaultMode;
3014 var o = this.offset, a = {h: 0};
3015 var rad = Math.floor(this.offset/2);
3016 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3022 a.l -= this.offset + rad;
3023 a.t -= this.offset + rad;
3034 a.l -= (this.offset - rad);
3035 a.t -= this.offset + rad;
3037 a.w -= (this.offset - rad)*2;
3048 a.l -= (this.offset - rad);
3049 a.t -= (this.offset - rad);
3051 a.w -= (this.offset + rad + 1);
3052 a.h -= (this.offset + rad);
3061 Roo.Shadow.prototype = {
3063 * @cfg {String} mode
3064 * The shadow display mode. Supports the following options:<br />
3065 * sides: Shadow displays on both sides and bottom only<br />
3066 * frame: Shadow displays equally on all four sides<br />
3067 * drop: Traditional bottom-right drop shadow (default)
3071 * @cfg {String} offset
3072 * The number of pixels to offset the shadow from the element (defaults to 4)
3077 defaultMode: "drop",
3080 * Displays the shadow under the target element
3081 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3083 show : function(target){
3084 target = Roo.get(target);
3086 this.el = Roo.Shadow.Pool.pull();
3087 if(this.el.dom.nextSibling != target.dom){
3088 this.el.insertBefore(target);
3091 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3093 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3096 target.getLeft(true),
3097 target.getTop(true),
3101 this.el.dom.style.display = "block";
3105 * Returns true if the shadow is visible, else false
3107 isVisible : function(){
3108 return this.el ? true : false;
3112 * Direct alignment when values are already available. Show must be called at least once before
3113 * calling this method to ensure it is initialized.
3114 * @param {Number} left The target element left position
3115 * @param {Number} top The target element top position
3116 * @param {Number} width The target element width
3117 * @param {Number} height The target element height
3119 realign : function(l, t, w, h){
3123 var a = this.adjusts, d = this.el.dom, s = d.style;
3125 s.left = (l+a.l)+"px";
3126 s.top = (t+a.t)+"px";
3127 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3129 if(s.width != sws || s.height != shs){
3133 var cn = d.childNodes;
3134 var sww = Math.max(0, (sw-12))+"px";
3135 cn[0].childNodes[1].style.width = sww;
3136 cn[1].childNodes[1].style.width = sww;
3137 cn[2].childNodes[1].style.width = sww;
3138 cn[1].style.height = Math.max(0, (sh-12))+"px";
3148 this.el.dom.style.display = "none";
3149 Roo.Shadow.Pool.push(this.el);
3155 * Adjust the z-index of this shadow
3156 * @param {Number} zindex The new z-index
3158 setZIndex : function(z){
3161 this.el.setStyle("z-index", z);
3166 // Private utility class that manages the internal Shadow cache
3167 Roo.Shadow.Pool = function(){
3169 var markup = Roo.isIE ?
3170 '<div class="x-ie-shadow"></div>' :
3171 '<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>';
3176 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3177 sh.autoBoxAdjust = false;
3182 push : function(sh){
3188 * Ext JS Library 1.1.1
3189 * Copyright(c) 2006-2007, Ext JS, LLC.
3191 * Originally Released Under LGPL - original licence link has changed is not relivant.
3194 * <script type="text/javascript">
3199 * @class Roo.SplitBar
3200 * @extends Roo.util.Observable
3201 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3205 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3206 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3207 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3208 split.minSize = 100;
3209 split.maxSize = 600;
3210 split.animate = true;
3211 split.on('moved', splitterMoved);
3214 * Create a new SplitBar
3215 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
3216 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
3217 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3218 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
3219 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3220 position of the SplitBar).
3222 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3225 this.el = Roo.get(dragElement, true);
3226 this.el.dom.unselectable = "on";
3228 this.resizingEl = Roo.get(resizingElement, true);
3232 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3233 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3236 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3239 * The minimum size of the resizing element. (Defaults to 0)
3245 * The maximum size of the resizing element. (Defaults to 2000)
3248 this.maxSize = 2000;
3251 * Whether to animate the transition to the new size
3254 this.animate = false;
3257 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3260 this.useShim = false;
3267 this.proxy = Roo.SplitBar.createProxy(this.orientation);
3269 this.proxy = Roo.get(existingProxy).dom;
3272 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3275 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3278 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3281 this.dragSpecs = {};
3284 * @private The adapter to use to positon and resize elements
3286 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3287 this.adapter.init(this);
3289 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3291 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3292 this.el.addClass("x-splitbar-h");
3295 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3296 this.el.addClass("x-splitbar-v");
3302 * Fires when the splitter is moved (alias for {@link #event-moved})
3303 * @param {Roo.SplitBar} this
3304 * @param {Number} newSize the new width or height
3309 * Fires when the splitter is moved
3310 * @param {Roo.SplitBar} this
3311 * @param {Number} newSize the new width or height
3315 * @event beforeresize
3316 * Fires before the splitter is dragged
3317 * @param {Roo.SplitBar} this
3319 "beforeresize" : true,
3321 "beforeapply" : true
3324 Roo.util.Observable.call(this);
3327 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3328 onStartProxyDrag : function(x, y){
3329 this.fireEvent("beforeresize", this);
3331 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
3333 o.enableDisplayMode("block");
3334 // all splitbars share the same overlay
3335 Roo.SplitBar.prototype.overlay = o;
3337 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3338 this.overlay.show();
3339 Roo.get(this.proxy).setDisplayed("block");
3340 var size = this.adapter.getElementSize(this);
3341 this.activeMinSize = this.getMinimumSize();;
3342 this.activeMaxSize = this.getMaximumSize();;
3343 var c1 = size - this.activeMinSize;
3344 var c2 = Math.max(this.activeMaxSize - size, 0);
3345 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3346 this.dd.resetConstraints();
3347 this.dd.setXConstraint(
3348 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
3349 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3351 this.dd.setYConstraint(0, 0);
3353 this.dd.resetConstraints();
3354 this.dd.setXConstraint(0, 0);
3355 this.dd.setYConstraint(
3356 this.placement == Roo.SplitBar.TOP ? c1 : c2,
3357 this.placement == Roo.SplitBar.TOP ? c2 : c1
3360 this.dragSpecs.startSize = size;
3361 this.dragSpecs.startPoint = [x, y];
3362 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3366 * @private Called after the drag operation by the DDProxy
3368 onEndProxyDrag : function(e){
3369 Roo.get(this.proxy).setDisplayed(false);
3370 var endPoint = Roo.lib.Event.getXY(e);
3372 this.overlay.hide();
3375 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3376 newSize = this.dragSpecs.startSize +
3377 (this.placement == Roo.SplitBar.LEFT ?
3378 endPoint[0] - this.dragSpecs.startPoint[0] :
3379 this.dragSpecs.startPoint[0] - endPoint[0]
3382 newSize = this.dragSpecs.startSize +
3383 (this.placement == Roo.SplitBar.TOP ?
3384 endPoint[1] - this.dragSpecs.startPoint[1] :
3385 this.dragSpecs.startPoint[1] - endPoint[1]
3388 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3389 if(newSize != this.dragSpecs.startSize){
3390 if(this.fireEvent('beforeapply', this, newSize) !== false){
3391 this.adapter.setElementSize(this, newSize);
3392 this.fireEvent("moved", this, newSize);
3393 this.fireEvent("resize", this, newSize);
3399 * Get the adapter this SplitBar uses
3400 * @return The adapter object
3402 getAdapter : function(){
3403 return this.adapter;
3407 * Set the adapter this SplitBar uses
3408 * @param {Object} adapter A SplitBar adapter object
3410 setAdapter : function(adapter){
3411 this.adapter = adapter;
3412 this.adapter.init(this);
3416 * Gets the minimum size for the resizing element
3417 * @return {Number} The minimum size
3419 getMinimumSize : function(){
3420 return this.minSize;
3424 * Sets the minimum size for the resizing element
3425 * @param {Number} minSize The minimum size
3427 setMinimumSize : function(minSize){
3428 this.minSize = minSize;
3432 * Gets the maximum size for the resizing element
3433 * @return {Number} The maximum size
3435 getMaximumSize : function(){
3436 return this.maxSize;
3440 * Sets the maximum size for the resizing element
3441 * @param {Number} maxSize The maximum size
3443 setMaximumSize : function(maxSize){
3444 this.maxSize = maxSize;
3448 * Sets the initialize size for the resizing element
3449 * @param {Number} size The initial size
3451 setCurrentSize : function(size){
3452 var oldAnimate = this.animate;
3453 this.animate = false;
3454 this.adapter.setElementSize(this, size);
3455 this.animate = oldAnimate;
3459 * Destroy this splitbar.
3460 * @param {Boolean} removeEl True to remove the element
3462 destroy : function(removeEl){
3467 this.proxy.parentNode.removeChild(this.proxy);
3475 * @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.
3477 Roo.SplitBar.createProxy = function(dir){
3478 var proxy = new Roo.Element(document.createElement("div"));
3479 proxy.unselectable();
3480 var cls = 'x-splitbar-proxy';
3481 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3482 document.body.appendChild(proxy.dom);
3487 * @class Roo.SplitBar.BasicLayoutAdapter
3488 * Default Adapter. It assumes the splitter and resizing element are not positioned
3489 * elements and only gets/sets the width of the element. Generally used for table based layouts.
3491 Roo.SplitBar.BasicLayoutAdapter = function(){
3494 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3495 // do nothing for now
3500 * Called before drag operations to get the current size of the resizing element.
3501 * @param {Roo.SplitBar} s The SplitBar using this adapter
3503 getElementSize : function(s){
3504 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3505 return s.resizingEl.getWidth();
3507 return s.resizingEl.getHeight();
3512 * Called after drag operations to set the size of the resizing element.
3513 * @param {Roo.SplitBar} s The SplitBar using this adapter
3514 * @param {Number} newSize The new size to set
3515 * @param {Function} onComplete A function to be invoked when resizing is complete
3517 setElementSize : function(s, newSize, onComplete){
3518 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3520 s.resizingEl.setWidth(newSize);
3522 onComplete(s, newSize);
3525 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3530 s.resizingEl.setHeight(newSize);
3532 onComplete(s, newSize);
3535 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3542 *@class Roo.SplitBar.AbsoluteLayoutAdapter
3543 * @extends Roo.SplitBar.BasicLayoutAdapter
3544 * Adapter that moves the splitter element to align with the resized sizing element.
3545 * Used with an absolute positioned SplitBar.
3546 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3547 * document.body, make sure you assign an id to the body element.
3549 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3550 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3551 this.container = Roo.get(container);
3554 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3559 getElementSize : function(s){
3560 return this.basic.getElementSize(s);
3563 setElementSize : function(s, newSize, onComplete){
3564 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3567 moveSplitter : function(s){
3568 var yes = Roo.SplitBar;
3569 switch(s.placement){
3571 s.el.setX(s.resizingEl.getRight());
3574 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3577 s.el.setY(s.resizingEl.getBottom());
3580 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3587 * Orientation constant - Create a vertical SplitBar
3591 Roo.SplitBar.VERTICAL = 1;
3594 * Orientation constant - Create a horizontal SplitBar
3598 Roo.SplitBar.HORIZONTAL = 2;
3601 * Placement constant - The resizing element is to the left of the splitter element
3605 Roo.SplitBar.LEFT = 1;
3608 * Placement constant - The resizing element is to the right of the splitter element
3612 Roo.SplitBar.RIGHT = 2;
3615 * Placement constant - The resizing element is positioned above the splitter element
3619 Roo.SplitBar.TOP = 3;
3622 * Placement constant - The resizing element is positioned under splitter element
3626 Roo.SplitBar.BOTTOM = 4;
3629 * Ext JS Library 1.1.1
3630 * Copyright(c) 2006-2007, Ext JS, LLC.
3632 * Originally Released Under LGPL - original licence link has changed is not relivant.
3635 * <script type="text/javascript">
3640 * @extends Roo.util.Observable
3641 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
3642 * This class also supports single and multi selection modes. <br>
3643 * Create a data model bound view:
3645 var store = new Roo.data.Store(...);
3647 var view = new Roo.View({
3649 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
3652 selectedClass: "ydataview-selected",
3656 // listen for node click?
3657 view.on("click", function(vw, index, node, e){
3658 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3662 dataModel.load("foobar.xml");
3664 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3666 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3667 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3669 * Note: old style constructor is still suported (container, template, config)
3673 * @param {Object} config The config object
3676 Roo.View = function(config, depreciated_tpl, depreciated_config){
3678 this.parent = false;
3680 if (typeof(depreciated_tpl) == 'undefined') {
3681 // new way.. - universal constructor.
3682 Roo.apply(this, config);
3683 this.el = Roo.get(this.el);
3686 this.el = Roo.get(config);
3687 this.tpl = depreciated_tpl;
3688 Roo.apply(this, depreciated_config);
3690 this.wrapEl = this.el.wrap().wrap();
3691 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3694 if(typeof(this.tpl) == "string"){
3695 this.tpl = new Roo.Template(this.tpl);
3697 // support xtype ctors..
3698 this.tpl = new Roo.factory(this.tpl, Roo);
3707 * @event beforeclick
3708 * Fires before a click is processed. Returns false to cancel the default action.
3709 * @param {Roo.View} this
3710 * @param {Number} index The index of the target node
3711 * @param {HTMLElement} node The target node
3712 * @param {Roo.EventObject} e The raw event object
3714 "beforeclick" : true,
3717 * Fires when a template node is clicked.
3718 * @param {Roo.View} this
3719 * @param {Number} index The index of the target node
3720 * @param {HTMLElement} node The target node
3721 * @param {Roo.EventObject} e The raw event object
3726 * Fires when a template node is double clicked.
3727 * @param {Roo.View} this
3728 * @param {Number} index The index of the target node
3729 * @param {HTMLElement} node The target node
3730 * @param {Roo.EventObject} e The raw event object
3734 * @event contextmenu
3735 * Fires when a template node is right clicked.
3736 * @param {Roo.View} this
3737 * @param {Number} index The index of the target node
3738 * @param {HTMLElement} node The target node
3739 * @param {Roo.EventObject} e The raw event object
3741 "contextmenu" : true,
3743 * @event selectionchange
3744 * Fires when the selected nodes change.
3745 * @param {Roo.View} this
3746 * @param {Array} selections Array of the selected nodes
3748 "selectionchange" : true,
3751 * @event beforeselect
3752 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3753 * @param {Roo.View} this
3754 * @param {HTMLElement} node The node to be selected
3755 * @param {Array} selections Array of currently selected nodes
3757 "beforeselect" : true,
3759 * @event preparedata
3760 * Fires on every row to render, to allow you to change the data.
3761 * @param {Roo.View} this
3762 * @param {Object} data to be rendered (change this)
3764 "preparedata" : true
3772 "click": this.onClick,
3773 "dblclick": this.onDblClick,
3774 "contextmenu": this.onContextMenu,
3778 this.selections = [];
3780 this.cmp = new Roo.CompositeElementLite([]);
3782 this.store = Roo.factory(this.store, Roo.data);
3783 this.setStore(this.store, true);
3786 if ( this.footer && this.footer.xtype) {
3788 var fctr = this.wrapEl.appendChild(document.createElement("div"));
3790 this.footer.dataSource = this.store;
3791 this.footer.container = fctr;
3792 this.footer = Roo.factory(this.footer, Roo);
3793 fctr.insertFirst(this.el);
3795 // this is a bit insane - as the paging toolbar seems to detach the el..
3796 // dom.parentNode.parentNode.parentNode
3797 // they get detached?
3801 Roo.View.superclass.constructor.call(this);
3806 Roo.extend(Roo.View, Roo.util.Observable, {
3809 * @cfg {Roo.data.Store} store Data store to load data from.
3814 * @cfg {String|Roo.Element} el The container element.
3819 * @cfg {String|Roo.Template} tpl The template used by this View
3823 * @cfg {String} dataName the named area of the template to use as the data area
3824 * Works with domtemplates roo-name="name"
3828 * @cfg {String} selectedClass The css class to add to selected nodes
3830 selectedClass : "x-view-selected",
3832 * @cfg {String} emptyText The empty text to show when nothing is loaded.
3837 * @cfg {String} text to display on mask (default Loading)
3841 * @cfg {Boolean} multiSelect Allow multiple selection
3843 multiSelect : false,
3845 * @cfg {Boolean} singleSelect Allow single selection
3847 singleSelect: false,
3850 * @cfg {Boolean} toggleSelect - selecting
3852 toggleSelect : false,
3855 * @cfg {Boolean} tickable - selecting
3860 * Returns the element this view is bound to.
3861 * @return {Roo.Element}
3870 * Refreshes the view. - called by datachanged on the store. - do not call directly.
3872 refresh : function(){
3873 //Roo.log('refresh');
3876 // if we are using something like 'domtemplate', then
3877 // the what gets used is:
3878 // t.applySubtemplate(NAME, data, wrapping data..)
3879 // the outer template then get' applied with
3880 // the store 'extra data'
3881 // and the body get's added to the
3882 // roo-name="data" node?
3883 // <span class='roo-tpl-{name}'></span> ?????
3887 this.clearSelections();
3890 var records = this.store.getRange();
3891 if(records.length < 1) {
3893 // is this valid?? = should it render a template??
3895 this.el.update(this.emptyText);
3899 if (this.dataName) {
3900 this.el.update(t.apply(this.store.meta)); //????
3901 el = this.el.child('.roo-tpl-' + this.dataName);
3904 for(var i = 0, len = records.length; i < len; i++){
3905 var data = this.prepareData(records[i].data, i, records[i]);
3906 this.fireEvent("preparedata", this, data, i, records[i]);
3908 var d = Roo.apply({}, data);
3911 Roo.apply(d, {'roo-id' : Roo.id()});
3915 Roo.each(this.parent.item, function(item){
3916 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3919 Roo.apply(d, {'roo-data-checked' : 'checked'});
3923 html[html.length] = Roo.util.Format.trim(
3925 t.applySubtemplate(this.dataName, d, this.store.meta) :
3932 el.update(html.join(""));
3933 this.nodes = el.dom.childNodes;
3934 this.updateIndexes(0);
3939 * Function to override to reformat the data that is sent to
3940 * the template for each node.
3941 * DEPRICATED - use the preparedata event handler.
3942 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3943 * a JSON object for an UpdateManager bound view).
3945 prepareData : function(data, index, record)
3947 this.fireEvent("preparedata", this, data, index, record);
3951 onUpdate : function(ds, record){
3952 // Roo.log('on update');
3953 this.clearSelections();
3954 var index = this.store.indexOf(record);
3955 var n = this.nodes[index];
3956 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3957 n.parentNode.removeChild(n);
3958 this.updateIndexes(index, index);
3964 onAdd : function(ds, records, index)
3966 //Roo.log(['on Add', ds, records, index] );
3967 this.clearSelections();
3968 if(this.nodes.length == 0){
3972 var n = this.nodes[index];
3973 for(var i = 0, len = records.length; i < len; i++){
3974 var d = this.prepareData(records[i].data, i, records[i]);
3976 this.tpl.insertBefore(n, d);
3979 this.tpl.append(this.el, d);
3982 this.updateIndexes(index);
3985 onRemove : function(ds, record, index){
3986 // Roo.log('onRemove');
3987 this.clearSelections();
3988 var el = this.dataName ?
3989 this.el.child('.roo-tpl-' + this.dataName) :
3992 el.dom.removeChild(this.nodes[index]);
3993 this.updateIndexes(index);
3997 * Refresh an individual node.
3998 * @param {Number} index
4000 refreshNode : function(index){
4001 this.onUpdate(this.store, this.store.getAt(index));
4004 updateIndexes : function(startIndex, endIndex){
4005 var ns = this.nodes;
4006 startIndex = startIndex || 0;
4007 endIndex = endIndex || ns.length - 1;
4008 for(var i = startIndex; i <= endIndex; i++){
4009 ns[i].nodeIndex = i;
4014 * Changes the data store this view uses and refresh the view.
4015 * @param {Store} store
4017 setStore : function(store, initial){
4018 if(!initial && this.store){
4019 this.store.un("datachanged", this.refresh);
4020 this.store.un("add", this.onAdd);
4021 this.store.un("remove", this.onRemove);
4022 this.store.un("update", this.onUpdate);
4023 this.store.un("clear", this.refresh);
4024 this.store.un("beforeload", this.onBeforeLoad);
4025 this.store.un("load", this.onLoad);
4026 this.store.un("loadexception", this.onLoad);
4030 store.on("datachanged", this.refresh, this);
4031 store.on("add", this.onAdd, this);
4032 store.on("remove", this.onRemove, this);
4033 store.on("update", this.onUpdate, this);
4034 store.on("clear", this.refresh, this);
4035 store.on("beforeload", this.onBeforeLoad, this);
4036 store.on("load", this.onLoad, this);
4037 store.on("loadexception", this.onLoad, this);
4045 * onbeforeLoad - masks the loading area.
4048 onBeforeLoad : function(store,opts)
4050 //Roo.log('onBeforeLoad');
4054 this.el.mask(this.mask ? this.mask : "Loading" );
4056 onLoad : function ()
4063 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4064 * @param {HTMLElement} node
4065 * @return {HTMLElement} The template node
4067 findItemFromChild : function(node){
4068 var el = this.dataName ?
4069 this.el.child('.roo-tpl-' + this.dataName,true) :
4072 if(!node || node.parentNode == el){
4075 var p = node.parentNode;
4076 while(p && p != el){
4077 if(p.parentNode == el){
4086 onClick : function(e){
4087 var item = this.findItemFromChild(e.getTarget());
4089 var index = this.indexOf(item);
4090 if(this.onItemClick(item, index, e) !== false){
4091 this.fireEvent("click", this, index, item, e);
4094 this.clearSelections();
4099 onContextMenu : function(e){
4100 var item = this.findItemFromChild(e.getTarget());
4102 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4107 onDblClick : function(e){
4108 var item = this.findItemFromChild(e.getTarget());
4110 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4114 onItemClick : function(item, index, e)
4116 if(this.fireEvent("beforeclick", this, index, item, e) === false){
4119 if (this.toggleSelect) {
4120 var m = this.isSelected(item) ? 'unselect' : 'select';
4123 _t[m](item, true, false);
4126 if(this.multiSelect || this.singleSelect){
4127 if(this.multiSelect && e.shiftKey && this.lastSelection){
4128 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4130 this.select(item, this.multiSelect && e.ctrlKey);
4131 this.lastSelection = item;
4143 * Get the number of selected nodes.
4146 getSelectionCount : function(){
4147 return this.selections.length;
4151 * Get the currently selected nodes.
4152 * @return {Array} An array of HTMLElements
4154 getSelectedNodes : function(){
4155 return this.selections;
4159 * Get the indexes of the selected nodes.
4162 getSelectedIndexes : function(){
4163 var indexes = [], s = this.selections;
4164 for(var i = 0, len = s.length; i < len; i++){
4165 indexes.push(s[i].nodeIndex);
4171 * Clear all selections
4172 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4174 clearSelections : function(suppressEvent){
4175 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4176 this.cmp.elements = this.selections;
4177 this.cmp.removeClass(this.selectedClass);
4178 this.selections = [];
4180 this.fireEvent("selectionchange", this, this.selections);
4186 * Returns true if the passed node is selected
4187 * @param {HTMLElement/Number} node The node or node index
4190 isSelected : function(node){
4191 var s = this.selections;
4195 node = this.getNode(node);
4196 return s.indexOf(node) !== -1;
4201 * @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
4202 * @param {Boolean} keepExisting (optional) true to keep existing selections
4203 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4205 select : function(nodeInfo, keepExisting, suppressEvent){
4206 if(nodeInfo instanceof Array){
4208 this.clearSelections(true);
4210 for(var i = 0, len = nodeInfo.length; i < len; i++){
4211 this.select(nodeInfo[i], true, true);
4215 var node = this.getNode(nodeInfo);
4216 if(!node || this.isSelected(node)){
4217 return; // already selected.
4220 this.clearSelections(true);
4223 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4224 Roo.fly(node).addClass(this.selectedClass);
4225 this.selections.push(node);
4227 this.fireEvent("selectionchange", this, this.selections);
4235 * @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
4236 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4237 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4239 unselect : function(nodeInfo, keepExisting, suppressEvent)
4241 if(nodeInfo instanceof Array){
4242 Roo.each(this.selections, function(s) {
4243 this.unselect(s, nodeInfo);
4247 var node = this.getNode(nodeInfo);
4248 if(!node || !this.isSelected(node)){
4249 //Roo.log("not selected");
4250 return; // not selected.
4254 Roo.each(this.selections, function(s) {
4256 Roo.fly(node).removeClass(this.selectedClass);
4263 this.selections= ns;
4264 this.fireEvent("selectionchange", this, this.selections);
4268 * Gets a template node.
4269 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4270 * @return {HTMLElement} The node or null if it wasn't found
4272 getNode : function(nodeInfo){
4273 if(typeof nodeInfo == "string"){
4274 return document.getElementById(nodeInfo);
4275 }else if(typeof nodeInfo == "number"){
4276 return this.nodes[nodeInfo];
4282 * Gets a range template nodes.
4283 * @param {Number} startIndex
4284 * @param {Number} endIndex
4285 * @return {Array} An array of nodes
4287 getNodes : function(start, end){
4288 var ns = this.nodes;
4290 end = typeof end == "undefined" ? ns.length - 1 : end;
4293 for(var i = start; i <= end; i++){
4297 for(var i = start; i >= end; i--){
4305 * Finds the index of the passed node
4306 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4307 * @return {Number} The index of the node or -1
4309 indexOf : function(node){
4310 node = this.getNode(node);
4311 if(typeof node.nodeIndex == "number"){
4312 return node.nodeIndex;
4314 var ns = this.nodes;
4315 for(var i = 0, len = ns.length; i < len; i++){
4325 * Ext JS Library 1.1.1
4326 * Copyright(c) 2006-2007, Ext JS, LLC.
4328 * Originally Released Under LGPL - original licence link has changed is not relivant.
4331 * <script type="text/javascript">
4335 * @class Roo.JsonView
4337 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4339 var view = new Roo.JsonView({
4340 container: "my-element",
4341 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
4346 // listen for node click?
4347 view.on("click", function(vw, index, node, e){
4348 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4351 // direct load of JSON data
4352 view.load("foobar.php");
4354 // Example from my blog list
4355 var tpl = new Roo.Template(
4356 '<div class="entry">' +
4357 '<a class="entry-title" href="{link}">{title}</a>' +
4358 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
4359 "</div><hr />"
4362 var moreView = new Roo.JsonView({
4363 container : "entry-list",
4367 moreView.on("beforerender", this.sortEntries, this);
4369 url: "/blog/get-posts.php",
4370 params: "allposts=true",
4371 text: "Loading Blog Entries..."
4375 * Note: old code is supported with arguments : (container, template, config)
4379 * Create a new JsonView
4381 * @param {Object} config The config object
4384 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4387 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4389 var um = this.el.getUpdateManager();
4390 um.setRenderer(this);
4391 um.on("update", this.onLoad, this);
4392 um.on("failure", this.onLoadException, this);
4395 * @event beforerender
4396 * Fires before rendering of the downloaded JSON data.
4397 * @param {Roo.JsonView} this
4398 * @param {Object} data The JSON data loaded
4402 * Fires when data is loaded.
4403 * @param {Roo.JsonView} this
4404 * @param {Object} data The JSON data loaded
4405 * @param {Object} response The raw Connect response object
4408 * @event loadexception
4409 * Fires when loading fails.
4410 * @param {Roo.JsonView} this
4411 * @param {Object} response The raw Connect response object
4414 'beforerender' : true,
4416 'loadexception' : true
4419 Roo.extend(Roo.JsonView, Roo.View, {
4421 * @type {String} The root property in the loaded JSON object that contains the data
4426 * Refreshes the view.
4428 refresh : function(){
4429 this.clearSelections();
4432 var o = this.jsonData;
4433 if(o && o.length > 0){
4434 for(var i = 0, len = o.length; i < len; i++){
4435 var data = this.prepareData(o[i], i, o);
4436 html[html.length] = this.tpl.apply(data);
4439 html.push(this.emptyText);
4441 this.el.update(html.join(""));
4442 this.nodes = this.el.dom.childNodes;
4443 this.updateIndexes(0);
4447 * 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.
4448 * @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:
4451 url: "your-url.php",
4452 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4453 callback: yourFunction,
4454 scope: yourObject, //(optional scope)
4462 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4463 * 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.
4464 * @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}
4465 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4466 * @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.
4469 var um = this.el.getUpdateManager();
4470 um.update.apply(um, arguments);
4473 // note - render is a standard framework call...
4474 // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4475 render : function(el, response){
4477 this.clearSelections();
4481 if (response != '') {
4482 o = Roo.util.JSON.decode(response.responseText);
4485 o = o[this.jsonRoot];
4491 * The current JSON data or null
4494 this.beforeRender();
4499 * Get the number of records in the current JSON dataset
4502 getCount : function(){
4503 return this.jsonData ? this.jsonData.length : 0;
4507 * Returns the JSON object for the specified node(s)
4508 * @param {HTMLElement/Array} node The node or an array of nodes
4509 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4510 * you get the JSON object for the node
4512 getNodeData : function(node){
4513 if(node instanceof Array){
4515 for(var i = 0, len = node.length; i < len; i++){
4516 data.push(this.getNodeData(node[i]));
4520 return this.jsonData[this.indexOf(node)] || null;
4523 beforeRender : function(){
4524 this.snapshot = this.jsonData;
4526 this.sort.apply(this, this.sortInfo);
4528 this.fireEvent("beforerender", this, this.jsonData);
4531 onLoad : function(el, o){
4532 this.fireEvent("load", this, this.jsonData, o);
4535 onLoadException : function(el, o){
4536 this.fireEvent("loadexception", this, o);
4540 * Filter the data by a specific property.
4541 * @param {String} property A property on your JSON objects
4542 * @param {String/RegExp} value Either string that the property values
4543 * should start with, or a RegExp to test against the property
4545 filter : function(property, value){
4548 var ss = this.snapshot;
4549 if(typeof value == "string"){
4550 var vlen = value.length;
4555 value = value.toLowerCase();
4556 for(var i = 0, len = ss.length; i < len; i++){
4558 if(o[property].substr(0, vlen).toLowerCase() == value){
4562 } else if(value.exec){ // regex?
4563 for(var i = 0, len = ss.length; i < len; i++){
4565 if(value.test(o[property])){
4572 this.jsonData = data;
4578 * Filter by a function. The passed function will be called with each
4579 * object in the current dataset. If the function returns true the value is kept,
4580 * otherwise it is filtered.
4581 * @param {Function} fn
4582 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4584 filterBy : function(fn, scope){
4587 var ss = this.snapshot;
4588 for(var i = 0, len = ss.length; i < len; i++){
4590 if(fn.call(scope || this, o)){
4594 this.jsonData = data;
4600 * Clears the current filter.
4602 clearFilter : function(){
4603 if(this.snapshot && this.jsonData != this.snapshot){
4604 this.jsonData = this.snapshot;
4611 * Sorts the data for this view and refreshes it.
4612 * @param {String} property A property on your JSON objects to sort on
4613 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4614 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4616 sort : function(property, dir, sortType){
4617 this.sortInfo = Array.prototype.slice.call(arguments, 0);
4620 var dsc = dir && dir.toLowerCase() == "desc";
4621 var f = function(o1, o2){
4622 var v1 = sortType ? sortType(o1[p]) : o1[p];
4623 var v2 = sortType ? sortType(o2[p]) : o2[p];
4626 return dsc ? +1 : -1;
4628 return dsc ? -1 : +1;
4633 this.jsonData.sort(f);
4635 if(this.jsonData != this.snapshot){
4636 this.snapshot.sort(f);
4642 * Ext JS Library 1.1.1
4643 * Copyright(c) 2006-2007, Ext JS, LLC.
4645 * Originally Released Under LGPL - original licence link has changed is not relivant.
4648 * <script type="text/javascript">
4653 * @class Roo.ColorPalette
4654 * @extends Roo.Component
4655 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
4656 * Here's an example of typical usage:
4658 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
4659 cp.render('my-div');
4661 cp.on('select', function(palette, selColor){
4662 // do something with selColor
4666 * Create a new ColorPalette
4667 * @param {Object} config The config object
4669 Roo.ColorPalette = function(config){
4670 Roo.ColorPalette.superclass.constructor.call(this, config);
4674 * Fires when a color is selected
4675 * @param {ColorPalette} this
4676 * @param {String} color The 6-digit color hex code (without the # symbol)
4682 this.on("select", this.handler, this.scope, true);
4685 Roo.extend(Roo.ColorPalette, Roo.Component, {
4687 * @cfg {String} itemCls
4688 * The CSS class to apply to the containing element (defaults to "x-color-palette")
4690 itemCls : "x-color-palette",
4692 * @cfg {String} value
4693 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
4694 * the hex codes are case-sensitive.
4699 ctype: "Roo.ColorPalette",
4702 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4704 allowReselect : false,
4707 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
4708 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
4709 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4710 * of colors with the width setting until the box is symmetrical.</p>
4711 * <p>You can override individual colors if needed:</p>
4713 var cp = new Roo.ColorPalette();
4714 cp.colors[0] = "FF0000"; // change the first box to red
4717 Or you can provide a custom array of your own for complete control:
4719 var cp = new Roo.ColorPalette();
4720 cp.colors = ["000000", "993300", "333300"];
4725 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4726 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4727 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4728 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4729 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4733 onRender : function(container, position){
4734 var t = new Roo.MasterTemplate(
4735 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
4737 var c = this.colors;
4738 for(var i = 0, len = c.length; i < len; i++){
4741 var el = document.createElement("div");
4742 el.className = this.itemCls;
4744 container.dom.insertBefore(el, position);
4745 this.el = Roo.get(el);
4746 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
4747 if(this.clickEvent != 'click'){
4748 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
4753 afterRender : function(){
4754 Roo.ColorPalette.superclass.afterRender.call(this);
4763 handleClick : function(e, t){
4766 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4767 this.select(c.toUpperCase());
4772 * Selects the specified color in the palette (fires the select event)
4773 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4775 select : function(color){
4776 color = color.replace("#", "");
4777 if(color != this.value || this.allowReselect){
4780 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4782 el.child("a.color-"+color).addClass("x-color-palette-sel");
4784 this.fireEvent("select", this, color);
4789 * Ext JS Library 1.1.1
4790 * Copyright(c) 2006-2007, Ext JS, LLC.
4792 * Originally Released Under LGPL - original licence link has changed is not relivant.
4795 * <script type="text/javascript">
4799 * @class Roo.DatePicker
4800 * @extends Roo.Component
4801 * Simple date picker class.
4803 * Create a new DatePicker
4804 * @param {Object} config The config object
4806 Roo.DatePicker = function(config){
4807 Roo.DatePicker.superclass.constructor.call(this, config);
4809 this.value = config && config.value ?
4810 config.value.clearTime() : new Date().clearTime();
4815 * Fires when a date is selected
4816 * @param {DatePicker} this
4817 * @param {Date} date The selected date
4821 * @event monthchange
4822 * Fires when the displayed month changes
4823 * @param {DatePicker} this
4824 * @param {Date} date The selected month
4830 this.on("select", this.handler, this.scope || this);
4832 // build the disabledDatesRE
4833 if(!this.disabledDatesRE && this.disabledDates){
4834 var dd = this.disabledDates;
4836 for(var i = 0; i < dd.length; i++){
4838 if(i != dd.length-1) {
4842 this.disabledDatesRE = new RegExp(re + ")");
4846 Roo.extend(Roo.DatePicker, Roo.Component, {
4848 * @cfg {String} todayText
4849 * The text to display on the button that selects the current date (defaults to "Today")
4851 todayText : "Today",
4853 * @cfg {String} okText
4854 * The text to display on the ok button
4856 okText : " OK ", //   to give the user extra clicking room
4858 * @cfg {String} cancelText
4859 * The text to display on the cancel button
4861 cancelText : "Cancel",
4863 * @cfg {String} todayTip
4864 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4866 todayTip : "{0} (Spacebar)",
4868 * @cfg {Date} minDate
4869 * Minimum allowable date (JavaScript date object, defaults to null)
4873 * @cfg {Date} maxDate
4874 * Maximum allowable date (JavaScript date object, defaults to null)
4878 * @cfg {String} minText
4879 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4881 minText : "This date is before the minimum date",
4883 * @cfg {String} maxText
4884 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4886 maxText : "This date is after the maximum date",
4888 * @cfg {String} format
4889 * The default date format string which can be overriden for localization support. The format must be
4890 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4894 * @cfg {Array} disabledDays
4895 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4897 disabledDays : null,
4899 * @cfg {String} disabledDaysText
4900 * The tooltip to display when the date falls on a disabled day (defaults to "")
4902 disabledDaysText : "",
4904 * @cfg {RegExp} disabledDatesRE
4905 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4907 disabledDatesRE : null,
4909 * @cfg {String} disabledDatesText
4910 * The tooltip text to display when the date falls on a disabled date (defaults to "")
4912 disabledDatesText : "",
4914 * @cfg {Boolean} constrainToViewport
4915 * True to constrain the date picker to the viewport (defaults to true)
4917 constrainToViewport : true,
4919 * @cfg {Array} monthNames
4920 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4922 monthNames : Date.monthNames,
4924 * @cfg {Array} dayNames
4925 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4927 dayNames : Date.dayNames,
4929 * @cfg {String} nextText
4930 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4932 nextText: 'Next Month (Control+Right)',
4934 * @cfg {String} prevText
4935 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4937 prevText: 'Previous Month (Control+Left)',
4939 * @cfg {String} monthYearText
4940 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4942 monthYearText: 'Choose a month (Control+Up/Down to move years)',
4944 * @cfg {Number} startDay
4945 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4949 * @cfg {Bool} showClear
4950 * Show a clear button (usefull for date form elements that can be blank.)
4956 * Sets the value of the date field
4957 * @param {Date} value The date to set
4959 setValue : function(value){
4960 var old = this.value;
4962 if (typeof(value) == 'string') {
4964 value = Date.parseDate(value, this.format);
4970 this.value = value.clearTime(true);
4972 this.update(this.value);
4977 * Gets the current selected value of the date field
4978 * @return {Date} The selected date
4980 getValue : function(){
4987 this.update(this.activeDate);
4992 onRender : function(container, position){
4995 '<table cellspacing="0">',
4996 '<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>',
4997 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
4998 var dn = this.dayNames;
4999 for(var i = 0; i < 7; i++){
5000 var d = this.startDay+i;
5004 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5006 m[m.length] = "</tr></thead><tbody><tr>";
5007 for(var i = 0; i < 42; i++) {
5008 if(i % 7 == 0 && i != 0){
5009 m[m.length] = "</tr><tr>";
5011 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5013 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5014 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5016 var el = document.createElement("div");
5017 el.className = "x-date-picker";
5018 el.innerHTML = m.join("");
5020 container.dom.insertBefore(el, position);
5022 this.el = Roo.get(el);
5023 this.eventEl = Roo.get(el.firstChild);
5025 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5026 handler: this.showPrevMonth,
5028 preventDefault:true,
5032 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5033 handler: this.showNextMonth,
5035 preventDefault:true,
5039 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
5041 this.monthPicker = this.el.down('div.x-date-mp');
5042 this.monthPicker.enableDisplayMode('block');
5044 var kn = new Roo.KeyNav(this.eventEl, {
5045 "left" : function(e){
5047 this.showPrevMonth() :
5048 this.update(this.activeDate.add("d", -1));
5051 "right" : function(e){
5053 this.showNextMonth() :
5054 this.update(this.activeDate.add("d", 1));
5059 this.showNextYear() :
5060 this.update(this.activeDate.add("d", -7));
5063 "down" : function(e){
5065 this.showPrevYear() :
5066 this.update(this.activeDate.add("d", 7));
5069 "pageUp" : function(e){
5070 this.showNextMonth();
5073 "pageDown" : function(e){
5074 this.showPrevMonth();
5077 "enter" : function(e){
5078 e.stopPropagation();
5085 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
5087 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
5089 this.el.unselectable();
5091 this.cells = this.el.select("table.x-date-inner tbody td");
5092 this.textNodes = this.el.query("table.x-date-inner tbody span");
5094 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5096 tooltip: this.monthYearText
5099 this.mbtn.on('click', this.showMonthPicker, this);
5100 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5103 var today = (new Date()).dateFormat(this.format);
5105 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5106 if (this.showClear) {
5107 baseTb.add( new Roo.Toolbar.Fill());
5110 text: String.format(this.todayText, today),
5111 tooltip: String.format(this.todayTip, today),
5112 handler: this.selectToday,
5116 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5119 if (this.showClear) {
5121 baseTb.add( new Roo.Toolbar.Fill());
5124 cls: 'x-btn-icon x-btn-clear',
5125 handler: function() {
5127 this.fireEvent("select", this, '');
5137 this.update(this.value);
5140 createMonthPicker : function(){
5141 if(!this.monthPicker.dom.firstChild){
5142 var buf = ['<table border="0" cellspacing="0">'];
5143 for(var i = 0; i < 6; i++){
5145 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5146 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5148 '<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>' :
5149 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5153 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5155 '</button><button type="button" class="x-date-mp-cancel">',
5157 '</button></td></tr>',
5160 this.monthPicker.update(buf.join(''));
5161 this.monthPicker.on('click', this.onMonthClick, this);
5162 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5164 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5165 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5167 this.mpMonths.each(function(m, a, i){
5170 m.dom.xmonth = 5 + Math.round(i * .5);
5172 m.dom.xmonth = Math.round((i-1) * .5);
5178 showMonthPicker : function(){
5179 this.createMonthPicker();
5180 var size = this.el.getSize();
5181 this.monthPicker.setSize(size);
5182 this.monthPicker.child('table').setSize(size);
5184 this.mpSelMonth = (this.activeDate || this.value).getMonth();
5185 this.updateMPMonth(this.mpSelMonth);
5186 this.mpSelYear = (this.activeDate || this.value).getFullYear();
5187 this.updateMPYear(this.mpSelYear);
5189 this.monthPicker.slideIn('t', {duration:.2});
5192 updateMPYear : function(y){
5194 var ys = this.mpYears.elements;
5195 for(var i = 1; i <= 10; i++){
5196 var td = ys[i-1], y2;
5198 y2 = y + Math.round(i * .5);
5199 td.firstChild.innerHTML = y2;
5202 y2 = y - (5-Math.round(i * .5));
5203 td.firstChild.innerHTML = y2;
5206 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5210 updateMPMonth : function(sm){
5211 this.mpMonths.each(function(m, a, i){
5212 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5216 selectMPMonth: function(m){
5220 onMonthClick : function(e, t){
5222 var el = new Roo.Element(t), pn;
5223 if(el.is('button.x-date-mp-cancel')){
5224 this.hideMonthPicker();
5226 else if(el.is('button.x-date-mp-ok')){
5227 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5228 this.hideMonthPicker();
5230 else if(pn = el.up('td.x-date-mp-month', 2)){
5231 this.mpMonths.removeClass('x-date-mp-sel');
5232 pn.addClass('x-date-mp-sel');
5233 this.mpSelMonth = pn.dom.xmonth;
5235 else if(pn = el.up('td.x-date-mp-year', 2)){
5236 this.mpYears.removeClass('x-date-mp-sel');
5237 pn.addClass('x-date-mp-sel');
5238 this.mpSelYear = pn.dom.xyear;
5240 else if(el.is('a.x-date-mp-prev')){
5241 this.updateMPYear(this.mpyear-10);
5243 else if(el.is('a.x-date-mp-next')){
5244 this.updateMPYear(this.mpyear+10);
5248 onMonthDblClick : function(e, t){
5250 var el = new Roo.Element(t), pn;
5251 if(pn = el.up('td.x-date-mp-month', 2)){
5252 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5253 this.hideMonthPicker();
5255 else if(pn = el.up('td.x-date-mp-year', 2)){
5256 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5257 this.hideMonthPicker();
5261 hideMonthPicker : function(disableAnim){
5262 if(this.monthPicker){
5263 if(disableAnim === true){
5264 this.monthPicker.hide();
5266 this.monthPicker.slideOut('t', {duration:.2});
5272 showPrevMonth : function(e){
5273 this.update(this.activeDate.add("mo", -1));
5277 showNextMonth : function(e){
5278 this.update(this.activeDate.add("mo", 1));
5282 showPrevYear : function(){
5283 this.update(this.activeDate.add("y", -1));
5287 showNextYear : function(){
5288 this.update(this.activeDate.add("y", 1));
5292 handleMouseWheel : function(e){
5293 var delta = e.getWheelDelta();
5295 this.showPrevMonth();
5297 } else if(delta < 0){
5298 this.showNextMonth();
5304 handleDateClick : function(e, t){
5306 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5307 this.setValue(new Date(t.dateValue));
5308 this.fireEvent("select", this, this.value);
5313 selectToday : function(){
5314 this.setValue(new Date().clearTime());
5315 this.fireEvent("select", this, this.value);
5319 update : function(date)
5321 var vd = this.activeDate;
5322 this.activeDate = date;
5324 var t = date.getTime();
5325 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5326 this.cells.removeClass("x-date-selected");
5327 this.cells.each(function(c){
5328 if(c.dom.firstChild.dateValue == t){
5329 c.addClass("x-date-selected");
5330 setTimeout(function(){
5331 try{c.dom.firstChild.focus();}catch(e){}
5340 var days = date.getDaysInMonth();
5341 var firstOfMonth = date.getFirstDateOfMonth();
5342 var startingPos = firstOfMonth.getDay()-this.startDay;
5344 if(startingPos <= this.startDay){
5348 var pm = date.add("mo", -1);
5349 var prevStart = pm.getDaysInMonth()-startingPos;
5351 var cells = this.cells.elements;
5352 var textEls = this.textNodes;
5353 days += startingPos;
5355 // convert everything to numbers so it's fast
5357 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5358 var today = new Date().clearTime().getTime();
5359 var sel = date.clearTime().getTime();
5360 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5361 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5362 var ddMatch = this.disabledDatesRE;
5363 var ddText = this.disabledDatesText;
5364 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5365 var ddaysText = this.disabledDaysText;
5366 var format = this.format;
5368 var setCellClass = function(cal, cell){
5370 var t = d.getTime();
5371 cell.firstChild.dateValue = t;
5373 cell.className += " x-date-today";
5374 cell.title = cal.todayText;
5377 cell.className += " x-date-selected";
5378 setTimeout(function(){
5379 try{cell.firstChild.focus();}catch(e){}
5384 cell.className = " x-date-disabled";
5385 cell.title = cal.minText;
5389 cell.className = " x-date-disabled";
5390 cell.title = cal.maxText;
5394 if(ddays.indexOf(d.getDay()) != -1){
5395 cell.title = ddaysText;
5396 cell.className = " x-date-disabled";
5399 if(ddMatch && format){
5400 var fvalue = d.dateFormat(format);
5401 if(ddMatch.test(fvalue)){
5402 cell.title = ddText.replace("%0", fvalue);
5403 cell.className = " x-date-disabled";
5409 for(; i < startingPos; i++) {
5410 textEls[i].innerHTML = (++prevStart);
5411 d.setDate(d.getDate()+1);
5412 cells[i].className = "x-date-prevday";
5413 setCellClass(this, cells[i]);
5415 for(; i < days; i++){
5416 intDay = i - startingPos + 1;
5417 textEls[i].innerHTML = (intDay);
5418 d.setDate(d.getDate()+1);
5419 cells[i].className = "x-date-active";
5420 setCellClass(this, cells[i]);
5423 for(; i < 42; i++) {
5424 textEls[i].innerHTML = (++extraDays);
5425 d.setDate(d.getDate()+1);
5426 cells[i].className = "x-date-nextday";
5427 setCellClass(this, cells[i]);
5430 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5431 this.fireEvent('monthchange', this, date);
5433 if(!this.internalRender){
5434 var main = this.el.dom.firstChild;
5435 var w = main.offsetWidth;
5436 this.el.setWidth(w + this.el.getBorderWidth("lr"));
5437 Roo.fly(main).setWidth(w);
5438 this.internalRender = true;
5439 // opera does not respect the auto grow header center column
5440 // then, after it gets a width opera refuses to recalculate
5441 // without a second pass
5442 if(Roo.isOpera && !this.secondPass){
5443 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5444 this.secondPass = true;
5445 this.update.defer(10, this, [date]);
5453 * Ext JS Library 1.1.1
5454 * Copyright(c) 2006-2007, Ext JS, LLC.
5456 * Originally Released Under LGPL - original licence link has changed is not relivant.
5459 * <script type="text/javascript">
5462 * @class Roo.TabPanel
5463 * @extends Roo.util.Observable
5464 * A lightweight tab container.
5468 // basic tabs 1, built from existing content
5469 var tabs = new Roo.TabPanel("tabs1");
5470 tabs.addTab("script", "View Script");
5471 tabs.addTab("markup", "View Markup");
5472 tabs.activate("script");
5474 // more advanced tabs, built from javascript
5475 var jtabs = new Roo.TabPanel("jtabs");
5476 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5478 // set up the UpdateManager
5479 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5480 var updater = tab2.getUpdateManager();
5481 updater.setDefaultUrl("ajax1.htm");
5482 tab2.on('activate', updater.refresh, updater, true);
5484 // Use setUrl for Ajax loading
5485 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5486 tab3.setUrl("ajax2.htm", null, true);
5489 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5492 jtabs.activate("jtabs-1");
5495 * Create a new TabPanel.
5496 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5497 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5499 Roo.TabPanel = function(container, config){
5501 * The container element for this TabPanel.
5504 this.el = Roo.get(container, true);
5506 if(typeof config == "boolean"){
5507 this.tabPosition = config ? "bottom" : "top";
5509 Roo.apply(this, config);
5512 if(this.tabPosition == "bottom"){
5513 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5514 this.el.addClass("x-tabs-bottom");
5516 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5517 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5518 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5520 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5522 if(this.tabPosition != "bottom"){
5523 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5526 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5527 this.el.addClass("x-tabs-top");
5531 this.bodyEl.setStyle("position", "relative");
5534 this.activateDelegate = this.activate.createDelegate(this);
5539 * Fires when the active tab changes
5540 * @param {Roo.TabPanel} this
5541 * @param {Roo.TabPanelItem} activePanel The new active tab
5545 * @event beforetabchange
5546 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5547 * @param {Roo.TabPanel} this
5548 * @param {Object} e Set cancel to true on this object to cancel the tab change
5549 * @param {Roo.TabPanelItem} tab The tab being changed to
5551 "beforetabchange" : true
5554 Roo.EventManager.onWindowResize(this.onResize, this);
5555 this.cpad = this.el.getPadding("lr");
5556 this.hiddenCount = 0;
5559 // toolbar on the tabbar support...
5561 var tcfg = this.toolbar;
5562 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
5563 this.toolbar = new Roo.Toolbar(tcfg);
5565 var tbl = tcfg.container.child('table', true);
5566 tbl.setAttribute('width', '100%');
5573 Roo.TabPanel.superclass.constructor.call(this);
5576 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5578 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5580 tabPosition : "top",
5582 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5584 currentTabWidth : 0,
5586 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5590 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5594 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5596 preferredTabWidth : 175,
5598 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5602 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5604 monitorResize : true,
5606 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
5611 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5612 * @param {String} id The id of the div to use <b>or create</b>
5613 * @param {String} text The text for the tab
5614 * @param {String} content (optional) Content to put in the TabPanelItem body
5615 * @param {Boolean} closable (optional) True to create a close icon on the tab
5616 * @return {Roo.TabPanelItem} The created TabPanelItem
5618 addTab : function(id, text, content, closable){
5619 var item = new Roo.TabPanelItem(this, id, text, closable);
5620 this.addTabItem(item);
5622 item.setContent(content);
5628 * Returns the {@link Roo.TabPanelItem} with the specified id/index
5629 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5630 * @return {Roo.TabPanelItem}
5632 getTab : function(id){
5633 return this.items[id];
5637 * Hides the {@link Roo.TabPanelItem} with the specified id/index
5638 * @param {String/Number} id The id or index of the TabPanelItem to hide.
5640 hideTab : function(id){
5641 var t = this.items[id];
5645 this.autoSizeTabs();
5650 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5651 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5653 unhideTab : function(id){
5654 var t = this.items[id];
5658 this.autoSizeTabs();
5663 * Adds an existing {@link Roo.TabPanelItem}.
5664 * @param {Roo.TabPanelItem} item The TabPanelItem to add
5666 addTabItem : function(item){
5667 this.items[item.id] = item;
5668 this.items.push(item);
5669 if(this.resizeTabs){
5670 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5671 this.autoSizeTabs();
5678 * Removes a {@link Roo.TabPanelItem}.
5679 * @param {String/Number} id The id or index of the TabPanelItem to remove.
5681 removeTab : function(id){
5682 var items = this.items;
5683 var tab = items[id];
5684 if(!tab) { return; }
5685 var index = items.indexOf(tab);
5686 if(this.active == tab && items.length > 1){
5687 var newTab = this.getNextAvailable(index);
5692 this.stripEl.dom.removeChild(tab.pnode.dom);
5693 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5694 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5696 items.splice(index, 1);
5697 delete this.items[tab.id];
5698 tab.fireEvent("close", tab);
5699 tab.purgeListeners();
5700 this.autoSizeTabs();
5703 getNextAvailable : function(start){
5704 var items = this.items;
5706 // look for a next tab that will slide over to
5707 // replace the one being removed
5708 while(index < items.length){
5709 var item = items[++index];
5710 if(item && !item.isHidden()){
5714 // if one isn't found select the previous tab (on the left)
5717 var item = items[--index];
5718 if(item && !item.isHidden()){
5726 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5727 * @param {String/Number} id The id or index of the TabPanelItem to disable.
5729 disableTab : function(id){
5730 var tab = this.items[id];
5731 if(tab && this.active != tab){
5737 * Enables a {@link Roo.TabPanelItem} that is disabled.
5738 * @param {String/Number} id The id or index of the TabPanelItem to enable.
5740 enableTab : function(id){
5741 var tab = this.items[id];
5746 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5747 * @param {String/Number} id The id or index of the TabPanelItem to activate.
5748 * @return {Roo.TabPanelItem} The TabPanelItem.
5750 activate : function(id){
5751 var tab = this.items[id];
5755 if(tab == this.active || tab.disabled){
5759 this.fireEvent("beforetabchange", this, e, tab);
5760 if(e.cancel !== true && !tab.disabled){
5764 this.active = this.items[id];
5766 this.fireEvent("tabchange", this, this.active);
5772 * Gets the active {@link Roo.TabPanelItem}.
5773 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5775 getActiveTab : function(){
5780 * Updates the tab body element to fit the height of the container element
5781 * for overflow scrolling
5782 * @param {Number} targetHeight (optional) Override the starting height from the elements height
5784 syncHeight : function(targetHeight){
5785 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5786 var bm = this.bodyEl.getMargins();
5787 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5788 this.bodyEl.setHeight(newHeight);
5792 onResize : function(){
5793 if(this.monitorResize){
5794 this.autoSizeTabs();
5799 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5801 beginUpdate : function(){
5802 this.updating = true;
5806 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5808 endUpdate : function(){
5809 this.updating = false;
5810 this.autoSizeTabs();
5814 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5816 autoSizeTabs : function(){
5817 var count = this.items.length;
5818 var vcount = count - this.hiddenCount;
5819 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5822 var w = Math.max(this.el.getWidth() - this.cpad, 10);
5823 var availWidth = Math.floor(w / vcount);
5824 var b = this.stripBody;
5825 if(b.getWidth() > w){
5826 var tabs = this.items;
5827 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5828 if(availWidth < this.minTabWidth){
5829 /*if(!this.sleft){ // incomplete scrolling code
5830 this.createScrollButtons();
5833 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5836 if(this.currentTabWidth < this.preferredTabWidth){
5837 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5843 * Returns the number of tabs in this TabPanel.
5846 getCount : function(){
5847 return this.items.length;
5851 * Resizes all the tabs to the passed width
5852 * @param {Number} The new width
5854 setTabWidth : function(width){
5855 this.currentTabWidth = width;
5856 for(var i = 0, len = this.items.length; i < len; i++) {
5857 if(!this.items[i].isHidden()) {
5858 this.items[i].setWidth(width);
5864 * Destroys this TabPanel
5865 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5867 destroy : function(removeEl){
5868 Roo.EventManager.removeResizeListener(this.onResize, this);
5869 for(var i = 0, len = this.items.length; i < len; i++){
5870 this.items[i].purgeListeners();
5872 if(removeEl === true){
5880 * @class Roo.TabPanelItem
5881 * @extends Roo.util.Observable
5882 * Represents an individual item (tab plus body) in a TabPanel.
5883 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5884 * @param {String} id The id of this TabPanelItem
5885 * @param {String} text The text for the tab of this TabPanelItem
5886 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5888 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5890 * The {@link Roo.TabPanel} this TabPanelItem belongs to
5891 * @type Roo.TabPanel
5893 this.tabPanel = tabPanel;
5895 * The id for this TabPanelItem
5900 this.disabled = false;
5904 this.loaded = false;
5905 this.closable = closable;
5908 * The body element for this TabPanelItem.
5911 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5912 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5913 this.bodyEl.setStyle("display", "block");
5914 this.bodyEl.setStyle("zoom", "1");
5917 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5919 this.el = Roo.get(els.el, true);
5920 this.inner = Roo.get(els.inner, true);
5921 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5922 this.pnode = Roo.get(els.el.parentNode, true);
5923 this.el.on("mousedown", this.onTabMouseDown, this);
5924 this.el.on("click", this.onTabClick, this);
5927 var c = Roo.get(els.close, true);
5928 c.dom.title = this.closeText;
5929 c.addClassOnOver("close-over");
5930 c.on("click", this.closeClick, this);
5936 * Fires when this tab becomes the active tab.
5937 * @param {Roo.TabPanel} tabPanel The parent TabPanel
5938 * @param {Roo.TabPanelItem} this
5942 * @event beforeclose
5943 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5944 * @param {Roo.TabPanelItem} this
5945 * @param {Object} e Set cancel to true on this object to cancel the close.
5947 "beforeclose": true,
5950 * Fires when this tab is closed.
5951 * @param {Roo.TabPanelItem} this
5956 * Fires when this tab is no longer the active tab.
5957 * @param {Roo.TabPanel} tabPanel The parent TabPanel
5958 * @param {Roo.TabPanelItem} this
5962 this.hidden = false;
5964 Roo.TabPanelItem.superclass.constructor.call(this);
5967 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5968 purgeListeners : function(){
5969 Roo.util.Observable.prototype.purgeListeners.call(this);
5970 this.el.removeAllListeners();
5973 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5976 this.pnode.addClass("on");
5979 this.tabPanel.stripWrap.repaint();
5981 this.fireEvent("activate", this.tabPanel, this);
5985 * Returns true if this tab is the active tab.
5988 isActive : function(){
5989 return this.tabPanel.getActiveTab() == this;
5993 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
5996 this.pnode.removeClass("on");
5998 this.fireEvent("deactivate", this.tabPanel, this);
6001 hideAction : function(){
6003 this.bodyEl.setStyle("position", "absolute");
6004 this.bodyEl.setLeft("-20000px");
6005 this.bodyEl.setTop("-20000px");
6008 showAction : function(){
6009 this.bodyEl.setStyle("position", "relative");
6010 this.bodyEl.setTop("");
6011 this.bodyEl.setLeft("");
6016 * Set the tooltip for the tab.
6017 * @param {String} tooltip The tab's tooltip
6019 setTooltip : function(text){
6020 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6021 this.textEl.dom.qtip = text;
6022 this.textEl.dom.removeAttribute('title');
6024 this.textEl.dom.title = text;
6028 onTabClick : function(e){
6030 this.tabPanel.activate(this.id);
6033 onTabMouseDown : function(e){
6035 this.tabPanel.activate(this.id);
6038 getWidth : function(){
6039 return this.inner.getWidth();
6042 setWidth : function(width){
6043 var iwidth = width - this.pnode.getPadding("lr");
6044 this.inner.setWidth(iwidth);
6045 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6046 this.pnode.setWidth(width);
6050 * Show or hide the tab
6051 * @param {Boolean} hidden True to hide or false to show.
6053 setHidden : function(hidden){
6054 this.hidden = hidden;
6055 this.pnode.setStyle("display", hidden ? "none" : "");
6059 * Returns true if this tab is "hidden"
6062 isHidden : function(){
6067 * Returns the text for this tab
6070 getText : function(){
6074 autoSize : function(){
6075 //this.el.beginMeasure();
6076 this.textEl.setWidth(1);
6078 * #2804 [new] Tabs in Roojs
6079 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6081 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6082 //this.el.endMeasure();
6086 * Sets the text for the tab (Note: this also sets the tooltip text)
6087 * @param {String} text The tab's text and tooltip
6089 setText : function(text){
6091 this.textEl.update(text);
6092 this.setTooltip(text);
6093 if(!this.tabPanel.resizeTabs){
6098 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6100 activate : function(){
6101 this.tabPanel.activate(this.id);
6105 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6107 disable : function(){
6108 if(this.tabPanel.active != this){
6109 this.disabled = true;
6110 this.pnode.addClass("disabled");
6115 * Enables this TabPanelItem if it was previously disabled.
6117 enable : function(){
6118 this.disabled = false;
6119 this.pnode.removeClass("disabled");
6123 * Sets the content for this TabPanelItem.
6124 * @param {String} content The content
6125 * @param {Boolean} loadScripts true to look for and load scripts
6127 setContent : function(content, loadScripts){
6128 this.bodyEl.update(content, loadScripts);
6132 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6133 * @return {Roo.UpdateManager} The UpdateManager
6135 getUpdateManager : function(){
6136 return this.bodyEl.getUpdateManager();
6140 * Set a URL to be used to load the content for this TabPanelItem.
6141 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6142 * @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)
6143 * @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)
6144 * @return {Roo.UpdateManager} The UpdateManager
6146 setUrl : function(url, params, loadOnce){
6147 if(this.refreshDelegate){
6148 this.un('activate', this.refreshDelegate);
6150 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6151 this.on("activate", this.refreshDelegate);
6152 return this.bodyEl.getUpdateManager();
6156 _handleRefresh : function(url, params, loadOnce){
6157 if(!loadOnce || !this.loaded){
6158 var updater = this.bodyEl.getUpdateManager();
6159 updater.update(url, params, this._setLoaded.createDelegate(this));
6164 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
6165 * Will fail silently if the setUrl method has not been called.
6166 * This does not activate the panel, just updates its content.
6168 refresh : function(){
6169 if(this.refreshDelegate){
6170 this.loaded = false;
6171 this.refreshDelegate();
6176 _setLoaded : function(){
6181 closeClick : function(e){
6184 this.fireEvent("beforeclose", this, o);
6185 if(o.cancel !== true){
6186 this.tabPanel.removeTab(this.id);
6190 * The text displayed in the tooltip for the close icon.
6193 closeText : "Close this tab"
6197 Roo.TabPanel.prototype.createStrip = function(container){
6198 var strip = document.createElement("div");
6199 strip.className = "x-tabs-wrap";
6200 container.appendChild(strip);
6204 Roo.TabPanel.prototype.createStripList = function(strip){
6205 // div wrapper for retard IE
6206 // returns the "tr" element.
6207 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6208 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6209 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6210 return strip.firstChild.firstChild.firstChild.firstChild;
6213 Roo.TabPanel.prototype.createBody = function(container){
6214 var body = document.createElement("div");
6215 Roo.id(body, "tab-body");
6216 Roo.fly(body).addClass("x-tabs-body");
6217 container.appendChild(body);
6221 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6222 var body = Roo.getDom(id);
6224 body = document.createElement("div");
6227 Roo.fly(body).addClass("x-tabs-item-body");
6228 bodyEl.insertBefore(body, bodyEl.firstChild);
6232 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6233 var td = document.createElement("td");
6234 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6235 //stripEl.appendChild(td);
6237 td.className = "x-tabs-closable";
6239 this.closeTpl = new Roo.Template(
6240 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6241 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6242 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
6245 var el = this.closeTpl.overwrite(td, {"text": text});
6246 var close = el.getElementsByTagName("div")[0];
6247 var inner = el.getElementsByTagName("em")[0];
6248 return {"el": el, "close": close, "inner": inner};
6251 this.tabTpl = new Roo.Template(
6252 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6253 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6256 var el = this.tabTpl.overwrite(td, {"text": text});
6257 var inner = el.getElementsByTagName("em")[0];
6258 return {"el": el, "inner": inner};
6262 * Ext JS Library 1.1.1
6263 * Copyright(c) 2006-2007, Ext JS, LLC.
6265 * Originally Released Under LGPL - original licence link has changed is not relivant.
6268 * <script type="text/javascript">
6273 * @extends Roo.util.Observable
6274 * Simple Button class
6275 * @cfg {String} text The button text
6276 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6277 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6278 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6279 * @cfg {Object} scope The scope of the handler
6280 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6281 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6282 * @cfg {Boolean} hidden True to start hidden (defaults to false)
6283 * @cfg {Boolean} disabled True to start disabled (defaults to false)
6284 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6285 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6286 applies if enableToggle = true)
6287 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6288 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6289 an {@link Roo.util.ClickRepeater} config object (defaults to false).
6291 * Create a new button
6292 * @param {Object} config The config object
6294 Roo.Button = function(renderTo, config)
6298 renderTo = config.renderTo || false;
6301 Roo.apply(this, config);
6305 * Fires when this button is clicked
6306 * @param {Button} this
6307 * @param {EventObject} e The click event
6312 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6313 * @param {Button} this
6314 * @param {Boolean} pressed
6319 * Fires when the mouse hovers over the button
6320 * @param {Button} this
6321 * @param {Event} e The event object
6326 * Fires when the mouse exits the button
6327 * @param {Button} this
6328 * @param {Event} e The event object
6333 * Fires when the button is rendered
6334 * @param {Button} this
6339 this.menu = Roo.menu.MenuMgr.get(this.menu);
6341 // register listeners first!! - so render can be captured..
6342 Roo.util.Observable.call(this);
6344 this.render(renderTo);
6350 Roo.extend(Roo.Button, Roo.util.Observable, {
6356 * Read-only. True if this button is hidden
6361 * Read-only. True if this button is disabled
6366 * Read-only. True if this button is pressed (only if enableToggle = true)
6372 * @cfg {Number} tabIndex
6373 * The DOM tabIndex for this button (defaults to undefined)
6375 tabIndex : undefined,
6378 * @cfg {Boolean} enableToggle
6379 * True to enable pressed/not pressed toggling (defaults to false)
6381 enableToggle: false,
6383 * @cfg {Roo.menu.Menu} menu
6384 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6388 * @cfg {String} menuAlign
6389 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6391 menuAlign : "tl-bl?",
6394 * @cfg {String} iconCls
6395 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6397 iconCls : undefined,
6399 * @cfg {String} type
6400 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
6405 menuClassTarget: 'tr',
6408 * @cfg {String} clickEvent
6409 * The type of event to map to the button's event handler (defaults to 'click')
6411 clickEvent : 'click',
6414 * @cfg {Boolean} handleMouseEvents
6415 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6417 handleMouseEvents : true,
6420 * @cfg {String} tooltipType
6421 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6423 tooltipType : 'qtip',
6427 * A CSS class to apply to the button's main element.
6431 * @cfg {Roo.Template} template (Optional)
6432 * An {@link Roo.Template} with which to create the Button's main element. This Template must
6433 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6434 * require code modifications if required elements (e.g. a button) aren't present.
6438 render : function(renderTo){
6440 if(this.hideParent){
6441 this.parentEl = Roo.get(renderTo);
6445 if(!Roo.Button.buttonTemplate){
6446 // hideous table template
6447 Roo.Button.buttonTemplate = new Roo.Template(
6448 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6449 '<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>',
6450 "</tr></tbody></table>");
6452 this.template = Roo.Button.buttonTemplate;
6454 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
6455 var btnEl = btn.child("button:first");
6456 btnEl.on('focus', this.onFocus, this);
6457 btnEl.on('blur', this.onBlur, this);
6459 btn.addClass(this.cls);
6462 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6465 btnEl.addClass(this.iconCls);
6467 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6470 if(this.tabIndex !== undefined){
6471 btnEl.dom.tabIndex = this.tabIndex;
6474 if(typeof this.tooltip == 'object'){
6475 Roo.QuickTips.tips(Roo.apply({
6479 btnEl.dom[this.tooltipType] = this.tooltip;
6483 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6487 this.el.dom.id = this.el.id = this.id;
6490 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6491 this.menu.on("show", this.onMenuShow, this);
6492 this.menu.on("hide", this.onMenuHide, this);
6494 btn.addClass("x-btn");
6495 if(Roo.isIE && !Roo.isIE7){
6496 this.autoWidth.defer(1, this);
6500 if(this.handleMouseEvents){
6501 btn.on("mouseover", this.onMouseOver, this);
6502 btn.on("mouseout", this.onMouseOut, this);
6503 btn.on("mousedown", this.onMouseDown, this);
6505 btn.on(this.clickEvent, this.onClick, this);
6506 //btn.on("mouseup", this.onMouseUp, this);
6513 Roo.ButtonToggleMgr.register(this);
6515 this.el.addClass("x-btn-pressed");
6518 var repeater = new Roo.util.ClickRepeater(btn,
6519 typeof this.repeat == "object" ? this.repeat : {}
6521 repeater.on("click", this.onClick, this);
6524 this.fireEvent('render', this);
6528 * Returns the button's underlying element
6529 * @return {Roo.Element} The element
6536 * Destroys this Button and removes any listeners.
6538 destroy : function(){
6539 Roo.ButtonToggleMgr.unregister(this);
6540 this.el.removeAllListeners();
6541 this.purgeListeners();
6546 autoWidth : function(){
6548 this.el.setWidth("auto");
6549 if(Roo.isIE7 && Roo.isStrict){
6550 var ib = this.el.child('button');
6551 if(ib && ib.getWidth() > 20){
6553 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6558 this.el.beginMeasure();
6560 if(this.el.getWidth() < this.minWidth){
6561 this.el.setWidth(this.minWidth);
6564 this.el.endMeasure();
6571 * Assigns this button's click handler
6572 * @param {Function} handler The function to call when the button is clicked
6573 * @param {Object} scope (optional) Scope for the function passed in
6575 setHandler : function(handler, scope){
6576 this.handler = handler;
6581 * Sets this button's text
6582 * @param {String} text The button text
6584 setText : function(text){
6587 this.el.child("td.x-btn-center button.x-btn-text").update(text);
6593 * Gets the text for this button
6594 * @return {String} The button text
6596 getText : function(){
6604 this.hidden = false;
6606 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6616 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6621 * Convenience function for boolean show/hide
6622 * @param {Boolean} visible True to show, false to hide
6624 setVisible: function(visible){
6633 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6634 * @param {Boolean} state (optional) Force a particular state
6636 toggle : function(state){
6637 state = state === undefined ? !this.pressed : state;
6638 if(state != this.pressed){
6640 this.el.addClass("x-btn-pressed");
6641 this.pressed = true;
6642 this.fireEvent("toggle", this, true);
6644 this.el.removeClass("x-btn-pressed");
6645 this.pressed = false;
6646 this.fireEvent("toggle", this, false);
6648 if(this.toggleHandler){
6649 this.toggleHandler.call(this.scope || this, this, state);
6658 this.el.child('button:first').focus();
6662 * Disable this button
6664 disable : function(){
6666 this.el.addClass("x-btn-disabled");
6668 this.disabled = true;
6672 * Enable this button
6674 enable : function(){
6676 this.el.removeClass("x-btn-disabled");
6678 this.disabled = false;
6682 * Convenience function for boolean enable/disable
6683 * @param {Boolean} enabled True to enable, false to disable
6685 setDisabled : function(v){
6686 this[v !== true ? "enable" : "disable"]();
6690 onClick : function(e)
6699 if(this.enableToggle){
6702 if(this.menu && !this.menu.isVisible()){
6703 this.menu.show(this.el, this.menuAlign);
6705 this.fireEvent("click", this, e);
6707 this.el.removeClass("x-btn-over");
6708 this.handler.call(this.scope || this, this, e);
6713 onMouseOver : function(e){
6715 this.el.addClass("x-btn-over");
6716 this.fireEvent('mouseover', this, e);
6720 onMouseOut : function(e){
6721 if(!e.within(this.el, true)){
6722 this.el.removeClass("x-btn-over");
6723 this.fireEvent('mouseout', this, e);
6727 onFocus : function(e){
6729 this.el.addClass("x-btn-focus");
6733 onBlur : function(e){
6734 this.el.removeClass("x-btn-focus");
6737 onMouseDown : function(e){
6738 if(!this.disabled && e.button == 0){
6739 this.el.addClass("x-btn-click");
6740 Roo.get(document).on('mouseup', this.onMouseUp, this);
6744 onMouseUp : function(e){
6746 this.el.removeClass("x-btn-click");
6747 Roo.get(document).un('mouseup', this.onMouseUp, this);
6751 onMenuShow : function(e){
6752 this.el.addClass("x-btn-menu-active");
6755 onMenuHide : function(e){
6756 this.el.removeClass("x-btn-menu-active");
6760 // Private utility class used by Button
6761 Roo.ButtonToggleMgr = function(){
6764 function toggleGroup(btn, state){
6766 var g = groups[btn.toggleGroup];
6767 for(var i = 0, l = g.length; i < l; i++){
6776 register : function(btn){
6777 if(!btn.toggleGroup){
6780 var g = groups[btn.toggleGroup];
6782 g = groups[btn.toggleGroup] = [];
6785 btn.on("toggle", toggleGroup);
6788 unregister : function(btn){
6789 if(!btn.toggleGroup){
6792 var g = groups[btn.toggleGroup];
6795 btn.un("toggle", toggleGroup);
6801 * Ext JS Library 1.1.1
6802 * Copyright(c) 2006-2007, Ext JS, LLC.
6804 * Originally Released Under LGPL - original licence link has changed is not relivant.
6807 * <script type="text/javascript">
6811 * @class Roo.SplitButton
6812 * @extends Roo.Button
6813 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6814 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
6815 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6816 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6817 * @cfg {String} arrowTooltip The title attribute of the arrow
6819 * Create a new menu button
6820 * @param {String/HTMLElement/Element} renderTo The element to append the button to
6821 * @param {Object} config The config object
6823 Roo.SplitButton = function(renderTo, config){
6824 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6827 * Fires when this button's arrow is clicked
6828 * @param {SplitButton} this
6829 * @param {EventObject} e The click event
6831 this.addEvents({"arrowclick":true});
6834 Roo.extend(Roo.SplitButton, Roo.Button, {
6835 render : function(renderTo){
6836 // this is one sweet looking template!
6837 var tpl = new Roo.Template(
6838 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6839 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6840 '<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>',
6841 "</tbody></table></td><td>",
6842 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6843 '<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>',
6844 "</tbody></table></td></tr></table>"
6846 var btn = tpl.append(renderTo, [this.text, this.type], true);
6847 var btnEl = btn.child("button");
6849 btn.addClass(this.cls);
6852 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6855 btnEl.addClass(this.iconCls);
6857 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6861 if(this.handleMouseEvents){
6862 btn.on("mouseover", this.onMouseOver, this);
6863 btn.on("mouseout", this.onMouseOut, this);
6864 btn.on("mousedown", this.onMouseDown, this);
6865 btn.on("mouseup", this.onMouseUp, this);
6867 btn.on(this.clickEvent, this.onClick, this);
6869 if(typeof this.tooltip == 'object'){
6870 Roo.QuickTips.tips(Roo.apply({
6874 btnEl.dom[this.tooltipType] = this.tooltip;
6877 if(this.arrowTooltip){
6878 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6887 this.el.addClass("x-btn-pressed");
6889 if(Roo.isIE && !Roo.isIE7){
6890 this.autoWidth.defer(1, this);
6895 this.menu.on("show", this.onMenuShow, this);
6896 this.menu.on("hide", this.onMenuHide, this);
6898 this.fireEvent('render', this);
6902 autoWidth : function(){
6904 var tbl = this.el.child("table:first");
6905 var tbl2 = this.el.child("table:last");
6906 this.el.setWidth("auto");
6907 tbl.setWidth("auto");
6908 if(Roo.isIE7 && Roo.isStrict){
6909 var ib = this.el.child('button:first');
6910 if(ib && ib.getWidth() > 20){
6912 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6917 this.el.beginMeasure();
6919 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6920 tbl.setWidth(this.minWidth-tbl2.getWidth());
6923 this.el.endMeasure();
6926 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6930 * Sets this button's click handler
6931 * @param {Function} handler The function to call when the button is clicked
6932 * @param {Object} scope (optional) Scope for the function passed above
6934 setHandler : function(handler, scope){
6935 this.handler = handler;
6940 * Sets this button's arrow click handler
6941 * @param {Function} handler The function to call when the arrow is clicked
6942 * @param {Object} scope (optional) Scope for the function passed above
6944 setArrowHandler : function(handler, scope){
6945 this.arrowHandler = handler;
6954 this.el.child("button:first").focus();
6959 onClick : function(e){
6962 if(e.getTarget(".x-btn-menu-arrow-wrap")){
6963 if(this.menu && !this.menu.isVisible()){
6964 this.menu.show(this.el, this.menuAlign);
6966 this.fireEvent("arrowclick", this, e);
6967 if(this.arrowHandler){
6968 this.arrowHandler.call(this.scope || this, this, e);
6971 this.fireEvent("click", this, e);
6973 this.handler.call(this.scope || this, this, e);
6979 onMouseDown : function(e){
6981 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6985 onMouseUp : function(e){
6986 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
6992 Roo.MenuButton = Roo.SplitButton;/*
6994 * Ext JS Library 1.1.1
6995 * Copyright(c) 2006-2007, Ext JS, LLC.
6997 * Originally Released Under LGPL - original licence link has changed is not relivant.
7000 * <script type="text/javascript">
7004 * @class Roo.Toolbar
7005 * @children Roo.Toolbar.Item Roo.form.Field
7006 * Basic Toolbar class.
7008 * Creates a new Toolbar
7009 * @param {Object} container The config object
7011 Roo.Toolbar = function(container, buttons, config)
7013 /// old consturctor format still supported..
7014 if(container instanceof Array){ // omit the container for later rendering
7015 buttons = container;
7019 if (typeof(container) == 'object' && container.xtype) {
7021 container = config.container;
7022 buttons = config.buttons || []; // not really - use items!!
7025 if (config && config.items) {
7026 xitems = config.items;
7027 delete config.items;
7029 Roo.apply(this, config);
7030 this.buttons = buttons;
7033 this.render(container);
7035 this.xitems = xitems;
7036 Roo.each(xitems, function(b) {
7042 Roo.Toolbar.prototype = {
7044 * @cfg {Array} items
7045 * array of button configs or elements to add (will be converted to a MixedCollection)
7049 * @cfg {String/HTMLElement/Element} container
7050 * The id or element that will contain the toolbar
7053 render : function(ct){
7054 this.el = Roo.get(ct);
7056 this.el.addClass(this.cls);
7058 // using a table allows for vertical alignment
7059 // 100% width is needed by Safari...
7060 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7061 this.tr = this.el.child("tr", true);
7063 this.items = new Roo.util.MixedCollection(false, function(o){
7064 return o.id || ("item" + (++autoId));
7067 this.add.apply(this, this.buttons);
7068 delete this.buttons;
7073 * Adds element(s) to the toolbar -- this function takes a variable number of
7074 * arguments of mixed type and adds them to the toolbar.
7075 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7077 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7078 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7079 * <li>Field: Any form field (equivalent to {@link #addField})</li>
7080 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7081 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7082 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7083 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7084 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7085 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7087 * @param {Mixed} arg2
7088 * @param {Mixed} etc.
7091 var a = arguments, l = a.length;
7092 for(var i = 0; i < l; i++){
7097 _add : function(el) {
7100 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7103 if (el.applyTo){ // some kind of form field
7104 return this.addField(el);
7106 if (el.render){ // some kind of Toolbar.Item
7107 return this.addItem(el);
7109 if (typeof el == "string"){ // string
7110 if(el == "separator" || el == "-"){
7111 return this.addSeparator();
7114 return this.addSpacer();
7117 return this.addFill();
7119 return this.addText(el);
7122 if(el.tagName){ // element
7123 return this.addElement(el);
7125 if(typeof el == "object"){ // must be button config?
7126 return this.addButton(el);
7134 * Add an Xtype element
7135 * @param {Object} xtype Xtype Object
7136 * @return {Object} created Object
7138 addxtype : function(e){
7143 * Returns the Element for this toolbar.
7144 * @return {Roo.Element}
7152 * @return {Roo.Toolbar.Item} The separator item
7154 addSeparator : function(){
7155 return this.addItem(new Roo.Toolbar.Separator());
7159 * Adds a spacer element
7160 * @return {Roo.Toolbar.Spacer} The spacer item
7162 addSpacer : function(){
7163 return this.addItem(new Roo.Toolbar.Spacer());
7167 * Adds a fill element that forces subsequent additions to the right side of the toolbar
7168 * @return {Roo.Toolbar.Fill} The fill item
7170 addFill : function(){
7171 return this.addItem(new Roo.Toolbar.Fill());
7175 * Adds any standard HTML element to the toolbar
7176 * @param {String/HTMLElement/Element} el The element or id of the element to add
7177 * @return {Roo.Toolbar.Item} The element's item
7179 addElement : function(el){
7180 return this.addItem(new Roo.Toolbar.Item(el));
7183 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7184 * @type Roo.util.MixedCollection
7189 * Adds any Toolbar.Item or subclass
7190 * @param {Roo.Toolbar.Item} item
7191 * @return {Roo.Toolbar.Item} The item
7193 addItem : function(item){
7194 var td = this.nextBlock();
7196 this.items.add(item);
7201 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7202 * @param {Object/Array} config A button config or array of configs
7203 * @return {Roo.Toolbar.Button/Array}
7205 addButton : function(config){
7206 if(config instanceof Array){
7208 for(var i = 0, len = config.length; i < len; i++) {
7209 buttons.push(this.addButton(config[i]));
7214 if(!(config instanceof Roo.Toolbar.Button)){
7216 new Roo.Toolbar.SplitButton(config) :
7217 new Roo.Toolbar.Button(config);
7219 var td = this.nextBlock();
7226 * Adds text to the toolbar
7227 * @param {String} text The text to add
7228 * @return {Roo.Toolbar.Item} The element's item
7230 addText : function(text){
7231 return this.addItem(new Roo.Toolbar.TextItem(text));
7235 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7236 * @param {Number} index The index where the item is to be inserted
7237 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7238 * @return {Roo.Toolbar.Button/Item}
7240 insertButton : function(index, item){
7241 if(item instanceof Array){
7243 for(var i = 0, len = item.length; i < len; i++) {
7244 buttons.push(this.insertButton(index + i, item[i]));
7248 if (!(item instanceof Roo.Toolbar.Button)){
7249 item = new Roo.Toolbar.Button(item);
7251 var td = document.createElement("td");
7252 this.tr.insertBefore(td, this.tr.childNodes[index]);
7254 this.items.insert(index, item);
7259 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7260 * @param {Object} config
7261 * @return {Roo.Toolbar.Item} The element's item
7263 addDom : function(config, returnEl){
7264 var td = this.nextBlock();
7265 Roo.DomHelper.overwrite(td, config);
7266 var ti = new Roo.Toolbar.Item(td.firstChild);
7273 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7274 * @type Roo.util.MixedCollection
7279 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7280 * Note: the field should not have been rendered yet. For a field that has already been
7281 * rendered, use {@link #addElement}.
7282 * @param {Roo.form.Field} field
7283 * @return {Roo.ToolbarItem}
7287 addField : function(field) {
7290 this.fields = new Roo.util.MixedCollection(false, function(o){
7291 return o.id || ("item" + (++autoId));
7296 var td = this.nextBlock();
7298 var ti = new Roo.Toolbar.Item(td.firstChild);
7301 this.fields.add(field);
7312 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7313 this.el.child('div').hide();
7321 this.el.child('div').show();
7325 nextBlock : function(){
7326 var td = document.createElement("td");
7327 this.tr.appendChild(td);
7332 destroy : function(){
7333 if(this.items){ // rendered?
7334 Roo.destroy.apply(Roo, this.items.items);
7336 if(this.fields){ // rendered?
7337 Roo.destroy.apply(Roo, this.fields.items);
7339 Roo.Element.uncache(this.el, this.tr);
7344 * @class Roo.Toolbar.Item
7345 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7347 * Creates a new Item
7348 * @param {HTMLElement} el
7350 Roo.Toolbar.Item = function(el){
7352 if (typeof (el.xtype) != 'undefined') {
7357 this.el = Roo.getDom(el);
7358 this.id = Roo.id(this.el);
7359 this.hidden = false;
7364 * Fires when the button is rendered
7365 * @param {Button} this
7369 Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7371 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7372 //Roo.Toolbar.Item.prototype = {
7375 * Get this item's HTML Element
7376 * @return {HTMLElement}
7383 render : function(td){
7386 td.appendChild(this.el);
7388 this.fireEvent('render', this);
7392 * Removes and destroys this item.
7394 destroy : function(){
7395 this.td.parentNode.removeChild(this.td);
7402 this.hidden = false;
7403 this.td.style.display = "";
7411 this.td.style.display = "none";
7415 * Convenience function for boolean show/hide.
7416 * @param {Boolean} visible true to show/false to hide
7418 setVisible: function(visible){
7427 * Try to focus this item.
7430 Roo.fly(this.el).focus();
7434 * Disables this item.
7436 disable : function(){
7437 Roo.fly(this.td).addClass("x-item-disabled");
7438 this.disabled = true;
7439 this.el.disabled = true;
7443 * Enables this item.
7445 enable : function(){
7446 Roo.fly(this.td).removeClass("x-item-disabled");
7447 this.disabled = false;
7448 this.el.disabled = false;
7454 * @class Roo.Toolbar.Separator
7455 * @extends Roo.Toolbar.Item
7456 * A simple toolbar separator class
7458 * Creates a new Separator
7460 Roo.Toolbar.Separator = function(cfg){
7462 var s = document.createElement("span");
7463 s.className = "ytb-sep";
7468 Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7470 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7472 disable:Roo.emptyFn,
7477 * @class Roo.Toolbar.Spacer
7478 * @extends Roo.Toolbar.Item
7479 * A simple element that adds extra horizontal space to a toolbar.
7481 * Creates a new Spacer
7483 Roo.Toolbar.Spacer = function(cfg){
7484 var s = document.createElement("div");
7485 s.className = "ytb-spacer";
7489 Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7491 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7493 disable:Roo.emptyFn,
7498 * @class Roo.Toolbar.Fill
7499 * @extends Roo.Toolbar.Spacer
7500 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7502 * Creates a new Spacer
7504 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7506 render : function(td){
7507 td.style.width = '100%';
7508 Roo.Toolbar.Fill.superclass.render.call(this, td);
7513 * @class Roo.Toolbar.TextItem
7514 * @extends Roo.Toolbar.Item
7515 * A simple class that renders text directly into a toolbar.
7517 * Creates a new TextItem
7518 * @cfg {string} text
7520 Roo.Toolbar.TextItem = function(cfg){
7521 var text = cfg || "";
7522 if (typeof(cfg) == 'object') {
7523 text = cfg.text || "";
7527 var s = document.createElement("span");
7528 s.className = "ytb-text";
7534 Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg || s);
7536 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7540 disable:Roo.emptyFn,
7545 * @class Roo.Toolbar.Button
7546 * @extends Roo.Button
7547 * A button that renders into a toolbar.
7549 * Creates a new Button
7550 * @param {Object} config A standard {@link Roo.Button} config object
7552 Roo.Toolbar.Button = function(config){
7553 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7555 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7559 render : function(td){
7561 Roo.Toolbar.Button.superclass.render.call(this, td);
7565 * Removes and destroys this button
7567 destroy : function(){
7568 Roo.Toolbar.Button.superclass.destroy.call(this);
7569 this.td.parentNode.removeChild(this.td);
7576 this.hidden = false;
7577 this.td.style.display = "";
7585 this.td.style.display = "none";
7589 * Disables this item
7591 disable : function(){
7592 Roo.fly(this.td).addClass("x-item-disabled");
7593 this.disabled = true;
7599 enable : function(){
7600 Roo.fly(this.td).removeClass("x-item-disabled");
7601 this.disabled = false;
7605 Roo.ToolbarButton = Roo.Toolbar.Button;
7608 * @class Roo.Toolbar.SplitButton
7609 * @extends Roo.SplitButton
7610 * A menu button that renders into a toolbar.
7612 * Creates a new SplitButton
7613 * @param {Object} config A standard {@link Roo.SplitButton} config object
7615 Roo.Toolbar.SplitButton = function(config){
7616 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7618 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7619 render : function(td){
7621 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7625 * Removes and destroys this button
7627 destroy : function(){
7628 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7629 this.td.parentNode.removeChild(this.td);
7636 this.hidden = false;
7637 this.td.style.display = "";
7645 this.td.style.display = "none";
7650 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7652 * Ext JS Library 1.1.1
7653 * Copyright(c) 2006-2007, Ext JS, LLC.
7655 * Originally Released Under LGPL - original licence link has changed is not relivant.
7658 * <script type="text/javascript">
7662 * @class Roo.PagingToolbar
7663 * @extends Roo.Toolbar
7664 * @children Roo.Toolbar.Item Roo.form.Field
7665 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7667 * Create a new PagingToolbar
7668 * @param {Object} config The config object
7670 Roo.PagingToolbar = function(el, ds, config)
7672 // old args format still supported... - xtype is prefered..
7673 if (typeof(el) == 'object' && el.xtype) {
7674 // created from xtype...
7677 el = config.container;
7681 items = config.items;
7685 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7688 this.renderButtons(this.el);
7691 // supprot items array.
7693 Roo.each(items, function(e) {
7694 this.add(Roo.factory(e));
7699 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7702 * @cfg {String/HTMLElement/Element} container
7703 * container The id or element that will contain the toolbar
7706 * @cfg {Boolean} displayInfo
7707 * True to display the displayMsg (defaults to false)
7712 * @cfg {Number} pageSize
7713 * The number of records to display per page (defaults to 20)
7717 * @cfg {String} displayMsg
7718 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7720 displayMsg : 'Displaying {0} - {1} of {2}',
7722 * @cfg {String} emptyMsg
7723 * The message to display when no records are found (defaults to "No data to display")
7725 emptyMsg : 'No data to display',
7727 * Customizable piece of the default paging text (defaults to "Page")
7730 beforePageText : "Page",
7732 * Customizable piece of the default paging text (defaults to "of %0")
7735 afterPageText : "of {0}",
7737 * Customizable piece of the default paging text (defaults to "First Page")
7740 firstText : "First Page",
7742 * Customizable piece of the default paging text (defaults to "Previous Page")
7745 prevText : "Previous Page",
7747 * Customizable piece of the default paging text (defaults to "Next Page")
7750 nextText : "Next Page",
7752 * Customizable piece of the default paging text (defaults to "Last Page")
7755 lastText : "Last Page",
7757 * Customizable piece of the default paging text (defaults to "Refresh")
7760 refreshText : "Refresh",
7763 renderButtons : function(el){
7764 Roo.PagingToolbar.superclass.render.call(this, el);
7765 this.first = this.addButton({
7766 tooltip: this.firstText,
7767 cls: "x-btn-icon x-grid-page-first",
7769 handler: this.onClick.createDelegate(this, ["first"])
7771 this.prev = this.addButton({
7772 tooltip: this.prevText,
7773 cls: "x-btn-icon x-grid-page-prev",
7775 handler: this.onClick.createDelegate(this, ["prev"])
7777 //this.addSeparator();
7778 this.add(this.beforePageText);
7779 this.field = Roo.get(this.addDom({
7784 cls: "x-grid-page-number"
7786 this.field.on("keydown", this.onPagingKeydown, this);
7787 this.field.on("focus", function(){this.dom.select();});
7788 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7789 this.field.setHeight(18);
7790 //this.addSeparator();
7791 this.next = this.addButton({
7792 tooltip: this.nextText,
7793 cls: "x-btn-icon x-grid-page-next",
7795 handler: this.onClick.createDelegate(this, ["next"])
7797 this.last = this.addButton({
7798 tooltip: this.lastText,
7799 cls: "x-btn-icon x-grid-page-last",
7801 handler: this.onClick.createDelegate(this, ["last"])
7803 //this.addSeparator();
7804 this.loading = this.addButton({
7805 tooltip: this.refreshText,
7806 cls: "x-btn-icon x-grid-loading",
7807 handler: this.onClick.createDelegate(this, ["refresh"])
7810 if(this.displayInfo){
7811 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7816 updateInfo : function(){
7818 var count = this.ds.getCount();
7819 var msg = count == 0 ?
7823 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
7825 this.displayEl.update(msg);
7830 onLoad : function(ds, r, o){
7831 this.cursor = o.params ? o.params.start : 0;
7832 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7834 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7835 this.field.dom.value = ap;
7836 this.first.setDisabled(ap == 1);
7837 this.prev.setDisabled(ap == 1);
7838 this.next.setDisabled(ap == ps);
7839 this.last.setDisabled(ap == ps);
7840 this.loading.enable();
7845 getPageData : function(){
7846 var total = this.ds.getTotalCount();
7849 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7850 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7855 onLoadError : function(){
7856 this.loading.enable();
7860 onPagingKeydown : function(e){
7862 var d = this.getPageData();
7864 var v = this.field.dom.value, pageNum;
7865 if(!v || isNaN(pageNum = parseInt(v, 10))){
7866 this.field.dom.value = d.activePage;
7869 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7870 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7873 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))
7875 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7876 this.field.dom.value = pageNum;
7877 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7880 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7882 var v = this.field.dom.value, pageNum;
7883 var increment = (e.shiftKey) ? 10 : 1;
7884 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7887 if(!v || isNaN(pageNum = parseInt(v, 10))) {
7888 this.field.dom.value = d.activePage;
7891 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7893 this.field.dom.value = parseInt(v, 10) + increment;
7894 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7895 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7902 beforeLoad : function(){
7904 this.loading.disable();
7909 onClick : function(which){
7913 ds.load({params:{start: 0, limit: this.pageSize}});
7916 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7919 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7922 var total = ds.getTotalCount();
7923 var extra = total % this.pageSize;
7924 var lastStart = extra ? (total - extra) : total-this.pageSize;
7925 ds.load({params:{start: lastStart, limit: this.pageSize}});
7928 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7934 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7935 * @param {Roo.data.Store} store The data store to unbind
7937 unbind : function(ds){
7938 ds.un("beforeload", this.beforeLoad, this);
7939 ds.un("load", this.onLoad, this);
7940 ds.un("loadexception", this.onLoadError, this);
7941 ds.un("remove", this.updateInfo, this);
7942 ds.un("add", this.updateInfo, this);
7943 this.ds = undefined;
7947 * Binds the paging toolbar to the specified {@link Roo.data.Store}
7948 * @param {Roo.data.Store} store The data store to bind
7950 bind : function(ds){
7951 ds.on("beforeload", this.beforeLoad, this);
7952 ds.on("load", this.onLoad, this);
7953 ds.on("loadexception", this.onLoadError, this);
7954 ds.on("remove", this.updateInfo, this);
7955 ds.on("add", this.updateInfo, this);
7960 * Ext JS Library 1.1.1
7961 * Copyright(c) 2006-2007, Ext JS, LLC.
7963 * Originally Released Under LGPL - original licence link has changed is not relivant.
7966 * <script type="text/javascript">
7970 * @class Roo.Resizable
7971 * @extends Roo.util.Observable
7972 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
7973 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
7974 * 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
7975 * the element will be wrapped for you automatically.</p>
7976 * <p>Here is the list of valid resize handles:</p>
7979 ------ -------------------
7988 'hd' horizontal drag
7991 * <p>Here's an example showing the creation of a typical Resizable:</p>
7993 var resizer = new Roo.Resizable("element-id", {
8001 resizer.on("resize", myHandler);
8003 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8004 * resizer.east.setDisplayed(false);</p>
8005 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8006 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8007 * resize operation's new size (defaults to [0, 0])
8008 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8009 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8010 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8011 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8012 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8013 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8014 * @cfg {Number} width The width of the element in pixels (defaults to null)
8015 * @cfg {Number} height The height of the element in pixels (defaults to null)
8016 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8017 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8018 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8019 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8020 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
8021 * in favor of the handles config option (defaults to false)
8022 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8023 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8024 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8025 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8026 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8027 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8028 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8029 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8030 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8031 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8032 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8034 * Create a new resizable component
8035 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8036 * @param {Object} config configuration options
8038 Roo.Resizable = function(el, config)
8040 this.el = Roo.get(el);
8042 if(config && config.wrap){
8043 config.resizeChild = this.el;
8044 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8045 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8046 this.el.setStyle("overflow", "hidden");
8047 this.el.setPositioning(config.resizeChild.getPositioning());
8048 config.resizeChild.clearPositioning();
8049 if(!config.width || !config.height){
8050 var csize = config.resizeChild.getSize();
8051 this.el.setSize(csize.width, csize.height);
8053 if(config.pinned && !config.adjustments){
8054 config.adjustments = "auto";
8058 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8059 this.proxy.unselectable();
8060 this.proxy.enableDisplayMode('block');
8062 Roo.apply(this, config);
8065 this.disableTrackOver = true;
8066 this.el.addClass("x-resizable-pinned");
8068 // if the element isn't positioned, make it relative
8069 var position = this.el.getStyle("position");
8070 if(position != "absolute" && position != "fixed"){
8071 this.el.setStyle("position", "relative");
8073 if(!this.handles){ // no handles passed, must be legacy style
8074 this.handles = 's,e,se';
8075 if(this.multiDirectional){
8076 this.handles += ',n,w';
8079 if(this.handles == "all"){
8080 this.handles = "n s e w ne nw se sw";
8082 var hs = this.handles.split(/\s*?[,;]\s*?| /);
8083 var ps = Roo.Resizable.positions;
8084 for(var i = 0, len = hs.length; i < len; i++){
8085 if(hs[i] && ps[hs[i]]){
8086 var pos = ps[hs[i]];
8087 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8091 this.corner = this.southeast;
8093 // updateBox = the box can move..
8094 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8095 this.updateBox = true;
8098 this.activeHandle = null;
8100 if(this.resizeChild){
8101 if(typeof this.resizeChild == "boolean"){
8102 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8104 this.resizeChild = Roo.get(this.resizeChild, true);
8108 if(this.adjustments == "auto"){
8109 var rc = this.resizeChild;
8110 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8111 if(rc && (hw || hn)){
8112 rc.position("relative");
8113 rc.setLeft(hw ? hw.el.getWidth() : 0);
8114 rc.setTop(hn ? hn.el.getHeight() : 0);
8116 this.adjustments = [
8117 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8118 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8123 this.dd = this.dynamic ?
8124 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8125 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8131 * @event beforeresize
8132 * Fired before resize is allowed. Set enabled to false to cancel resize.
8133 * @param {Roo.Resizable} this
8134 * @param {Roo.EventObject} e The mousedown event
8136 "beforeresize" : true,
8140 * @param {Roo.Resizable} this
8141 * @param {Number} x The new x position
8142 * @param {Number} y The new y position
8143 * @param {Number} w The new w width
8144 * @param {Number} h The new h hight
8145 * @param {Roo.EventObject} e The mouseup event
8150 * Fired after a resize.
8151 * @param {Roo.Resizable} this
8152 * @param {Number} width The new width
8153 * @param {Number} height The new height
8154 * @param {Roo.EventObject} e The mouseup event
8159 if(this.width !== null && this.height !== null){
8160 this.resizeTo(this.width, this.height);
8162 this.updateChildSize();
8165 this.el.dom.style.zoom = 1;
8167 Roo.Resizable.superclass.constructor.call(this);
8170 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8171 resizeChild : false,
8172 adjustments : [0, 0],
8182 multiDirectional : false,
8183 disableTrackOver : false,
8184 easing : 'easeOutStrong',
8186 heightIncrement : 0,
8190 preserveRatio : false,
8197 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8199 constrainTo: undefined,
8201 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8203 resizeRegion: undefined,
8207 * Perform a manual resize
8208 * @param {Number} width
8209 * @param {Number} height
8211 resizeTo : function(width, height){
8212 this.el.setSize(width, height);
8213 this.updateChildSize();
8214 this.fireEvent("resize", this, width, height, null);
8218 startSizing : function(e, handle){
8219 this.fireEvent("beforeresize", this, e);
8220 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8223 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
8224 this.overlay.unselectable();
8225 this.overlay.enableDisplayMode("block");
8226 this.overlay.on("mousemove", this.onMouseMove, this);
8227 this.overlay.on("mouseup", this.onMouseUp, this);
8229 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8231 this.resizing = true;
8232 this.startBox = this.el.getBox();
8233 this.startPoint = e.getXY();
8234 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8235 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8237 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8238 this.overlay.show();
8240 if(this.constrainTo) {
8241 var ct = Roo.get(this.constrainTo);
8242 this.resizeRegion = ct.getRegion().adjust(
8243 ct.getFrameWidth('t'),
8244 ct.getFrameWidth('l'),
8245 -ct.getFrameWidth('b'),
8246 -ct.getFrameWidth('r')
8250 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8252 this.proxy.setBox(this.startBox);
8254 this.proxy.setStyle('visibility', 'visible');
8260 onMouseDown : function(handle, e){
8263 this.activeHandle = handle;
8264 this.startSizing(e, handle);
8269 onMouseUp : function(e){
8270 var size = this.resizeElement();
8271 this.resizing = false;
8273 this.overlay.hide();
8275 this.fireEvent("resize", this, size.width, size.height, e);
8279 updateChildSize : function(){
8281 if(this.resizeChild){
8283 var child = this.resizeChild;
8284 var adj = this.adjustments;
8285 if(el.dom.offsetWidth){
8286 var b = el.getSize(true);
8287 child.setSize(b.width+adj[0], b.height+adj[1]);
8289 // Second call here for IE
8290 // The first call enables instant resizing and
8291 // the second call corrects scroll bars if they
8294 setTimeout(function(){
8295 if(el.dom.offsetWidth){
8296 var b = el.getSize(true);
8297 child.setSize(b.width+adj[0], b.height+adj[1]);
8305 snap : function(value, inc, min){
8306 if(!inc || !value) {
8309 var newValue = value;
8310 var m = value % inc;
8313 newValue = value + (inc-m);
8315 newValue = value - m;
8318 return Math.max(min, newValue);
8322 resizeElement : function(){
8323 var box = this.proxy.getBox();
8325 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8327 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8329 this.updateChildSize();
8337 constrain : function(v, diff, m, mx){
8340 }else if(v - diff > mx){
8347 onMouseMove : function(e){
8350 try{// try catch so if something goes wrong the user doesn't get hung
8352 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8356 //var curXY = this.startPoint;
8357 var curSize = this.curSize || this.startBox;
8358 var x = this.startBox.x, y = this.startBox.y;
8360 var w = curSize.width, h = curSize.height;
8362 var mw = this.minWidth, mh = this.minHeight;
8363 var mxw = this.maxWidth, mxh = this.maxHeight;
8364 var wi = this.widthIncrement;
8365 var hi = this.heightIncrement;
8367 var eventXY = e.getXY();
8368 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8369 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8371 var pos = this.activeHandle.position;
8376 w = Math.min(Math.max(mw, w), mxw);
8381 h = Math.min(Math.max(mh, h), mxh);
8386 w = Math.min(Math.max(mw, w), mxw);
8387 h = Math.min(Math.max(mh, h), mxh);
8390 diffY = this.constrain(h, diffY, mh, mxh);
8397 var adiffX = Math.abs(diffX);
8398 var sub = (adiffX % wi); // how much
8399 if (sub > (wi/2)) { // far enough to snap
8400 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8402 // remove difference..
8403 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8407 x = Math.max(this.minX, x);
8410 diffX = this.constrain(w, diffX, mw, mxw);
8416 w = Math.min(Math.max(mw, w), mxw);
8417 diffY = this.constrain(h, diffY, mh, mxh);
8422 diffX = this.constrain(w, diffX, mw, mxw);
8423 diffY = this.constrain(h, diffY, mh, mxh);
8430 diffX = this.constrain(w, diffX, mw, mxw);
8432 h = Math.min(Math.max(mh, h), mxh);
8438 var sw = this.snap(w, wi, mw);
8439 var sh = this.snap(h, hi, mh);
8440 if(sw != w || sh != h){
8463 if(this.preserveRatio){
8468 h = Math.min(Math.max(mh, h), mxh);
8473 w = Math.min(Math.max(mw, w), mxw);
8478 w = Math.min(Math.max(mw, w), mxw);
8484 w = Math.min(Math.max(mw, w), mxw);
8490 h = Math.min(Math.max(mh, h), mxh);
8498 h = Math.min(Math.max(mh, h), mxh);
8508 h = Math.min(Math.max(mh, h), mxh);
8516 if (pos == 'hdrag') {
8519 this.proxy.setBounds(x, y, w, h);
8521 this.resizeElement();
8525 this.fireEvent("resizing", this, x, y, w, h, e);
8529 handleOver : function(){
8531 this.el.addClass("x-resizable-over");
8536 handleOut : function(){
8538 this.el.removeClass("x-resizable-over");
8543 * Returns the element this component is bound to.
8544 * @return {Roo.Element}
8551 * Returns the resizeChild element (or null).
8552 * @return {Roo.Element}
8554 getResizeChild : function(){
8555 return this.resizeChild;
8557 groupHandler : function()
8562 * Destroys this resizable. If the element was wrapped and
8563 * removeEl is not true then the element remains.
8564 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8566 destroy : function(removeEl){
8567 this.proxy.remove();
8569 this.overlay.removeAllListeners();
8570 this.overlay.remove();
8572 var ps = Roo.Resizable.positions;
8574 if(typeof ps[k] != "function" && this[ps[k]]){
8575 var h = this[ps[k]];
8576 h.el.removeAllListeners();
8588 // hash to map config positions to true positions
8589 Roo.Resizable.positions = {
8590 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
8595 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8597 // only initialize the template if resizable is used
8598 var tpl = Roo.DomHelper.createTemplate(
8599 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8602 Roo.Resizable.Handle.prototype.tpl = tpl;
8604 this.position = pos;
8606 // show north drag fro topdra
8607 var handlepos = pos == 'hdrag' ? 'north' : pos;
8609 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8610 if (pos == 'hdrag') {
8611 this.el.setStyle('cursor', 'pointer');
8613 this.el.unselectable();
8615 this.el.setOpacity(0);
8617 this.el.on("mousedown", this.onMouseDown, this);
8618 if(!disableTrackOver){
8619 this.el.on("mouseover", this.onMouseOver, this);
8620 this.el.on("mouseout", this.onMouseOut, this);
8625 Roo.Resizable.Handle.prototype = {
8626 afterResize : function(rz){
8631 onMouseDown : function(e){
8632 this.rz.onMouseDown(this, e);
8635 onMouseOver : function(e){
8636 this.rz.handleOver(this, e);
8639 onMouseOut : function(e){
8640 this.rz.handleOut(this, e);
8644 * Ext JS Library 1.1.1
8645 * Copyright(c) 2006-2007, Ext JS, LLC.
8647 * Originally Released Under LGPL - original licence link has changed is not relivant.
8650 * <script type="text/javascript">
8655 * @extends Roo.Component
8656 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8658 * Create a new Editor
8659 * @param {Roo.form.Field} field The Field object (or descendant)
8660 * @param {Object} config The config object
8662 Roo.Editor = function(field, config){
8663 Roo.Editor.superclass.constructor.call(this, config);
8667 * @event beforestartedit
8668 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
8669 * false from the handler of this event.
8670 * @param {Editor} this
8671 * @param {Roo.Element} boundEl The underlying element bound to this editor
8672 * @param {Mixed} value The field value being set
8674 "beforestartedit" : true,
8677 * Fires when this editor is displayed
8678 * @param {Roo.Element} boundEl The underlying element bound to this editor
8679 * @param {Mixed} value The starting field value
8683 * @event beforecomplete
8684 * Fires after a change has been made to the field, but before the change is reflected in the underlying
8685 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
8686 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8687 * event will not fire since no edit actually occurred.
8688 * @param {Editor} this
8689 * @param {Mixed} value The current field value
8690 * @param {Mixed} startValue The original field value
8692 "beforecomplete" : true,
8695 * Fires after editing is complete and any changed value has been written to the underlying field.
8696 * @param {Editor} this
8697 * @param {Mixed} value The current field value
8698 * @param {Mixed} startValue The original field value
8703 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8704 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8705 * @param {Roo.form.Field} this
8706 * @param {Roo.EventObject} e The event object
8712 Roo.extend(Roo.Editor, Roo.Component, {
8714 * @cfg {Boolean/String} autosize
8715 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8716 * or "height" to adopt the height only (defaults to false)
8719 * @cfg {Boolean} revertInvalid
8720 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8721 * validation fails (defaults to true)
8724 * @cfg {Boolean} ignoreNoChange
8725 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8726 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
8727 * will never be ignored.
8730 * @cfg {Boolean} hideEl
8731 * False to keep the bound element visible while the editor is displayed (defaults to true)
8734 * @cfg {Mixed} value
8735 * The data value of the underlying field (defaults to "")
8739 * @cfg {String} alignment
8740 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8744 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8745 * for bottom-right shadow (defaults to "frame")
8749 * @cfg {Boolean} constrain True to constrain the editor to the viewport
8753 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8755 completeOnEnter : false,
8757 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8759 cancelOnEsc : false,
8761 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8766 onRender : function(ct, position){
8767 this.el = new Roo.Layer({
8768 shadow: this.shadow,
8774 constrain: this.constrain
8776 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8777 if(this.field.msgTarget != 'title'){
8778 this.field.msgTarget = 'qtip';
8780 this.field.render(this.el);
8782 this.field.el.dom.setAttribute('autocomplete', 'off');
8784 this.field.on("specialkey", this.onSpecialKey, this);
8785 if(this.swallowKeys){
8786 this.field.el.swallowEvent(['keydown','keypress']);
8789 this.field.on("blur", this.onBlur, this);
8790 if(this.field.grow){
8791 this.field.on("autosize", this.el.sync, this.el, {delay:1});
8795 onSpecialKey : function(field, e)
8797 //Roo.log('editor onSpecialKey');
8798 if(this.completeOnEnter && e.getKey() == e.ENTER){
8800 this.completeEdit();
8803 // do not fire special key otherwise it might hide close the editor...
8804 if(e.getKey() == e.ENTER){
8807 if(this.cancelOnEsc && e.getKey() == e.ESC){
8811 this.fireEvent('specialkey', field, e);
8816 * Starts the editing process and shows the editor.
8817 * @param {String/HTMLElement/Element} el The element to edit
8818 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8819 * to the innerHTML of el.
8821 startEdit : function(el, value){
8823 this.completeEdit();
8825 this.boundEl = Roo.get(el);
8826 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8828 this.render(this.parentEl || document.body);
8830 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8833 this.startValue = v;
8834 this.field.setValue(v);
8836 var sz = this.boundEl.getSize();
8837 switch(this.autoSize){
8839 this.setSize(sz.width, "");
8842 this.setSize("", sz.height);
8845 this.setSize(sz.width, sz.height);
8848 this.el.alignTo(this.boundEl, this.alignment);
8849 this.editing = true;
8851 Roo.QuickTips.disable();
8857 * Sets the height and width of this editor.
8858 * @param {Number} width The new width
8859 * @param {Number} height The new height
8861 setSize : function(w, h){
8862 this.field.setSize(w, h);
8869 * Realigns the editor to the bound field based on the current alignment config value.
8871 realign : function(){
8872 this.el.alignTo(this.boundEl, this.alignment);
8876 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8877 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8879 completeEdit : function(remainVisible){
8883 var v = this.getValue();
8884 if(this.revertInvalid !== false && !this.field.isValid()){
8885 v = this.startValue;
8886 this.cancelEdit(true);
8888 if(String(v) === String(this.startValue) && this.ignoreNoChange){
8889 this.editing = false;
8893 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8894 this.editing = false;
8895 if(this.updateEl && this.boundEl){
8896 this.boundEl.update(v);
8898 if(remainVisible !== true){
8901 this.fireEvent("complete", this, v, this.startValue);
8906 onShow : function(){
8908 if(this.hideEl !== false){
8909 this.boundEl.hide();
8912 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8913 this.fixIEFocus = true;
8914 this.deferredFocus.defer(50, this);
8918 this.fireEvent("startedit", this.boundEl, this.startValue);
8921 deferredFocus : function(){
8928 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
8929 * reverted to the original starting value.
8930 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8931 * cancel (defaults to false)
8933 cancelEdit : function(remainVisible){
8935 this.setValue(this.startValue);
8936 if(remainVisible !== true){
8943 onBlur : function(){
8944 if(this.allowBlur !== true && this.editing){
8945 this.completeEdit();
8950 onHide : function(){
8952 this.completeEdit();
8956 if(this.field.collapse){
8957 this.field.collapse();
8960 if(this.hideEl !== false){
8961 this.boundEl.show();
8964 Roo.QuickTips.enable();
8969 * Sets the data value of the editor
8970 * @param {Mixed} value Any valid value supported by the underlying field
8972 setValue : function(v){
8973 this.field.setValue(v);
8977 * Gets the data value of the editor
8978 * @return {Mixed} The data value
8980 getValue : function(){
8981 return this.field.getValue();
8985 * Ext JS Library 1.1.1
8986 * Copyright(c) 2006-2007, Ext JS, LLC.
8988 * Originally Released Under LGPL - original licence link has changed is not relivant.
8991 * <script type="text/javascript">
8995 * @class Roo.BasicDialog
8996 * @extends Roo.util.Observable
8997 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
8999 var dlg = new Roo.BasicDialog("my-dlg", {
9008 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9009 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
9010 dlg.addButton('Cancel', dlg.hide, dlg);
9013 <b>A Dialog should always be a direct child of the body element.</b>
9014 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9015 * @cfg {String} title Default text to display in the title bar (defaults to null)
9016 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9017 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9018 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9019 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9020 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9021 * (defaults to null with no animation)
9022 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9023 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9024 * property for valid values (defaults to 'all')
9025 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9026 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9027 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9028 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9029 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9030 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9031 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9032 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9033 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9034 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9035 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9036 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9037 * draggable = true (defaults to false)
9038 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9039 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9040 * shadow (defaults to false)
9041 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9042 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9043 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9044 * @cfg {Array} buttons Array of buttons
9045 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9047 * Create a new BasicDialog.
9048 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9049 * @param {Object} config Configuration options
9051 Roo.BasicDialog = function(el, config){
9052 this.el = Roo.get(el);
9053 var dh = Roo.DomHelper;
9054 if(!this.el && config && config.autoCreate){
9055 if(typeof config.autoCreate == "object"){
9056 if(!config.autoCreate.id){
9057 config.autoCreate.id = el;
9059 this.el = dh.append(document.body,
9060 config.autoCreate, true);
9062 this.el = dh.append(document.body,
9063 {tag: "div", id: el, style:'visibility:hidden;'}, true);
9067 el.setDisplayed(true);
9068 el.hide = this.hideAction;
9070 el.addClass("x-dlg");
9072 Roo.apply(this, config);
9074 this.proxy = el.createProxy("x-dlg-proxy");
9075 this.proxy.hide = this.hideAction;
9076 this.proxy.setOpacity(.5);
9080 el.setWidth(config.width);
9083 el.setHeight(config.height);
9085 this.size = el.getSize();
9086 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9087 this.xy = [config.x,config.y];
9089 this.xy = el.getCenterXY(true);
9091 /** The header element @type Roo.Element */
9092 this.header = el.child("> .x-dlg-hd");
9093 /** The body element @type Roo.Element */
9094 this.body = el.child("> .x-dlg-bd");
9095 /** The footer element @type Roo.Element */
9096 this.footer = el.child("> .x-dlg-ft");
9099 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
9102 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9105 this.header.unselectable();
9107 this.header.update(this.title);
9109 // this element allows the dialog to be focused for keyboard event
9110 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9111 this.focusEl.swallowEvent("click", true);
9113 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9115 // wrap the body and footer for special rendering
9116 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9118 this.bwrap.dom.appendChild(this.footer.dom);
9121 this.bg = this.el.createChild({
9122 tag: "div", cls:"x-dlg-bg",
9123 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
9125 this.centerBg = this.bg.child("div.x-dlg-bg-center");
9128 if(this.autoScroll !== false && !this.autoTabs){
9129 this.body.setStyle("overflow", "auto");
9132 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9134 if(this.closable !== false){
9135 this.el.addClass("x-dlg-closable");
9136 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9137 this.close.on("click", this.closeClick, this);
9138 this.close.addClassOnOver("x-dlg-close-over");
9140 if(this.collapsible !== false){
9141 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9142 this.collapseBtn.on("click", this.collapseClick, this);
9143 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9144 this.header.on("dblclick", this.collapseClick, this);
9146 if(this.resizable !== false){
9147 this.el.addClass("x-dlg-resizable");
9148 this.resizer = new Roo.Resizable(el, {
9149 minWidth: this.minWidth || 80,
9150 minHeight:this.minHeight || 80,
9151 handles: this.resizeHandles || "all",
9154 this.resizer.on("beforeresize", this.beforeResize, this);
9155 this.resizer.on("resize", this.onResize, this);
9157 if(this.draggable !== false){
9158 el.addClass("x-dlg-draggable");
9159 if (!this.proxyDrag) {
9160 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9163 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9165 dd.setHandleElId(this.header.id);
9166 dd.endDrag = this.endMove.createDelegate(this);
9167 dd.startDrag = this.startMove.createDelegate(this);
9168 dd.onDrag = this.onDrag.createDelegate(this);
9173 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9174 this.mask.enableDisplayMode("block");
9176 this.el.addClass("x-dlg-modal");
9179 this.shadow = new Roo.Shadow({
9180 mode : typeof this.shadow == "string" ? this.shadow : "sides",
9181 offset : this.shadowOffset
9184 this.shadowOffset = 0;
9186 if(Roo.useShims && this.shim !== false){
9187 this.shim = this.el.createShim();
9188 this.shim.hide = this.hideAction;
9197 var bts= this.buttons;
9199 Roo.each(bts, function(b) {
9208 * Fires when a key is pressed
9209 * @param {Roo.BasicDialog} this
9210 * @param {Roo.EventObject} e
9215 * Fires when this dialog is moved by the user.
9216 * @param {Roo.BasicDialog} this
9217 * @param {Number} x The new page X
9218 * @param {Number} y The new page Y
9223 * Fires when this dialog is resized by the user.
9224 * @param {Roo.BasicDialog} this
9225 * @param {Number} width The new width
9226 * @param {Number} height The new height
9231 * Fires before this dialog is hidden.
9232 * @param {Roo.BasicDialog} this
9234 "beforehide" : true,
9237 * Fires when this dialog is hidden.
9238 * @param {Roo.BasicDialog} this
9243 * Fires before this dialog is shown.
9244 * @param {Roo.BasicDialog} this
9246 "beforeshow" : true,
9249 * Fires when this dialog is shown.
9250 * @param {Roo.BasicDialog} this
9254 el.on("keydown", this.onKeyDown, this);
9255 el.on("mousedown", this.toFront, this);
9256 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9258 Roo.DialogManager.register(this);
9259 Roo.BasicDialog.superclass.constructor.call(this);
9262 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9263 shadowOffset: Roo.isIE ? 6 : 5,
9267 defaultButton: null,
9268 buttonAlign: "right",
9273 * Sets the dialog title text
9274 * @param {String} text The title text to display
9275 * @return {Roo.BasicDialog} this
9277 setTitle : function(text){
9278 this.header.update(text);
9283 closeClick : function(){
9288 collapseClick : function(){
9289 this[this.collapsed ? "expand" : "collapse"]();
9293 * Collapses the dialog to its minimized state (only the title bar is visible).
9294 * Equivalent to the user clicking the collapse dialog button.
9296 collapse : function(){
9297 if(!this.collapsed){
9298 this.collapsed = true;
9299 this.el.addClass("x-dlg-collapsed");
9300 this.restoreHeight = this.el.getHeight();
9301 this.resizeTo(this.el.getWidth(), this.header.getHeight());
9306 * Expands a collapsed dialog back to its normal state. Equivalent to the user
9307 * clicking the expand dialog button.
9309 expand : function(){
9311 this.collapsed = false;
9312 this.el.removeClass("x-dlg-collapsed");
9313 this.resizeTo(this.el.getWidth(), this.restoreHeight);
9318 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9319 * @return {Roo.TabPanel} The tabs component
9321 initTabs : function(){
9322 var tabs = this.getTabs();
9323 while(tabs.getTab(0)){
9326 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9328 tabs.addTab(Roo.id(dom), dom.title);
9336 beforeResize : function(){
9337 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9341 onResize : function(){
9343 this.syncBodyHeight();
9344 this.adjustAssets();
9346 this.fireEvent("resize", this, this.size.width, this.size.height);
9350 onKeyDown : function(e){
9351 if(this.isVisible()){
9352 this.fireEvent("keydown", this, e);
9357 * Resizes the dialog.
9358 * @param {Number} width
9359 * @param {Number} height
9360 * @return {Roo.BasicDialog} this
9362 resizeTo : function(width, height){
9363 this.el.setSize(width, height);
9364 this.size = {width: width, height: height};
9365 this.syncBodyHeight();
9366 if(this.fixedcenter){
9369 if(this.isVisible()){
9371 this.adjustAssets();
9373 this.fireEvent("resize", this, width, height);
9379 * Resizes the dialog to fit the specified content size.
9380 * @param {Number} width
9381 * @param {Number} height
9382 * @return {Roo.BasicDialog} this
9384 setContentSize : function(w, h){
9385 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9386 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9387 //if(!this.el.isBorderBox()){
9388 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9389 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9392 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9393 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9395 this.resizeTo(w, h);
9400 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
9401 * executed in response to a particular key being pressed while the dialog is active.
9402 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9403 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9404 * @param {Function} fn The function to call
9405 * @param {Object} scope (optional) The scope of the function
9406 * @return {Roo.BasicDialog} this
9408 addKeyListener : function(key, fn, scope){
9409 var keyCode, shift, ctrl, alt;
9410 if(typeof key == "object" && !(key instanceof Array)){
9411 keyCode = key["key"];
9412 shift = key["shift"];
9418 var handler = function(dlg, e){
9419 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
9421 if(keyCode instanceof Array){
9422 for(var i = 0, len = keyCode.length; i < len; i++){
9423 if(keyCode[i] == k){
9424 fn.call(scope || window, dlg, k, e);
9430 fn.call(scope || window, dlg, k, e);
9435 this.on("keydown", handler);
9440 * Returns the TabPanel component (creates it if it doesn't exist).
9441 * Note: If you wish to simply check for the existence of tabs without creating them,
9442 * check for a null 'tabs' property.
9443 * @return {Roo.TabPanel} The tabs component
9445 getTabs : function(){
9447 this.el.addClass("x-dlg-auto-tabs");
9448 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9449 this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9455 * Adds a button to the footer section of the dialog.
9456 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9457 * object or a valid Roo.DomHelper element config
9458 * @param {Function} handler The function called when the button is clicked
9459 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9460 * @return {Roo.Button} The new button
9462 addButton : function(config, handler, scope){
9463 var dh = Roo.DomHelper;
9465 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9467 if(!this.btnContainer){
9468 var tb = this.footer.createChild({
9470 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9471 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9473 this.btnContainer = tb.firstChild.firstChild.firstChild;
9478 minWidth: this.minButtonWidth,
9481 if(typeof config == "string"){
9482 bconfig.text = config;
9485 bconfig.dhconfig = config;
9487 Roo.apply(bconfig, config);
9491 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9492 bconfig.position = Math.max(0, bconfig.position);
9493 fc = this.btnContainer.childNodes[bconfig.position];
9496 var btn = new Roo.Button(
9498 this.btnContainer.insertBefore(document.createElement("td"),fc)
9499 : this.btnContainer.appendChild(document.createElement("td")),
9500 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
9503 this.syncBodyHeight();
9506 * Array of all the buttons that have been added to this dialog via addButton
9511 this.buttons.push(btn);
9516 * Sets the default button to be focused when the dialog is displayed.
9517 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9518 * @return {Roo.BasicDialog} this
9520 setDefaultButton : function(btn){
9521 this.defaultButton = btn;
9526 getHeaderFooterHeight : function(safe){
9529 height += this.header.getHeight();
9532 var fm = this.footer.getMargins();
9533 height += (this.footer.getHeight()+fm.top+fm.bottom);
9535 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9536 height += this.centerBg.getPadding("tb");
9541 syncBodyHeight : function()
9543 var bd = this.body, // the text
9544 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9546 var height = this.size.height - this.getHeaderFooterHeight(false);
9547 bd.setHeight(height-bd.getMargins("tb"));
9548 var hh = this.header.getHeight();
9549 var h = this.size.height-hh;
9552 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9553 bw.setHeight(h-cb.getPadding("tb"));
9555 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9556 bd.setWidth(bw.getWidth(true));
9558 this.tabs.syncHeight();
9560 this.tabs.el.repaint();
9566 * Restores the previous state of the dialog if Roo.state is configured.
9567 * @return {Roo.BasicDialog} this
9569 restoreState : function(){
9570 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9571 if(box && box.width){
9572 this.xy = [box.x, box.y];
9573 this.resizeTo(box.width, box.height);
9579 beforeShow : function(){
9581 if(this.fixedcenter){
9582 this.xy = this.el.getCenterXY(true);
9585 Roo.get(document.body).addClass("x-body-masked");
9586 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9593 animShow : function(){
9594 var b = Roo.get(this.animateTarget).getBox();
9595 this.proxy.setSize(b.width, b.height);
9596 this.proxy.setLocation(b.x, b.y);
9598 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9599 true, .35, this.showEl.createDelegate(this));
9604 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9605 * @return {Roo.BasicDialog} this
9607 show : function(animateTarget){
9608 if (this.fireEvent("beforeshow", this) === false){
9611 if(this.syncHeightBeforeShow){
9612 this.syncBodyHeight();
9613 }else if(this.firstShow){
9614 this.firstShow = false;
9615 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9617 this.animateTarget = animateTarget || this.animateTarget;
9618 if(!this.el.isVisible()){
9620 if(this.animateTarget && Roo.get(this.animateTarget)){
9630 showEl : function(){
9632 this.el.setXY(this.xy);
9634 this.adjustAssets(true);
9637 // IE peekaboo bug - fix found by Dave Fenwick
9641 this.fireEvent("show", this);
9645 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
9646 * dialog itself will receive focus.
9649 if(this.defaultButton){
9650 this.defaultButton.focus();
9652 this.focusEl.focus();
9657 constrainXY : function(){
9658 if(this.constraintoviewport !== false){
9661 var s = this.container.getSize();
9662 this.viewSize = [s.width, s.height];
9664 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9667 var s = Roo.get(this.container||document).getScroll();
9669 var x = this.xy[0], y = this.xy[1];
9670 var w = this.size.width, h = this.size.height;
9671 var vw = this.viewSize[0], vh = this.viewSize[1];
9672 // only move it if it needs it
9674 // first validate right/bottom
9675 if(x + w > vw+s.left){
9679 if(y + h > vh+s.top){
9683 // then make sure top/left isn't negative
9695 if(this.isVisible()){
9696 this.el.setLocation(x, y);
9697 this.adjustAssets();
9704 onDrag : function(){
9705 if(!this.proxyDrag){
9706 this.xy = this.el.getXY();
9707 this.adjustAssets();
9712 adjustAssets : function(doShow){
9713 var x = this.xy[0], y = this.xy[1];
9714 var w = this.size.width, h = this.size.height;
9715 if(doShow === true){
9717 this.shadow.show(this.el);
9723 if(this.shadow && this.shadow.isVisible()){
9724 this.shadow.show(this.el);
9726 if(this.shim && this.shim.isVisible()){
9727 this.shim.setBounds(x, y, w, h);
9732 adjustViewport : function(w, h){
9734 w = Roo.lib.Dom.getViewWidth();
9735 h = Roo.lib.Dom.getViewHeight();
9738 this.viewSize = [w, h];
9739 if(this.modal && this.mask.isVisible()){
9740 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9741 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9743 if(this.isVisible()){
9749 * Destroys this dialog and all its supporting elements (including any tabs, shim,
9750 * shadow, proxy, mask, etc.) Also removes all event listeners.
9751 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9753 destroy : function(removeEl){
9754 if(this.isVisible()){
9755 this.animateTarget = null;
9758 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9760 this.tabs.destroy(removeEl);
9773 for(var i = 0, len = this.buttons.length; i < len; i++){
9774 this.buttons[i].destroy();
9777 this.el.removeAllListeners();
9778 if(removeEl === true){
9782 Roo.DialogManager.unregister(this);
9786 startMove : function(){
9790 if(this.constraintoviewport !== false){
9791 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9796 endMove : function(){
9797 if(!this.proxyDrag){
9798 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9800 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9804 this.adjustAssets();
9806 this.fireEvent("move", this, this.xy[0], this.xy[1]);
9810 * Brings this dialog to the front of any other visible dialogs
9811 * @return {Roo.BasicDialog} this
9813 toFront : function(){
9814 Roo.DialogManager.bringToFront(this);
9819 * Sends this dialog to the back (under) of any other visible dialogs
9820 * @return {Roo.BasicDialog} this
9822 toBack : function(){
9823 Roo.DialogManager.sendToBack(this);
9828 * Centers this dialog in the viewport
9829 * @return {Roo.BasicDialog} this
9831 center : function(){
9832 var xy = this.el.getCenterXY(true);
9833 this.moveTo(xy[0], xy[1]);
9838 * Moves the dialog's top-left corner to the specified point
9841 * @return {Roo.BasicDialog} this
9843 moveTo : function(x, y){
9845 if(this.isVisible()){
9846 this.el.setXY(this.xy);
9847 this.adjustAssets();
9853 * Aligns the dialog to the specified element
9854 * @param {String/HTMLElement/Roo.Element} element The element to align to.
9855 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9856 * @param {Array} offsets (optional) Offset the positioning by [x, y]
9857 * @return {Roo.BasicDialog} this
9859 alignTo : function(element, position, offsets){
9860 this.xy = this.el.getAlignToXY(element, position, offsets);
9861 if(this.isVisible()){
9862 this.el.setXY(this.xy);
9863 this.adjustAssets();
9869 * Anchors an element to another element and realigns it when the window is resized.
9870 * @param {String/HTMLElement/Roo.Element} element The element to align to.
9871 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9872 * @param {Array} offsets (optional) Offset the positioning by [x, y]
9873 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9874 * is a number, it is used as the buffer delay (defaults to 50ms).
9875 * @return {Roo.BasicDialog} this
9877 anchorTo : function(el, alignment, offsets, monitorScroll){
9878 var action = function(){
9879 this.alignTo(el, alignment, offsets);
9881 Roo.EventManager.onWindowResize(action, this);
9882 var tm = typeof monitorScroll;
9883 if(tm != 'undefined'){
9884 Roo.EventManager.on(window, 'scroll', action, this,
9885 {buffer: tm == 'number' ? monitorScroll : 50});
9892 * Returns true if the dialog is visible
9895 isVisible : function(){
9896 return this.el.isVisible();
9900 animHide : function(callback){
9901 var b = Roo.get(this.animateTarget).getBox();
9903 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9905 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9906 this.hideEl.createDelegate(this, [callback]));
9911 * @param {Function} callback (optional) Function to call when the dialog is hidden
9912 * @return {Roo.BasicDialog} this
9914 hide : function(callback){
9915 if (this.fireEvent("beforehide", this) === false){
9924 // sometimes animateTarget seems to get set.. causing problems...
9925 // this just double checks..
9926 if(this.animateTarget && Roo.get(this.animateTarget)) {
9927 this.animHide(callback);
9930 this.hideEl(callback);
9936 hideEl : function(callback){
9940 Roo.get(document.body).removeClass("x-body-masked");
9942 this.fireEvent("hide", this);
9943 if(typeof callback == "function"){
9949 hideAction : function(){
9950 this.setLeft("-10000px");
9951 this.setTop("-10000px");
9952 this.setStyle("visibility", "hidden");
9956 refreshSize : function(){
9957 this.size = this.el.getSize();
9958 this.xy = this.el.getXY();
9959 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9963 // z-index is managed by the DialogManager and may be overwritten at any time
9964 setZIndex : function(index){
9966 this.mask.setStyle("z-index", index);
9969 this.shim.setStyle("z-index", ++index);
9972 this.shadow.setZIndex(++index);
9974 this.el.setStyle("z-index", ++index);
9976 this.proxy.setStyle("z-index", ++index);
9979 this.resizer.proxy.setStyle("z-index", ++index);
9982 this.lastZIndex = index;
9986 * Returns the element for this dialog
9987 * @return {Roo.Element} The underlying dialog Element
9995 * @class Roo.DialogManager
9996 * Provides global access to BasicDialogs that have been created and
9997 * support for z-indexing (layering) multiple open dialogs.
9999 Roo.DialogManager = function(){
10001 var accessList = [];
10005 var sortDialogs = function(d1, d2){
10006 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10010 var orderDialogs = function(){
10011 accessList.sort(sortDialogs);
10012 var seed = Roo.DialogManager.zseed;
10013 for(var i = 0, len = accessList.length; i < len; i++){
10014 var dlg = accessList[i];
10016 dlg.setZIndex(seed + (i*10));
10023 * The starting z-index for BasicDialogs (defaults to 9000)
10024 * @type Number The z-index value
10029 register : function(dlg){
10030 list[dlg.id] = dlg;
10031 accessList.push(dlg);
10035 unregister : function(dlg){
10036 delete list[dlg.id];
10039 if(!accessList.indexOf){
10040 for( i = 0, len = accessList.length; i < len; i++){
10041 if(accessList[i] == dlg){
10042 accessList.splice(i, 1);
10047 i = accessList.indexOf(dlg);
10049 accessList.splice(i, 1);
10055 * Gets a registered dialog by id
10056 * @param {String/Object} id The id of the dialog or a dialog
10057 * @return {Roo.BasicDialog} this
10059 get : function(id){
10060 return typeof id == "object" ? id : list[id];
10064 * Brings the specified dialog to the front
10065 * @param {String/Object} dlg The id of the dialog or a dialog
10066 * @return {Roo.BasicDialog} this
10068 bringToFront : function(dlg){
10069 dlg = this.get(dlg);
10072 dlg._lastAccess = new Date().getTime();
10079 * Sends the specified dialog to the back
10080 * @param {String/Object} dlg The id of the dialog or a dialog
10081 * @return {Roo.BasicDialog} this
10083 sendToBack : function(dlg){
10084 dlg = this.get(dlg);
10085 dlg._lastAccess = -(new Date().getTime());
10091 * Hides all dialogs
10093 hideAll : function(){
10094 for(var id in list){
10095 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10104 * @class Roo.LayoutDialog
10105 * @extends Roo.BasicDialog
10106 * @children Roo.ContentPanel
10107 * @parent builder none
10108 * Dialog which provides adjustments for working with a layout in a Dialog.
10109 * Add your necessary layout config options to the dialog's config.<br>
10110 * Example usage (including a nested layout):
10113 dialog = new Roo.LayoutDialog("download-dlg", {
10122 // layout config merges with the dialog config
10124 tabPosition: "top",
10125 alwaysShowTabs: true
10128 dialog.addKeyListener(27, dialog.hide, dialog);
10129 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10130 dialog.addButton("Build It!", this.getDownload, this);
10132 // we can even add nested layouts
10133 var innerLayout = new Roo.BorderLayout("dl-inner", {
10143 innerLayout.beginUpdate();
10144 innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10145 innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10146 innerLayout.endUpdate(true);
10148 var layout = dialog.getLayout();
10149 layout.beginUpdate();
10150 layout.add("center", new Roo.ContentPanel("standard-panel",
10151 {title: "Download the Source", fitToFrame:true}));
10152 layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10153 {title: "Build your own roo.js"}));
10154 layout.getRegion("center").showPanel(sp);
10155 layout.endUpdate();
10159 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10160 * @param {Object} config configuration options
10162 Roo.LayoutDialog = function(el, cfg){
10165 if (typeof(cfg) == 'undefined') {
10166 config = Roo.apply({}, el);
10167 // not sure why we use documentElement here.. - it should always be body.
10168 // IE7 borks horribly if we use documentElement.
10169 // webkit also does not like documentElement - it creates a body element...
10170 el = Roo.get( document.body || document.documentElement ).createChild();
10171 //config.autoCreate = true;
10175 config.autoTabs = false;
10176 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10177 this.body.setStyle({overflow:"hidden", position:"relative"});
10178 this.layout = new Roo.BorderLayout(this.body.dom, config);
10179 this.layout.monitorWindowResize = false;
10180 this.el.addClass("x-dlg-auto-layout");
10181 // fix case when center region overwrites center function
10182 this.center = Roo.BasicDialog.prototype.center;
10183 this.on("show", this.layout.layout, this.layout, true);
10184 if (config.items) {
10185 var xitems = config.items;
10186 delete config.items;
10187 Roo.each(xitems, this.addxtype, this);
10192 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10196 * @cfg {Roo.LayoutRegion} east
10199 * @cfg {Roo.LayoutRegion} west
10202 * @cfg {Roo.LayoutRegion} south
10205 * @cfg {Roo.LayoutRegion} north
10208 * @cfg {Roo.LayoutRegion} center
10211 * @cfg {Roo.Button} buttons[] Bottom buttons..
10216 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10219 endUpdate : function(){
10220 this.layout.endUpdate();
10224 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10227 beginUpdate : function(){
10228 this.layout.beginUpdate();
10232 * Get the BorderLayout for this dialog
10233 * @return {Roo.BorderLayout}
10235 getLayout : function(){
10236 return this.layout;
10239 showEl : function(){
10240 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10242 this.layout.layout();
10247 // Use the syncHeightBeforeShow config option to control this automatically
10248 syncBodyHeight : function(){
10249 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10250 if(this.layout){this.layout.layout();}
10254 * Add an xtype element (actually adds to the layout.)
10255 * @return {Object} xdata xtype object data.
10258 addxtype : function(c) {
10259 return this.layout.addxtype(c);
10263 * Ext JS Library 1.1.1
10264 * Copyright(c) 2006-2007, Ext JS, LLC.
10266 * Originally Released Under LGPL - original licence link has changed is not relivant.
10269 * <script type="text/javascript">
10273 * @class Roo.MessageBox
10274 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
10278 Roo.Msg.alert('Status', 'Changes saved successfully.');
10280 // Prompt for user data:
10281 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10283 // process text value...
10287 // Show a dialog using config options:
10289 title:'Save Changes?',
10290 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10291 buttons: Roo.Msg.YESNOCANCEL,
10298 Roo.MessageBox = function(){
10299 var dlg, opt, mask, waitTimer;
10300 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10301 var buttons, activeTextEl, bwidth;
10304 var handleButton = function(button){
10306 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10310 var handleHide = function(){
10311 if(opt && opt.cls){
10312 dlg.el.removeClass(opt.cls);
10315 Roo.TaskMgr.stop(waitTimer);
10321 var updateButtons = function(b){
10324 buttons["ok"].hide();
10325 buttons["cancel"].hide();
10326 buttons["yes"].hide();
10327 buttons["no"].hide();
10328 dlg.footer.dom.style.display = 'none';
10331 dlg.footer.dom.style.display = '';
10332 for(var k in buttons){
10333 if(typeof buttons[k] != "function"){
10336 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10337 width += buttons[k].el.getWidth()+15;
10347 var handleEsc = function(d, k, e){
10348 if(opt && opt.closable !== false){
10358 * Returns a reference to the underlying {@link Roo.BasicDialog} element
10359 * @return {Roo.BasicDialog} The BasicDialog element
10361 getDialog : function(){
10363 dlg = new Roo.BasicDialog("x-msg-box", {
10368 constraintoviewport:false,
10370 collapsible : false,
10373 width:400, height:100,
10374 buttonAlign:"center",
10375 closeClick : function(){
10376 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10377 handleButton("no");
10379 handleButton("cancel");
10383 dlg.on("hide", handleHide);
10385 dlg.addKeyListener(27, handleEsc);
10387 var bt = this.buttonText;
10388 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10389 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10390 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10391 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10392 bodyEl = dlg.body.createChild({
10394 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>'
10396 msgEl = bodyEl.dom.firstChild;
10397 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10398 textboxEl.enableDisplayMode();
10399 textboxEl.addKeyListener([10,13], function(){
10400 if(dlg.isVisible() && opt && opt.buttons){
10401 if(opt.buttons.ok){
10402 handleButton("ok");
10403 }else if(opt.buttons.yes){
10404 handleButton("yes");
10408 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10409 textareaEl.enableDisplayMode();
10410 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10411 progressEl.enableDisplayMode();
10412 var pf = progressEl.dom.firstChild;
10414 pp = Roo.get(pf.firstChild);
10415 pp.setHeight(pf.offsetHeight);
10423 * Updates the message box body text
10424 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10425 * the XHTML-compliant non-breaking space character '&#160;')
10426 * @return {Roo.MessageBox} This message box
10428 updateText : function(text){
10429 if(!dlg.isVisible() && !opt.width){
10430 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10432 msgEl.innerHTML = text || ' ';
10434 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10435 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10437 Math.min(opt.width || cw , this.maxWidth),
10438 Math.max(opt.minWidth || this.minWidth, bwidth)
10441 activeTextEl.setWidth(w);
10443 if(dlg.isVisible()){
10444 dlg.fixedcenter = false;
10446 // to big, make it scroll. = But as usual stupid IE does not support
10449 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10450 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10451 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10453 bodyEl.dom.style.height = '';
10454 bodyEl.dom.style.overflowY = '';
10457 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10459 bodyEl.dom.style.overflowX = '';
10462 dlg.setContentSize(w, bodyEl.getHeight());
10463 if(dlg.isVisible()){
10464 dlg.fixedcenter = true;
10470 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
10471 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10472 * @param {Number} value Any number between 0 and 1 (e.g., .5)
10473 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10474 * @return {Roo.MessageBox} This message box
10476 updateProgress : function(value, text){
10478 this.updateText(text);
10480 if (pp) { // weird bug on my firefox - for some reason this is not defined
10481 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10487 * Returns true if the message box is currently displayed
10488 * @return {Boolean} True if the message box is visible, else false
10490 isVisible : function(){
10491 return dlg && dlg.isVisible();
10495 * Hides the message box if it is displayed
10498 if(this.isVisible()){
10504 * Displays a new message box, or reinitializes an existing message box, based on the config options
10505 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10506 * The following config object properties are supported:
10508 Property Type Description
10509 ---------- --------------- ------------------------------------------------------------------------------------
10510 animEl String/Element An id or Element from which the message box should animate as it opens and
10511 closes (defaults to undefined)
10512 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10513 cancel:'Bar'}), or false to not show any buttons (defaults to false)
10514 closable Boolean False to hide the top-right close button (defaults to true). Note that
10515 progress and wait dialogs will ignore this property and always hide the
10516 close button as they can only be closed programmatically.
10517 cls String A custom CSS class to apply to the message box element
10518 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
10519 displayed (defaults to 75)
10520 fn Function A callback function to execute after closing the dialog. The arguments to the
10521 function will be btn (the name of the button that was clicked, if applicable,
10522 e.g. "ok"), and text (the value of the active text field, if applicable).
10523 Progress and wait dialogs will ignore this option since they do not respond to
10524 user actions and can only be closed programmatically, so any required function
10525 should be called by the same code after it closes the dialog.
10526 icon String A CSS class that provides a background image to be used as an icon for
10527 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10528 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
10529 minWidth Number The minimum width in pixels of the message box (defaults to 100)
10530 modal Boolean False to allow user interaction with the page while the message box is
10531 displayed (defaults to true)
10532 msg String A string that will replace the existing message box body text (defaults
10533 to the XHTML-compliant non-breaking space character ' ')
10534 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
10535 progress Boolean True to display a progress bar (defaults to false)
10536 progressText String The text to display inside the progress bar if progress = true (defaults to '')
10537 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
10538 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
10539 title String The title text
10540 value String The string value to set into the active textbox element if displayed
10541 wait Boolean True to display a progress bar (defaults to false)
10542 width Number The width of the dialog in pixels
10549 msg: 'Please enter your address:',
10551 buttons: Roo.MessageBox.OKCANCEL,
10554 animEl: 'addAddressBtn'
10557 * @param {Object} config Configuration options
10558 * @return {Roo.MessageBox} This message box
10560 show : function(options)
10563 // this causes nightmares if you show one dialog after another
10564 // especially on callbacks..
10566 if(this.isVisible()){
10569 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10570 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
10571 Roo.log("New Dialog Message:" + options.msg )
10572 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10573 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10576 var d = this.getDialog();
10578 d.setTitle(opt.title || " ");
10579 d.close.setDisplayed(opt.closable !== false);
10580 activeTextEl = textboxEl;
10581 opt.prompt = opt.prompt || (opt.multiline ? true : false);
10586 textareaEl.setHeight(typeof opt.multiline == "number" ?
10587 opt.multiline : this.defaultTextHeight);
10588 activeTextEl = textareaEl;
10597 progressEl.setDisplayed(opt.progress === true);
10598 this.updateProgress(0);
10599 activeTextEl.dom.value = opt.value || "";
10601 dlg.setDefaultButton(activeTextEl);
10603 var bs = opt.buttons;
10606 db = buttons["ok"];
10607 }else if(bs && bs.yes){
10608 db = buttons["yes"];
10610 dlg.setDefaultButton(db);
10612 bwidth = updateButtons(opt.buttons);
10613 this.updateText(opt.msg);
10615 d.el.addClass(opt.cls);
10617 d.proxyDrag = opt.proxyDrag === true;
10618 d.modal = opt.modal !== false;
10619 d.mask = opt.modal !== false ? mask : false;
10620 if(!d.isVisible()){
10621 // force it to the end of the z-index stack so it gets a cursor in FF
10622 document.body.appendChild(dlg.el.dom);
10623 d.animateTarget = null;
10624 d.show(options.animEl);
10630 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
10631 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10632 * and closing the message box when the process is complete.
10633 * @param {String} title The title bar text
10634 * @param {String} msg The message box body text
10635 * @return {Roo.MessageBox} This message box
10637 progress : function(title, msg){
10644 minWidth: this.minProgressWidth,
10651 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10652 * If a callback function is passed it will be called after the user clicks the button, and the
10653 * id of the button that was clicked will be passed as the only parameter to the callback
10654 * (could also be the top-right close button).
10655 * @param {String} title The title bar text
10656 * @param {String} msg The message box body text
10657 * @param {Function} fn (optional) The callback function invoked after the message box is closed
10658 * @param {Object} scope (optional) The scope of the callback function
10659 * @return {Roo.MessageBox} This message box
10661 alert : function(title, msg, fn, scope){
10674 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
10675 * interaction while waiting for a long-running process to complete that does not have defined intervals.
10676 * You are responsible for closing the message box when the process is complete.
10677 * @param {String} msg The message box body text
10678 * @param {String} title (optional) The title bar text
10679 * @return {Roo.MessageBox} This message box
10681 wait : function(msg, title){
10692 waitTimer = Roo.TaskMgr.start({
10694 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10702 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10703 * If a callback function is passed it will be called after the user clicks either button, and the id of the
10704 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10705 * @param {String} title The title bar text
10706 * @param {String} msg The message box body text
10707 * @param {Function} fn (optional) The callback function invoked after the message box is closed
10708 * @param {Object} scope (optional) The scope of the callback function
10709 * @return {Roo.MessageBox} This message box
10711 confirm : function(title, msg, fn, scope){
10715 buttons: this.YESNO,
10724 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10725 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
10726 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10727 * (could also be the top-right close button) and the text that was entered will be passed as the two
10728 * parameters to the callback.
10729 * @param {String} title The title bar text
10730 * @param {String} msg The message box body text
10731 * @param {Function} fn (optional) The callback function invoked after the message box is closed
10732 * @param {Object} scope (optional) The scope of the callback function
10733 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10734 * property, or the height in pixels to create the textbox (defaults to false / single-line)
10735 * @return {Roo.MessageBox} This message box
10737 prompt : function(title, msg, fn, scope, multiline){
10741 buttons: this.OKCANCEL,
10746 multiline: multiline,
10753 * Button config that displays a single OK button
10758 * Button config that displays Yes and No buttons
10761 YESNO : {yes:true, no:true},
10763 * Button config that displays OK and Cancel buttons
10766 OKCANCEL : {ok:true, cancel:true},
10768 * Button config that displays Yes, No and Cancel buttons
10771 YESNOCANCEL : {yes:true, no:true, cancel:true},
10774 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10777 defaultTextHeight : 75,
10779 * The maximum width in pixels of the message box (defaults to 600)
10784 * The minimum width in pixels of the message box (defaults to 100)
10789 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
10790 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10793 minProgressWidth : 250,
10795 * An object containing the default button text strings that can be overriden for localized language support.
10796 * Supported properties are: ok, cancel, yes and no.
10797 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10810 * Shorthand for {@link Roo.MessageBox}
10812 Roo.Msg = Roo.MessageBox;/*
10814 * Ext JS Library 1.1.1
10815 * Copyright(c) 2006-2007, Ext JS, LLC.
10817 * Originally Released Under LGPL - original licence link has changed is not relivant.
10820 * <script type="text/javascript">
10823 * @class Roo.QuickTips
10824 * Provides attractive and customizable tooltips for any element.
10827 Roo.QuickTips = function(){
10828 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10829 var ce, bd, xy, dd;
10830 var visible = false, disabled = true, inited = false;
10831 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10833 var onOver = function(e){
10837 var t = e.getTarget();
10838 if(!t || t.nodeType !== 1 || t == document || t == document.body){
10841 if(ce && t == ce.el){
10842 clearTimeout(hideProc);
10845 if(t && tagEls[t.id]){
10846 tagEls[t.id].el = t;
10847 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10850 var ttp, et = Roo.fly(t);
10851 var ns = cfg.namespace;
10852 if(tm.interceptTitles && t.title){
10855 t.removeAttribute("title");
10856 e.preventDefault();
10858 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10861 showProc = show.defer(tm.showDelay, tm, [{
10863 text: ttp.replace(/\\n/g,'<br/>'),
10864 width: et.getAttributeNS(ns, cfg.width),
10865 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10866 title: et.getAttributeNS(ns, cfg.title),
10867 cls: et.getAttributeNS(ns, cfg.cls)
10872 var onOut = function(e){
10873 clearTimeout(showProc);
10874 var t = e.getTarget();
10875 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10876 hideProc = setTimeout(hide, tm.hideDelay);
10880 var onMove = function(e){
10886 if(tm.trackMouse && ce){
10891 var onDown = function(e){
10892 clearTimeout(showProc);
10893 clearTimeout(hideProc);
10895 if(tm.hideOnClick){
10898 tm.enable.defer(100, tm);
10903 var getPad = function(){
10904 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10907 var show = function(o){
10911 clearTimeout(dismissProc);
10913 if(removeCls){ // in case manually hidden
10914 el.removeClass(removeCls);
10918 el.addClass(ce.cls);
10919 removeCls = ce.cls;
10922 tipTitle.update(ce.title);
10925 tipTitle.update('');
10928 el.dom.style.width = tm.maxWidth+'px';
10929 //tipBody.dom.style.width = '';
10930 tipBodyText.update(o.text);
10931 var p = getPad(), w = ce.width;
10933 var td = tipBodyText.dom;
10934 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10935 if(aw > tm.maxWidth){
10937 }else if(aw < tm.minWidth){
10943 //tipBody.setWidth(w);
10944 el.setWidth(parseInt(w, 10) + p);
10945 if(ce.autoHide === false){
10946 close.setDisplayed(true);
10951 close.setDisplayed(false);
10957 el.avoidY = xy[1]-18;
10962 el.setStyle("visibility", "visible");
10963 el.fadeIn({callback: afterShow});
10969 var afterShow = function(){
10973 if(tm.autoDismiss && ce.autoHide !== false){
10974 dismissProc = setTimeout(hide, tm.autoDismissDelay);
10979 var hide = function(noanim){
10980 clearTimeout(dismissProc);
10981 clearTimeout(hideProc);
10983 if(el.isVisible()){
10985 if(noanim !== true && tm.animate){
10986 el.fadeOut({callback: afterHide});
10993 var afterHide = function(){
10996 el.removeClass(removeCls);
11003 * @cfg {Number} minWidth
11004 * The minimum width of the quick tip (defaults to 40)
11008 * @cfg {Number} maxWidth
11009 * The maximum width of the quick tip (defaults to 300)
11013 * @cfg {Boolean} interceptTitles
11014 * True to automatically use the element's DOM title value if available (defaults to false)
11016 interceptTitles : false,
11018 * @cfg {Boolean} trackMouse
11019 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11021 trackMouse : false,
11023 * @cfg {Boolean} hideOnClick
11024 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11026 hideOnClick : true,
11028 * @cfg {Number} showDelay
11029 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11033 * @cfg {Number} hideDelay
11034 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11038 * @cfg {Boolean} autoHide
11039 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11040 * Used in conjunction with hideDelay.
11045 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11046 * (defaults to true). Used in conjunction with autoDismissDelay.
11048 autoDismiss : true,
11051 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11053 autoDismissDelay : 5000,
11055 * @cfg {Boolean} animate
11056 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11061 * @cfg {String} title
11062 * Title text to display (defaults to ''). This can be any valid HTML markup.
11066 * @cfg {String} text
11067 * Body text to display (defaults to ''). This can be any valid HTML markup.
11071 * @cfg {String} cls
11072 * A CSS class to apply to the base quick tip element (defaults to '').
11076 * @cfg {Number} width
11077 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
11078 * minWidth or maxWidth.
11083 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
11084 * or display QuickTips in a page.
11087 tm = Roo.QuickTips;
11088 cfg = tm.tagConfig;
11090 if(!Roo.isReady){ // allow calling of init() before onReady
11091 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11094 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11095 el.fxDefaults = {stopFx: true};
11096 // maximum custom styling
11097 //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>');
11098 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>');
11099 tipTitle = el.child('h3');
11100 tipTitle.enableDisplayMode("block");
11101 tipBody = el.child('div.x-tip-bd');
11102 tipBodyText = el.child('div.x-tip-bd-inner');
11103 //bdLeft = el.child('div.x-tip-bd-left');
11104 //bdRight = el.child('div.x-tip-bd-right');
11105 close = el.child('div.x-tip-close');
11106 close.enableDisplayMode("block");
11107 close.on("click", hide);
11108 var d = Roo.get(document);
11109 d.on("mousedown", onDown);
11110 d.on("mouseover", onOver);
11111 d.on("mouseout", onOut);
11112 d.on("mousemove", onMove);
11113 esc = d.addKeyListener(27, hide);
11116 dd = el.initDD("default", null, {
11117 onDrag : function(){
11121 dd.setHandleElId(tipTitle.id);
11130 * Configures a new quick tip instance and assigns it to a target element. The following config options
11133 Property Type Description
11134 ---------- --------------------- ------------------------------------------------------------------------
11135 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
11137 * @param {Object} config The config object
11139 register : function(config){
11140 var cs = config instanceof Array ? config : arguments;
11141 for(var i = 0, len = cs.length; i < len; i++) {
11143 var target = c.target;
11145 if(target instanceof Array){
11146 for(var j = 0, jlen = target.length; j < jlen; j++){
11147 tagEls[target[j]] = c;
11150 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11157 * Removes this quick tip from its element and destroys it.
11158 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11160 unregister : function(el){
11161 delete tagEls[Roo.id(el)];
11165 * Enable this quick tip.
11167 enable : function(){
11168 if(inited && disabled){
11170 if(locks.length < 1){
11177 * Disable this quick tip.
11179 disable : function(){
11181 clearTimeout(showProc);
11182 clearTimeout(hideProc);
11183 clearTimeout(dismissProc);
11191 * Returns true if the quick tip is enabled, else false.
11193 isEnabled : function(){
11199 namespace : "roo", // was ext?? this may break..
11200 alt_namespace : "ext",
11201 attribute : "qtip",
11211 // backwards compat
11212 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11214 * Ext JS Library 1.1.1
11215 * Copyright(c) 2006-2007, Ext JS, LLC.
11217 * Originally Released Under LGPL - original licence link has changed is not relivant.
11220 * <script type="text/javascript">
11225 * @class Roo.tree.TreePanel
11226 * @extends Roo.data.Tree
11227 * @cfg {Roo.tree.TreeNode} root The root node
11228 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11229 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11230 * @cfg {Boolean} enableDD true to enable drag and drop
11231 * @cfg {Boolean} enableDrag true to enable just drag
11232 * @cfg {Boolean} enableDrop true to enable just drop
11233 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11234 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11235 * @cfg {String} ddGroup The DD group this TreePanel belongs to
11236 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11237 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11238 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11239 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11240 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11241 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11242 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11243 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11244 * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11245 * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11246 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11247 * @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>
11248 * @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>
11251 * @param {String/HTMLElement/Element} el The container element
11252 * @param {Object} config
11254 Roo.tree.TreePanel = function(el, config){
11256 var loader = false;
11258 root = config.root;
11259 delete config.root;
11261 if (config.loader) {
11262 loader = config.loader;
11263 delete config.loader;
11266 Roo.apply(this, config);
11267 Roo.tree.TreePanel.superclass.constructor.call(this);
11268 this.el = Roo.get(el);
11269 this.el.addClass('x-tree');
11270 //console.log(root);
11272 this.setRootNode( Roo.factory(root, Roo.tree));
11275 this.loader = Roo.factory(loader, Roo.tree);
11278 * Read-only. The id of the container element becomes this TreePanel's id.
11280 this.id = this.el.id;
11283 * @event beforeload
11284 * Fires before a node is loaded, return false to cancel
11285 * @param {Node} node The node being loaded
11287 "beforeload" : true,
11290 * Fires when a node is loaded
11291 * @param {Node} node The node that was loaded
11295 * @event textchange
11296 * Fires when the text for a node is changed
11297 * @param {Node} node The node
11298 * @param {String} text The new text
11299 * @param {String} oldText The old text
11301 "textchange" : true,
11303 * @event beforeexpand
11304 * Fires before a node is expanded, return false to cancel.
11305 * @param {Node} node The node
11306 * @param {Boolean} deep
11307 * @param {Boolean} anim
11309 "beforeexpand" : true,
11311 * @event beforecollapse
11312 * Fires before a node is collapsed, return false to cancel.
11313 * @param {Node} node The node
11314 * @param {Boolean} deep
11315 * @param {Boolean} anim
11317 "beforecollapse" : true,
11320 * Fires when a node is expanded
11321 * @param {Node} node The node
11325 * @event disabledchange
11326 * Fires when the disabled status of a node changes
11327 * @param {Node} node The node
11328 * @param {Boolean} disabled
11330 "disabledchange" : true,
11333 * Fires when a node is collapsed
11334 * @param {Node} node The node
11338 * @event beforeclick
11339 * Fires before click processing on a node. Return false to cancel the default action.
11340 * @param {Node} node The node
11341 * @param {Roo.EventObject} e The event object
11343 "beforeclick":true,
11345 * @event checkchange
11346 * Fires when a node with a checkbox's checked property changes
11347 * @param {Node} this This node
11348 * @param {Boolean} checked
11350 "checkchange":true,
11353 * Fires when a node is clicked
11354 * @param {Node} node The node
11355 * @param {Roo.EventObject} e The event object
11360 * Fires when a node is double clicked
11361 * @param {Node} node The node
11362 * @param {Roo.EventObject} e The event object
11366 * @event contextmenu
11367 * Fires when a node is right clicked
11368 * @param {Node} node The node
11369 * @param {Roo.EventObject} e The event object
11371 "contextmenu":true,
11373 * @event beforechildrenrendered
11374 * Fires right before the child nodes for a node are rendered
11375 * @param {Node} node The node
11377 "beforechildrenrendered":true,
11380 * Fires when a node starts being dragged
11381 * @param {Roo.tree.TreePanel} this
11382 * @param {Roo.tree.TreeNode} node
11383 * @param {event} e The raw browser event
11385 "startdrag" : true,
11388 * Fires when a drag operation is complete
11389 * @param {Roo.tree.TreePanel} this
11390 * @param {Roo.tree.TreeNode} node
11391 * @param {event} e The raw browser event
11396 * Fires when a dragged node is dropped on a valid DD target
11397 * @param {Roo.tree.TreePanel} this
11398 * @param {Roo.tree.TreeNode} node
11399 * @param {DD} dd The dd it was dropped on
11400 * @param {event} e The raw browser event
11404 * @event beforenodedrop
11405 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11406 * passed to handlers has the following properties:<br />
11407 * <ul style="padding:5px;padding-left:16px;">
11408 * <li>tree - The TreePanel</li>
11409 * <li>target - The node being targeted for the drop</li>
11410 * <li>data - The drag data from the drag source</li>
11411 * <li>point - The point of the drop - append, above or below</li>
11412 * <li>source - The drag source</li>
11413 * <li>rawEvent - Raw mouse event</li>
11414 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11415 * to be inserted by setting them on this object.</li>
11416 * <li>cancel - Set this to true to cancel the drop.</li>
11418 * @param {Object} dropEvent
11420 "beforenodedrop" : true,
11423 * Fires after a DD object is dropped on a node in this tree. The dropEvent
11424 * passed to handlers has the following properties:<br />
11425 * <ul style="padding:5px;padding-left:16px;">
11426 * <li>tree - The TreePanel</li>
11427 * <li>target - The node being targeted for the drop</li>
11428 * <li>data - The drag data from the drag source</li>
11429 * <li>point - The point of the drop - append, above or below</li>
11430 * <li>source - The drag source</li>
11431 * <li>rawEvent - Raw mouse event</li>
11432 * <li>dropNode - Dropped node(s).</li>
11434 * @param {Object} dropEvent
11438 * @event nodedragover
11439 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11440 * passed to handlers has the following properties:<br />
11441 * <ul style="padding:5px;padding-left:16px;">
11442 * <li>tree - The TreePanel</li>
11443 * <li>target - The node being targeted for the drop</li>
11444 * <li>data - The drag data from the drag source</li>
11445 * <li>point - The point of the drop - append, above or below</li>
11446 * <li>source - The drag source</li>
11447 * <li>rawEvent - Raw mouse event</li>
11448 * <li>dropNode - Drop node(s) provided by the source.</li>
11449 * <li>cancel - Set this to true to signal drop not allowed.</li>
11451 * @param {Object} dragOverEvent
11453 "nodedragover" : true,
11455 * @event appendnode
11456 * Fires when append node to the tree
11457 * @param {Roo.tree.TreePanel} this
11458 * @param {Roo.tree.TreeNode} node
11459 * @param {Number} index The index of the newly appended node
11461 "appendnode" : true
11464 if(this.singleExpand){
11465 this.on("beforeexpand", this.restrictExpand, this);
11468 this.editor.tree = this;
11469 this.editor = Roo.factory(this.editor, Roo.tree);
11472 if (this.selModel) {
11473 this.selModel = Roo.factory(this.selModel, Roo.tree);
11477 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11478 rootVisible : true,
11479 animate: Roo.enableFx,
11482 hlDrop : Roo.enableFx,
11486 rendererTip: false,
11488 restrictExpand : function(node){
11489 var p = node.parentNode;
11491 if(p.expandedChild && p.expandedChild.parentNode == p){
11492 p.expandedChild.collapse();
11494 p.expandedChild = node;
11498 // private override
11499 setRootNode : function(node){
11500 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11501 if(!this.rootVisible){
11502 node.ui = new Roo.tree.RootTreeNodeUI(node);
11508 * Returns the container element for this TreePanel
11510 getEl : function(){
11515 * Returns the default TreeLoader for this TreePanel
11517 getLoader : function(){
11518 return this.loader;
11524 expandAll : function(){
11525 this.root.expand(true);
11529 * Collapse all nodes
11531 collapseAll : function(){
11532 this.root.collapse(true);
11536 * Returns the selection model used by this TreePanel
11538 getSelectionModel : function(){
11539 if(!this.selModel){
11540 this.selModel = new Roo.tree.DefaultSelectionModel();
11542 return this.selModel;
11546 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11547 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11548 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11551 getChecked : function(a, startNode){
11552 startNode = startNode || this.root;
11554 var f = function(){
11555 if(this.attributes.checked){
11556 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11559 startNode.cascade(f);
11564 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11565 * @param {String} path
11566 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11567 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11568 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11570 expandPath : function(path, attr, callback){
11571 attr = attr || "id";
11572 var keys = path.split(this.pathSeparator);
11573 var curNode = this.root;
11574 if(curNode.attributes[attr] != keys[1]){ // invalid root
11576 callback(false, null);
11581 var f = function(){
11582 if(++index == keys.length){
11584 callback(true, curNode);
11588 var c = curNode.findChild(attr, keys[index]);
11591 callback(false, curNode);
11596 c.expand(false, false, f);
11598 curNode.expand(false, false, f);
11602 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11603 * @param {String} path
11604 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11605 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11606 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11608 selectPath : function(path, attr, callback){
11609 attr = attr || "id";
11610 var keys = path.split(this.pathSeparator);
11611 var v = keys.pop();
11612 if(keys.length > 0){
11613 var f = function(success, node){
11614 if(success && node){
11615 var n = node.findChild(attr, v);
11621 }else if(callback){
11622 callback(false, n);
11626 callback(false, n);
11630 this.expandPath(keys.join(this.pathSeparator), attr, f);
11632 this.root.select();
11634 callback(true, this.root);
11639 getTreeEl : function(){
11644 * Trigger rendering of this TreePanel
11646 render : function(){
11647 if (this.innerCt) {
11648 return this; // stop it rendering more than once!!
11651 this.innerCt = this.el.createChild({tag:"ul",
11652 cls:"x-tree-root-ct " +
11653 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11655 if(this.containerScroll){
11656 Roo.dd.ScrollManager.register(this.el);
11658 if((this.enableDD || this.enableDrop) && !this.dropZone){
11660 * The dropZone used by this tree if drop is enabled
11661 * @type Roo.tree.TreeDropZone
11663 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11664 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11667 if((this.enableDD || this.enableDrag) && !this.dragZone){
11669 * The dragZone used by this tree if drag is enabled
11670 * @type Roo.tree.TreeDragZone
11672 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11673 ddGroup: this.ddGroup || "TreeDD",
11674 scroll: this.ddScroll
11677 this.getSelectionModel().init(this);
11679 Roo.log("ROOT not set in tree");
11682 this.root.render();
11683 if(!this.rootVisible){
11684 this.root.renderChildren();
11690 * Ext JS Library 1.1.1
11691 * Copyright(c) 2006-2007, Ext JS, LLC.
11693 * Originally Released Under LGPL - original licence link has changed is not relivant.
11696 * <script type="text/javascript">
11701 * @class Roo.tree.DefaultSelectionModel
11702 * @extends Roo.util.Observable
11703 * The default single selection for a TreePanel.
11704 * @param {Object} cfg Configuration
11706 Roo.tree.DefaultSelectionModel = function(cfg){
11707 this.selNode = null;
11713 * @event selectionchange
11714 * Fires when the selected node changes
11715 * @param {DefaultSelectionModel} this
11716 * @param {TreeNode} node the new selection
11718 "selectionchange" : true,
11721 * @event beforeselect
11722 * Fires before the selected node changes, return false to cancel the change
11723 * @param {DefaultSelectionModel} this
11724 * @param {TreeNode} node the new selection
11725 * @param {TreeNode} node the old selection
11727 "beforeselect" : true
11730 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11733 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11734 init : function(tree){
11736 tree.getTreeEl().on("keydown", this.onKeyDown, this);
11737 tree.on("click", this.onNodeClick, this);
11740 onNodeClick : function(node, e){
11741 if (e.ctrlKey && this.selNode == node) {
11742 this.unselect(node);
11750 * @param {TreeNode} node The node to select
11751 * @return {TreeNode} The selected node
11753 select : function(node){
11754 var last = this.selNode;
11755 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11757 last.ui.onSelectedChange(false);
11759 this.selNode = node;
11760 node.ui.onSelectedChange(true);
11761 this.fireEvent("selectionchange", this, node, last);
11768 * @param {TreeNode} node The node to unselect
11770 unselect : function(node){
11771 if(this.selNode == node){
11772 this.clearSelections();
11777 * Clear all selections
11779 clearSelections : function(){
11780 var n = this.selNode;
11782 n.ui.onSelectedChange(false);
11783 this.selNode = null;
11784 this.fireEvent("selectionchange", this, null);
11790 * Get the selected node
11791 * @return {TreeNode} The selected node
11793 getSelectedNode : function(){
11794 return this.selNode;
11798 * Returns true if the node is selected
11799 * @param {TreeNode} node The node to check
11800 * @return {Boolean}
11802 isSelected : function(node){
11803 return this.selNode == node;
11807 * Selects the node above the selected node in the tree, intelligently walking the nodes
11808 * @return TreeNode The new selection
11810 selectPrevious : function(){
11811 var s = this.selNode || this.lastSelNode;
11815 var ps = s.previousSibling;
11817 if(!ps.isExpanded() || ps.childNodes.length < 1){
11818 return this.select(ps);
11820 var lc = ps.lastChild;
11821 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11824 return this.select(lc);
11826 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11827 return this.select(s.parentNode);
11833 * Selects the node above the selected node in the tree, intelligently walking the nodes
11834 * @return TreeNode The new selection
11836 selectNext : function(){
11837 var s = this.selNode || this.lastSelNode;
11841 if(s.firstChild && s.isExpanded()){
11842 return this.select(s.firstChild);
11843 }else if(s.nextSibling){
11844 return this.select(s.nextSibling);
11845 }else if(s.parentNode){
11847 s.parentNode.bubble(function(){
11848 if(this.nextSibling){
11849 newS = this.getOwnerTree().selModel.select(this.nextSibling);
11858 onKeyDown : function(e){
11859 var s = this.selNode || this.lastSelNode;
11860 // undesirable, but required
11865 var k = e.getKey();
11873 this.selectPrevious();
11876 e.preventDefault();
11877 if(s.hasChildNodes()){
11878 if(!s.isExpanded()){
11880 }else if(s.firstChild){
11881 this.select(s.firstChild, e);
11886 e.preventDefault();
11887 if(s.hasChildNodes() && s.isExpanded()){
11889 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11890 this.select(s.parentNode, e);
11898 * @class Roo.tree.MultiSelectionModel
11899 * @extends Roo.util.Observable
11900 * Multi selection for a TreePanel.
11901 * @param {Object} cfg Configuration
11903 Roo.tree.MultiSelectionModel = function(){
11904 this.selNodes = [];
11908 * @event selectionchange
11909 * Fires when the selected nodes change
11910 * @param {MultiSelectionModel} this
11911 * @param {Array} nodes Array of the selected nodes
11913 "selectionchange" : true
11915 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11919 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11920 init : function(tree){
11922 tree.getTreeEl().on("keydown", this.onKeyDown, this);
11923 tree.on("click", this.onNodeClick, this);
11926 onNodeClick : function(node, e){
11927 this.select(node, e, e.ctrlKey);
11932 * @param {TreeNode} node The node to select
11933 * @param {EventObject} e (optional) An event associated with the selection
11934 * @param {Boolean} keepExisting True to retain existing selections
11935 * @return {TreeNode} The selected node
11937 select : function(node, e, keepExisting){
11938 if(keepExisting !== true){
11939 this.clearSelections(true);
11941 if(this.isSelected(node)){
11942 this.lastSelNode = node;
11945 this.selNodes.push(node);
11946 this.selMap[node.id] = node;
11947 this.lastSelNode = node;
11948 node.ui.onSelectedChange(true);
11949 this.fireEvent("selectionchange", this, this.selNodes);
11955 * @param {TreeNode} node The node to unselect
11957 unselect : function(node){
11958 if(this.selMap[node.id]){
11959 node.ui.onSelectedChange(false);
11960 var sn = this.selNodes;
11963 index = sn.indexOf(node);
11965 for(var i = 0, len = sn.length; i < len; i++){
11973 this.selNodes.splice(index, 1);
11975 delete this.selMap[node.id];
11976 this.fireEvent("selectionchange", this, this.selNodes);
11981 * Clear all selections
11983 clearSelections : function(suppressEvent){
11984 var sn = this.selNodes;
11986 for(var i = 0, len = sn.length; i < len; i++){
11987 sn[i].ui.onSelectedChange(false);
11989 this.selNodes = [];
11991 if(suppressEvent !== true){
11992 this.fireEvent("selectionchange", this, this.selNodes);
11998 * Returns true if the node is selected
11999 * @param {TreeNode} node The node to check
12000 * @return {Boolean}
12002 isSelected : function(node){
12003 return this.selMap[node.id] ? true : false;
12007 * Returns an array of the selected nodes
12010 getSelectedNodes : function(){
12011 return this.selNodes;
12014 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12016 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12018 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12021 * Ext JS Library 1.1.1
12022 * Copyright(c) 2006-2007, Ext JS, LLC.
12024 * Originally Released Under LGPL - original licence link has changed is not relivant.
12027 * <script type="text/javascript">
12031 * @class Roo.tree.TreeNode
12032 * @extends Roo.data.Node
12033 * @cfg {String} text The text for this node
12034 * @cfg {Boolean} expanded true to start the node expanded
12035 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12036 * @cfg {Boolean} allowDrop false if this node cannot be drop on
12037 * @cfg {Boolean} disabled true to start the node disabled
12038 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12039 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
12040 * @cfg {String} cls A css class to be added to the node
12041 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12042 * @cfg {String} href URL of the link used for the node (defaults to #)
12043 * @cfg {String} hrefTarget target frame for the link
12044 * @cfg {String} qtip An Ext QuickTip for the node
12045 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12046 * @cfg {Boolean} singleClickExpand True for single click expand on this node
12047 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12048 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12049 * (defaults to undefined with no checkbox rendered)
12051 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12053 Roo.tree.TreeNode = function(attributes){
12054 attributes = attributes || {};
12055 if(typeof attributes == "string"){
12056 attributes = {text: attributes};
12058 this.childrenRendered = false;
12059 this.rendered = false;
12060 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12061 this.expanded = attributes.expanded === true;
12062 this.isTarget = attributes.isTarget !== false;
12063 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12064 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12067 * Read-only. The text for this node. To change it use setText().
12070 this.text = attributes.text;
12072 * True if this node is disabled.
12075 this.disabled = attributes.disabled === true;
12079 * @event textchange
12080 * Fires when the text for this node is changed
12081 * @param {Node} this This node
12082 * @param {String} text The new text
12083 * @param {String} oldText The old text
12085 "textchange" : true,
12087 * @event beforeexpand
12088 * Fires before this node is expanded, return false to cancel.
12089 * @param {Node} this This node
12090 * @param {Boolean} deep
12091 * @param {Boolean} anim
12093 "beforeexpand" : true,
12095 * @event beforecollapse
12096 * Fires before this node is collapsed, return false to cancel.
12097 * @param {Node} this This node
12098 * @param {Boolean} deep
12099 * @param {Boolean} anim
12101 "beforecollapse" : true,
12104 * Fires when this node is expanded
12105 * @param {Node} this This node
12109 * @event disabledchange
12110 * Fires when the disabled status of this node changes
12111 * @param {Node} this This node
12112 * @param {Boolean} disabled
12114 "disabledchange" : true,
12117 * Fires when this node is collapsed
12118 * @param {Node} this This node
12122 * @event beforeclick
12123 * Fires before click processing. Return false to cancel the default action.
12124 * @param {Node} this This node
12125 * @param {Roo.EventObject} e The event object
12127 "beforeclick":true,
12129 * @event checkchange
12130 * Fires when a node with a checkbox's checked property changes
12131 * @param {Node} this This node
12132 * @param {Boolean} checked
12134 "checkchange":true,
12137 * Fires when this node is clicked
12138 * @param {Node} this This node
12139 * @param {Roo.EventObject} e The event object
12144 * Fires when this node is double clicked
12145 * @param {Node} this This node
12146 * @param {Roo.EventObject} e The event object
12150 * @event contextmenu
12151 * Fires when this node is right clicked
12152 * @param {Node} this This node
12153 * @param {Roo.EventObject} e The event object
12155 "contextmenu":true,
12157 * @event beforechildrenrendered
12158 * Fires right before the child nodes for this node are rendered
12159 * @param {Node} this This node
12161 "beforechildrenrendered":true
12164 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12167 * Read-only. The UI for this node
12170 this.ui = new uiClass(this);
12172 // finally support items[]
12173 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12178 Roo.each(this.attributes.items, function(c) {
12179 this.appendChild(Roo.factory(c,Roo.Tree));
12181 delete this.attributes.items;
12186 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12187 preventHScroll: true,
12189 * Returns true if this node is expanded
12190 * @return {Boolean}
12192 isExpanded : function(){
12193 return this.expanded;
12197 * Returns the UI object for this node
12198 * @return {TreeNodeUI}
12200 getUI : function(){
12204 // private override
12205 setFirstChild : function(node){
12206 var of = this.firstChild;
12207 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12208 if(this.childrenRendered && of && node != of){
12209 of.renderIndent(true, true);
12212 this.renderIndent(true, true);
12216 // private override
12217 setLastChild : function(node){
12218 var ol = this.lastChild;
12219 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12220 if(this.childrenRendered && ol && node != ol){
12221 ol.renderIndent(true, true);
12224 this.renderIndent(true, true);
12228 // these methods are overridden to provide lazy rendering support
12229 // private override
12230 appendChild : function()
12232 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12233 if(node && this.childrenRendered){
12236 this.ui.updateExpandIcon();
12240 // private override
12241 removeChild : function(node){
12242 this.ownerTree.getSelectionModel().unselect(node);
12243 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12244 // if it's been rendered remove dom node
12245 if(this.childrenRendered){
12248 if(this.childNodes.length < 1){
12249 this.collapse(false, false);
12251 this.ui.updateExpandIcon();
12253 if(!this.firstChild) {
12254 this.childrenRendered = false;
12259 // private override
12260 insertBefore : function(node, refNode){
12261 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12262 if(newNode && refNode && this.childrenRendered){
12265 this.ui.updateExpandIcon();
12270 * Sets the text for this node
12271 * @param {String} text
12273 setText : function(text){
12274 var oldText = this.text;
12276 this.attributes.text = text;
12277 if(this.rendered){ // event without subscribing
12278 this.ui.onTextChange(this, text, oldText);
12280 this.fireEvent("textchange", this, text, oldText);
12284 * Triggers selection of this node
12286 select : function(){
12287 this.getOwnerTree().getSelectionModel().select(this);
12291 * Triggers deselection of this node
12293 unselect : function(){
12294 this.getOwnerTree().getSelectionModel().unselect(this);
12298 * Returns true if this node is selected
12299 * @return {Boolean}
12301 isSelected : function(){
12302 return this.getOwnerTree().getSelectionModel().isSelected(this);
12306 * Expand this node.
12307 * @param {Boolean} deep (optional) True to expand all children as well
12308 * @param {Boolean} anim (optional) false to cancel the default animation
12309 * @param {Function} callback (optional) A callback to be called when
12310 * expanding this node completes (does not wait for deep expand to complete).
12311 * Called with 1 parameter, this node.
12313 expand : function(deep, anim, callback){
12314 if(!this.expanded){
12315 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12318 if(!this.childrenRendered){
12319 this.renderChildren();
12321 this.expanded = true;
12323 if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12324 this.ui.animExpand(function(){
12325 this.fireEvent("expand", this);
12326 if(typeof callback == "function"){
12330 this.expandChildNodes(true);
12332 }.createDelegate(this));
12336 this.fireEvent("expand", this);
12337 if(typeof callback == "function"){
12342 if(typeof callback == "function"){
12347 this.expandChildNodes(true);
12351 isHiddenRoot : function(){
12352 return this.isRoot && !this.getOwnerTree().rootVisible;
12356 * Collapse this node.
12357 * @param {Boolean} deep (optional) True to collapse all children as well
12358 * @param {Boolean} anim (optional) false to cancel the default animation
12360 collapse : function(deep, anim){
12361 if(this.expanded && !this.isHiddenRoot()){
12362 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12365 this.expanded = false;
12366 if((this.getOwnerTree().animate && anim !== false) || anim){
12367 this.ui.animCollapse(function(){
12368 this.fireEvent("collapse", this);
12370 this.collapseChildNodes(true);
12372 }.createDelegate(this));
12375 this.ui.collapse();
12376 this.fireEvent("collapse", this);
12380 var cs = this.childNodes;
12381 for(var i = 0, len = cs.length; i < len; i++) {
12382 cs[i].collapse(true, false);
12388 delayedExpand : function(delay){
12389 if(!this.expandProcId){
12390 this.expandProcId = this.expand.defer(delay, this);
12395 cancelExpand : function(){
12396 if(this.expandProcId){
12397 clearTimeout(this.expandProcId);
12399 this.expandProcId = false;
12403 * Toggles expanded/collapsed state of the node
12405 toggle : function(){
12414 * Ensures all parent nodes are expanded
12416 ensureVisible : function(callback){
12417 var tree = this.getOwnerTree();
12418 tree.expandPath(this.parentNode.getPath(), false, function(){
12419 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12420 Roo.callback(callback);
12421 }.createDelegate(this));
12425 * Expand all child nodes
12426 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12428 expandChildNodes : function(deep){
12429 var cs = this.childNodes;
12430 for(var i = 0, len = cs.length; i < len; i++) {
12431 cs[i].expand(deep);
12436 * Collapse all child nodes
12437 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12439 collapseChildNodes : function(deep){
12440 var cs = this.childNodes;
12441 for(var i = 0, len = cs.length; i < len; i++) {
12442 cs[i].collapse(deep);
12447 * Disables this node
12449 disable : function(){
12450 this.disabled = true;
12452 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12453 this.ui.onDisableChange(this, true);
12455 this.fireEvent("disabledchange", this, true);
12459 * Enables this node
12461 enable : function(){
12462 this.disabled = false;
12463 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12464 this.ui.onDisableChange(this, false);
12466 this.fireEvent("disabledchange", this, false);
12470 renderChildren : function(suppressEvent){
12471 if(suppressEvent !== false){
12472 this.fireEvent("beforechildrenrendered", this);
12474 var cs = this.childNodes;
12475 for(var i = 0, len = cs.length; i < len; i++){
12476 cs[i].render(true);
12478 this.childrenRendered = true;
12482 sort : function(fn, scope){
12483 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12484 if(this.childrenRendered){
12485 var cs = this.childNodes;
12486 for(var i = 0, len = cs.length; i < len; i++){
12487 cs[i].render(true);
12493 render : function(bulkRender){
12494 this.ui.render(bulkRender);
12495 if(!this.rendered){
12496 this.rendered = true;
12498 this.expanded = false;
12499 this.expand(false, false);
12505 renderIndent : function(deep, refresh){
12507 this.ui.childIndent = null;
12509 this.ui.renderIndent();
12510 if(deep === true && this.childrenRendered){
12511 var cs = this.childNodes;
12512 for(var i = 0, len = cs.length; i < len; i++){
12513 cs[i].renderIndent(true, refresh);
12519 * Ext JS Library 1.1.1
12520 * Copyright(c) 2006-2007, Ext JS, LLC.
12522 * Originally Released Under LGPL - original licence link has changed is not relivant.
12525 * <script type="text/javascript">
12529 * @class Roo.tree.AsyncTreeNode
12530 * @extends Roo.tree.TreeNode
12531 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12533 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12535 Roo.tree.AsyncTreeNode = function(config){
12536 this.loaded = false;
12537 this.loading = false;
12538 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12540 * @event beforeload
12541 * Fires before this node is loaded, return false to cancel
12542 * @param {Node} this This node
12544 this.addEvents({'beforeload':true, 'load': true});
12547 * Fires when this node is loaded
12548 * @param {Node} this This node
12551 * The loader used by this node (defaults to using the tree's defined loader)
12556 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12557 expand : function(deep, anim, callback){
12558 if(this.loading){ // if an async load is already running, waiting til it's done
12560 var f = function(){
12561 if(!this.loading){ // done loading
12562 clearInterval(timer);
12563 this.expand(deep, anim, callback);
12565 }.createDelegate(this);
12566 timer = setInterval(f, 200);
12570 if(this.fireEvent("beforeload", this) === false){
12573 this.loading = true;
12574 this.ui.beforeLoad(this);
12575 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12577 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12581 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12585 * Returns true if this node is currently loading
12586 * @return {Boolean}
12588 isLoading : function(){
12589 return this.loading;
12592 loadComplete : function(deep, anim, callback){
12593 this.loading = false;
12594 this.loaded = true;
12595 this.ui.afterLoad(this);
12596 this.fireEvent("load", this);
12597 this.expand(deep, anim, callback);
12601 * Returns true if this node has been loaded
12602 * @return {Boolean}
12604 isLoaded : function(){
12605 return this.loaded;
12608 hasChildNodes : function(){
12609 if(!this.isLeaf() && !this.loaded){
12612 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12617 * Trigger a reload for this node
12618 * @param {Function} callback
12620 reload : function(callback){
12621 this.collapse(false, false);
12622 while(this.firstChild){
12623 this.removeChild(this.firstChild);
12625 this.childrenRendered = false;
12626 this.loaded = false;
12627 if(this.isHiddenRoot()){
12628 this.expanded = false;
12630 this.expand(false, false, callback);
12634 * Ext JS Library 1.1.1
12635 * Copyright(c) 2006-2007, Ext JS, LLC.
12637 * Originally Released Under LGPL - original licence link has changed is not relivant.
12640 * <script type="text/javascript">
12644 * @class Roo.tree.TreeNodeUI
12646 * @param {Object} node The node to render
12647 * The TreeNode UI implementation is separate from the
12648 * tree implementation. Unless you are customizing the tree UI,
12649 * you should never have to use this directly.
12651 Roo.tree.TreeNodeUI = function(node){
12653 this.rendered = false;
12654 this.animating = false;
12655 this.emptyIcon = Roo.BLANK_IMAGE_URL;
12658 Roo.tree.TreeNodeUI.prototype = {
12659 removeChild : function(node){
12661 this.ctNode.removeChild(node.ui.getEl());
12665 beforeLoad : function(){
12666 this.addClass("x-tree-node-loading");
12669 afterLoad : function(){
12670 this.removeClass("x-tree-node-loading");
12673 onTextChange : function(node, text, oldText){
12675 this.textNode.innerHTML = text;
12679 onDisableChange : function(node, state){
12680 this.disabled = state;
12682 this.addClass("x-tree-node-disabled");
12684 this.removeClass("x-tree-node-disabled");
12688 onSelectedChange : function(state){
12691 this.addClass("x-tree-selected");
12694 this.removeClass("x-tree-selected");
12698 onMove : function(tree, node, oldParent, newParent, index, refNode){
12699 this.childIndent = null;
12701 var targetNode = newParent.ui.getContainer();
12702 if(!targetNode){//target not rendered
12703 this.holder = document.createElement("div");
12704 this.holder.appendChild(this.wrap);
12707 var insertBefore = refNode ? refNode.ui.getEl() : null;
12709 targetNode.insertBefore(this.wrap, insertBefore);
12711 targetNode.appendChild(this.wrap);
12713 this.node.renderIndent(true);
12717 addClass : function(cls){
12719 Roo.fly(this.elNode).addClass(cls);
12723 removeClass : function(cls){
12725 Roo.fly(this.elNode).removeClass(cls);
12729 remove : function(){
12731 this.holder = document.createElement("div");
12732 this.holder.appendChild(this.wrap);
12736 fireEvent : function(){
12737 return this.node.fireEvent.apply(this.node, arguments);
12740 initEvents : function(){
12741 this.node.on("move", this.onMove, this);
12742 var E = Roo.EventManager;
12743 var a = this.anchor;
12745 var el = Roo.fly(a, '_treeui');
12747 if(Roo.isOpera){ // opera render bug ignores the CSS
12748 el.setStyle("text-decoration", "none");
12751 el.on("click", this.onClick, this);
12752 el.on("dblclick", this.onDblClick, this);
12755 Roo.EventManager.on(this.checkbox,
12756 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12759 el.on("contextmenu", this.onContextMenu, this);
12761 var icon = Roo.fly(this.iconNode);
12762 icon.on("click", this.onClick, this);
12763 icon.on("dblclick", this.onDblClick, this);
12764 icon.on("contextmenu", this.onContextMenu, this);
12765 E.on(this.ecNode, "click", this.ecClick, this, true);
12767 if(this.node.disabled){
12768 this.addClass("x-tree-node-disabled");
12770 if(this.node.hidden){
12771 this.addClass("x-tree-node-disabled");
12773 var ot = this.node.getOwnerTree();
12774 var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12775 if(dd && (!this.node.isRoot || ot.rootVisible)){
12776 Roo.dd.Registry.register(this.elNode, {
12778 handles: this.getDDHandles(),
12784 getDDHandles : function(){
12785 return [this.iconNode, this.textNode];
12790 this.wrap.style.display = "none";
12796 this.wrap.style.display = "";
12800 onContextMenu : function(e){
12801 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12802 e.preventDefault();
12804 this.fireEvent("contextmenu", this.node, e);
12808 onClick : function(e){
12813 if(this.fireEvent("beforeclick", this.node, e) !== false){
12814 if(!this.disabled && this.node.attributes.href){
12815 this.fireEvent("click", this.node, e);
12818 e.preventDefault();
12823 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12824 this.node.toggle();
12827 this.fireEvent("click", this.node, e);
12833 onDblClick : function(e){
12834 e.preventDefault();
12839 this.toggleCheck();
12841 if(!this.animating && this.node.hasChildNodes()){
12842 this.node.toggle();
12844 this.fireEvent("dblclick", this.node, e);
12847 onCheckChange : function(){
12848 var checked = this.checkbox.checked;
12849 this.node.attributes.checked = checked;
12850 this.fireEvent('checkchange', this.node, checked);
12853 ecClick : function(e){
12854 if(!this.animating && this.node.hasChildNodes()){
12855 this.node.toggle();
12859 startDrop : function(){
12860 this.dropping = true;
12863 // delayed drop so the click event doesn't get fired on a drop
12864 endDrop : function(){
12865 setTimeout(function(){
12866 this.dropping = false;
12867 }.createDelegate(this), 50);
12870 expand : function(){
12871 this.updateExpandIcon();
12872 this.ctNode.style.display = "";
12875 focus : function(){
12876 if(!this.node.preventHScroll){
12877 try{this.anchor.focus();
12879 }else if(!Roo.isIE){
12881 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12882 var l = noscroll.scrollLeft;
12883 this.anchor.focus();
12884 noscroll.scrollLeft = l;
12889 toggleCheck : function(value){
12890 var cb = this.checkbox;
12892 cb.checked = (value === undefined ? !cb.checked : value);
12898 this.anchor.blur();
12902 animExpand : function(callback){
12903 var ct = Roo.get(this.ctNode);
12905 if(!this.node.hasChildNodes()){
12906 this.updateExpandIcon();
12907 this.ctNode.style.display = "";
12908 Roo.callback(callback);
12911 this.animating = true;
12912 this.updateExpandIcon();
12915 callback : function(){
12916 this.animating = false;
12917 Roo.callback(callback);
12920 duration: this.node.ownerTree.duration || .25
12924 highlight : function(){
12925 var tree = this.node.getOwnerTree();
12926 Roo.fly(this.wrap).highlight(
12927 tree.hlColor || "C3DAF9",
12928 {endColor: tree.hlBaseColor}
12932 collapse : function(){
12933 this.updateExpandIcon();
12934 this.ctNode.style.display = "none";
12937 animCollapse : function(callback){
12938 var ct = Roo.get(this.ctNode);
12939 ct.enableDisplayMode('block');
12942 this.animating = true;
12943 this.updateExpandIcon();
12946 callback : function(){
12947 this.animating = false;
12948 Roo.callback(callback);
12951 duration: this.node.ownerTree.duration || .25
12955 getContainer : function(){
12956 return this.ctNode;
12959 getEl : function(){
12963 appendDDGhost : function(ghostNode){
12964 ghostNode.appendChild(this.elNode.cloneNode(true));
12967 getDDRepairXY : function(){
12968 return Roo.lib.Dom.getXY(this.iconNode);
12971 onRender : function(){
12975 render : function(bulkRender){
12976 var n = this.node, a = n.attributes;
12977 var targetNode = n.parentNode ?
12978 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
12980 if(!this.rendered){
12981 this.rendered = true;
12983 this.renderElements(n, a, targetNode, bulkRender);
12986 if(this.textNode.setAttributeNS){
12987 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
12989 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
12992 this.textNode.setAttribute("ext:qtip", a.qtip);
12994 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
12997 }else if(a.qtipCfg){
12998 a.qtipCfg.target = Roo.id(this.textNode);
12999 Roo.QuickTips.register(a.qtipCfg);
13002 if(!this.node.expanded){
13003 this.updateExpandIcon();
13006 if(bulkRender === true) {
13007 targetNode.appendChild(this.wrap);
13012 renderElements : function(n, a, targetNode, bulkRender)
13014 // add some indent caching, this helps performance when rendering a large tree
13015 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13016 var t = n.getOwnerTree();
13017 var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13018 if (typeof(n.attributes.html) != 'undefined') {
13019 txt = n.attributes.html;
13021 var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13022 var cb = typeof a.checked == 'boolean';
13023 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13024 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13025 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13026 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13027 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13028 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13029 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13030 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
13031 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13032 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13035 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13036 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13037 n.nextSibling.ui.getEl(), buf.join(""));
13039 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13042 this.elNode = this.wrap.childNodes[0];
13043 this.ctNode = this.wrap.childNodes[1];
13044 var cs = this.elNode.childNodes;
13045 this.indentNode = cs[0];
13046 this.ecNode = cs[1];
13047 this.iconNode = cs[2];
13050 this.checkbox = cs[3];
13053 this.anchor = cs[index];
13054 this.textNode = cs[index].firstChild;
13057 getAnchor : function(){
13058 return this.anchor;
13061 getTextEl : function(){
13062 return this.textNode;
13065 getIconEl : function(){
13066 return this.iconNode;
13069 isChecked : function(){
13070 return this.checkbox ? this.checkbox.checked : false;
13073 updateExpandIcon : function(){
13075 var n = this.node, c1, c2;
13076 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13077 var hasChild = n.hasChildNodes();
13081 c1 = "x-tree-node-collapsed";
13082 c2 = "x-tree-node-expanded";
13085 c1 = "x-tree-node-expanded";
13086 c2 = "x-tree-node-collapsed";
13089 this.removeClass("x-tree-node-leaf");
13090 this.wasLeaf = false;
13092 if(this.c1 != c1 || this.c2 != c2){
13093 Roo.fly(this.elNode).replaceClass(c1, c2);
13094 this.c1 = c1; this.c2 = c2;
13097 // this changes non-leafs into leafs if they have no children.
13098 // it's not very rational behaviour..
13100 if(!this.wasLeaf && this.node.leaf){
13101 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13104 this.wasLeaf = true;
13107 var ecc = "x-tree-ec-icon "+cls;
13108 if(this.ecc != ecc){
13109 this.ecNode.className = ecc;
13115 getChildIndent : function(){
13116 if(!this.childIndent){
13120 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13122 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13124 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13129 this.childIndent = buf.join("");
13131 return this.childIndent;
13134 renderIndent : function(){
13137 var p = this.node.parentNode;
13139 indent = p.ui.getChildIndent();
13141 if(this.indentMarkup != indent){ // don't rerender if not required
13142 this.indentNode.innerHTML = indent;
13143 this.indentMarkup = indent;
13145 this.updateExpandIcon();
13150 Roo.tree.RootTreeNodeUI = function(){
13151 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13153 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13154 render : function(){
13155 if(!this.rendered){
13156 var targetNode = this.node.ownerTree.innerCt.dom;
13157 this.node.expanded = true;
13158 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13159 this.wrap = this.ctNode = targetNode.firstChild;
13162 collapse : function(){
13164 expand : function(){
13168 * Ext JS Library 1.1.1
13169 * Copyright(c) 2006-2007, Ext JS, LLC.
13171 * Originally Released Under LGPL - original licence link has changed is not relivant.
13174 * <script type="text/javascript">
13177 * @class Roo.tree.TreeLoader
13178 * @extends Roo.util.Observable
13179 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13180 * nodes from a specified URL. The response must be a javascript Array definition
13181 * who's elements are node definition objects. eg:
13186 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13187 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13194 * The old style respose with just an array is still supported, but not recommended.
13197 * A server request is sent, and child nodes are loaded only when a node is expanded.
13198 * The loading node's id is passed to the server under the parameter name "node" to
13199 * enable the server to produce the correct child nodes.
13201 * To pass extra parameters, an event handler may be attached to the "beforeload"
13202 * event, and the parameters specified in the TreeLoader's baseParams property:
13204 myTreeLoader.on("beforeload", function(treeLoader, node) {
13205 this.baseParams.category = node.attributes.category;
13210 * This would pass an HTTP parameter called "category" to the server containing
13211 * the value of the Node's "category" attribute.
13213 * Creates a new Treeloader.
13214 * @param {Object} config A config object containing config properties.
13216 Roo.tree.TreeLoader = function(config){
13217 this.baseParams = {};
13218 this.requestMethod = "POST";
13219 Roo.apply(this, config);
13224 * @event beforeload
13225 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13226 * @param {Object} This TreeLoader object.
13227 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13228 * @param {Object} callback The callback function specified in the {@link #load} call.
13233 * Fires when the node has been successfuly loaded.
13234 * @param {Object} This TreeLoader object.
13235 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13236 * @param {Object} response The response object containing the data from the server.
13240 * @event loadexception
13241 * Fires if the network request failed.
13242 * @param {Object} This TreeLoader object.
13243 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13244 * @param {Object} response The response object containing the data from the server.
13246 loadexception : true,
13249 * Fires before a node is created, enabling you to return custom Node types
13250 * @param {Object} This TreeLoader object.
13251 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13256 Roo.tree.TreeLoader.superclass.constructor.call(this);
13259 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13261 * @cfg {String} dataUrl The URL from which to request a Json string which
13262 * specifies an array of node definition object representing the child nodes
13266 * @cfg {String} requestMethod either GET or POST
13267 * defaults to POST (due to BC)
13271 * @cfg {Object} baseParams (optional) An object containing properties which
13272 * specify HTTP parameters to be passed to each request for child nodes.
13275 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13276 * created by this loader. If the attributes sent by the server have an attribute in this object,
13277 * they take priority.
13280 * @cfg {Object} uiProviders (optional) An object containing properties which
13282 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13283 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13284 * <i>uiProvider</i> attribute of a returned child node is a string rather
13285 * than a reference to a TreeNodeUI implementation, this that string value
13286 * is used as a property name in the uiProviders object. You can define the provider named
13287 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13292 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13293 * child nodes before loading.
13295 clearOnLoad : true,
13298 * @cfg {String} root (optional) Default to false. Use this to read data from an object
13299 * property on loading, rather than expecting an array. (eg. more compatible to a standard
13300 * Grid query { data : [ .....] }
13305 * @cfg {String} queryParam (optional)
13306 * Name of the query as it will be passed on the querystring (defaults to 'node')
13307 * eg. the request will be ?node=[id]
13314 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13315 * This is called automatically when a node is expanded, but may be used to reload
13316 * a node (or append new children if the {@link #clearOnLoad} option is false.)
13317 * @param {Roo.tree.TreeNode} node
13318 * @param {Function} callback
13320 load : function(node, callback){
13321 if(this.clearOnLoad){
13322 while(node.firstChild){
13323 node.removeChild(node.firstChild);
13326 if(node.attributes.children){ // preloaded json children
13327 var cs = node.attributes.children;
13328 for(var i = 0, len = cs.length; i < len; i++){
13329 node.appendChild(this.createNode(cs[i]));
13331 if(typeof callback == "function"){
13334 }else if(this.dataUrl){
13335 this.requestData(node, callback);
13339 getParams: function(node){
13340 var buf = [], bp = this.baseParams;
13341 for(var key in bp){
13342 if(typeof bp[key] != "function"){
13343 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13346 var n = this.queryParam === false ? 'node' : this.queryParam;
13347 buf.push(n + "=", encodeURIComponent(node.id));
13348 return buf.join("");
13351 requestData : function(node, callback){
13352 if(this.fireEvent("beforeload", this, node, callback) !== false){
13353 this.transId = Roo.Ajax.request({
13354 method:this.requestMethod,
13355 url: this.dataUrl||this.url,
13356 success: this.handleResponse,
13357 failure: this.handleFailure,
13359 argument: {callback: callback, node: node},
13360 params: this.getParams(node)
13363 // if the load is cancelled, make sure we notify
13364 // the node that we are done
13365 if(typeof callback == "function"){
13371 isLoading : function(){
13372 return this.transId ? true : false;
13375 abort : function(){
13376 if(this.isLoading()){
13377 Roo.Ajax.abort(this.transId);
13382 createNode : function(attr)
13384 // apply baseAttrs, nice idea Corey!
13385 if(this.baseAttrs){
13386 Roo.applyIf(attr, this.baseAttrs);
13388 if(this.applyLoader !== false){
13389 attr.loader = this;
13391 // uiProvider = depreciated..
13393 if(typeof(attr.uiProvider) == 'string'){
13394 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
13395 /** eval:var:attr */ eval(attr.uiProvider);
13397 if(typeof(this.uiProviders['default']) != 'undefined') {
13398 attr.uiProvider = this.uiProviders['default'];
13401 this.fireEvent('create', this, attr);
13403 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13405 new Roo.tree.TreeNode(attr) :
13406 new Roo.tree.AsyncTreeNode(attr));
13409 processResponse : function(response, node, callback)
13411 var json = response.responseText;
13414 var o = Roo.decode(json);
13416 if (this.root === false && typeof(o.success) != undefined) {
13417 this.root = 'data'; // the default behaviour for list like data..
13420 if (this.root !== false && !o.success) {
13421 // it's a failure condition.
13422 var a = response.argument;
13423 this.fireEvent("loadexception", this, a.node, response);
13424 Roo.log("Load failed - should have a handler really");
13430 if (this.root !== false) {
13434 for(var i = 0, len = o.length; i < len; i++){
13435 var n = this.createNode(o[i]);
13437 node.appendChild(n);
13440 if(typeof callback == "function"){
13441 callback(this, node);
13444 this.handleFailure(response);
13448 handleResponse : function(response){
13449 this.transId = false;
13450 var a = response.argument;
13451 this.processResponse(response, a.node, a.callback);
13452 this.fireEvent("load", this, a.node, response);
13455 handleFailure : function(response)
13457 // should handle failure better..
13458 this.transId = false;
13459 var a = response.argument;
13460 this.fireEvent("loadexception", this, a.node, response);
13461 if(typeof a.callback == "function"){
13462 a.callback(this, a.node);
13467 * Ext JS Library 1.1.1
13468 * Copyright(c) 2006-2007, Ext JS, LLC.
13470 * Originally Released Under LGPL - original licence link has changed is not relivant.
13473 * <script type="text/javascript">
13477 * @class Roo.tree.TreeFilter
13478 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13479 * @param {TreePanel} tree
13480 * @param {Object} config (optional)
13482 Roo.tree.TreeFilter = function(tree, config){
13484 this.filtered = {};
13485 Roo.apply(this, config);
13488 Roo.tree.TreeFilter.prototype = {
13495 * Filter the data by a specific attribute.
13496 * @param {String/RegExp} value Either string that the attribute value
13497 * should start with or a RegExp to test against the attribute
13498 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13499 * @param {TreeNode} startNode (optional) The node to start the filter at.
13501 filter : function(value, attr, startNode){
13502 attr = attr || "text";
13504 if(typeof value == "string"){
13505 var vlen = value.length;
13506 // auto clear empty filter
13507 if(vlen == 0 && this.clearBlank){
13511 value = value.toLowerCase();
13513 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13515 }else if(value.exec){ // regex?
13517 return value.test(n.attributes[attr]);
13520 throw 'Illegal filter type, must be string or regex';
13522 this.filterBy(f, null, startNode);
13526 * Filter by a function. The passed function will be called with each
13527 * node in the tree (or from the startNode). If the function returns true, the node is kept
13528 * otherwise it is filtered. If a node is filtered, its children are also filtered.
13529 * @param {Function} fn The filter function
13530 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13532 filterBy : function(fn, scope, startNode){
13533 startNode = startNode || this.tree.root;
13534 if(this.autoClear){
13537 var af = this.filtered, rv = this.reverse;
13538 var f = function(n){
13539 if(n == startNode){
13545 var m = fn.call(scope || n, n);
13553 startNode.cascade(f);
13556 if(typeof id != "function"){
13558 if(n && n.parentNode){
13559 n.parentNode.removeChild(n);
13567 * Clears the current filter. Note: with the "remove" option
13568 * set a filter cannot be cleared.
13570 clear : function(){
13572 var af = this.filtered;
13574 if(typeof id != "function"){
13581 this.filtered = {};
13586 * Ext JS Library 1.1.1
13587 * Copyright(c) 2006-2007, Ext JS, LLC.
13589 * Originally Released Under LGPL - original licence link has changed is not relivant.
13592 * <script type="text/javascript">
13597 * @class Roo.tree.TreeSorter
13598 * Provides sorting of nodes in a TreePanel
13600 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13601 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13602 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13603 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13604 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13605 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13607 * @param {TreePanel} tree
13608 * @param {Object} config
13610 Roo.tree.TreeSorter = function(tree, config){
13611 Roo.apply(this, config);
13612 tree.on("beforechildrenrendered", this.doSort, this);
13613 tree.on("append", this.updateSort, this);
13614 tree.on("insert", this.updateSort, this);
13616 var dsc = this.dir && this.dir.toLowerCase() == "desc";
13617 var p = this.property || "text";
13618 var sortType = this.sortType;
13619 var fs = this.folderSort;
13620 var cs = this.caseSensitive === true;
13621 var leafAttr = this.leafAttr || 'leaf';
13623 this.sortFn = function(n1, n2){
13625 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13628 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13632 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13633 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13635 return dsc ? +1 : -1;
13637 return dsc ? -1 : +1;
13644 Roo.tree.TreeSorter.prototype = {
13645 doSort : function(node){
13646 node.sort(this.sortFn);
13649 compareNodes : function(n1, n2){
13650 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13653 updateSort : function(tree, node){
13654 if(node.childrenRendered){
13655 this.doSort.defer(1, this, [node]);
13660 * Ext JS Library 1.1.1
13661 * Copyright(c) 2006-2007, Ext JS, LLC.
13663 * Originally Released Under LGPL - original licence link has changed is not relivant.
13666 * <script type="text/javascript">
13669 if(Roo.dd.DropZone){
13671 Roo.tree.TreeDropZone = function(tree, config){
13672 this.allowParentInsert = false;
13673 this.allowContainerDrop = false;
13674 this.appendOnly = false;
13675 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13677 this.lastInsertClass = "x-tree-no-status";
13678 this.dragOverData = {};
13681 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13682 ddGroup : "TreeDD",
13685 expandDelay : 1000,
13687 expandNode : function(node){
13688 if(node.hasChildNodes() && !node.isExpanded()){
13689 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13693 queueExpand : function(node){
13694 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13697 cancelExpand : function(){
13698 if(this.expandProcId){
13699 clearTimeout(this.expandProcId);
13700 this.expandProcId = false;
13704 isValidDropPoint : function(n, pt, dd, e, data){
13705 if(!n || !data){ return false; }
13706 var targetNode = n.node;
13707 var dropNode = data.node;
13708 // default drop rules
13709 if(!(targetNode && targetNode.isTarget && pt)){
13712 if(pt == "append" && targetNode.allowChildren === false){
13715 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13718 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13721 // reuse the object
13722 var overEvent = this.dragOverData;
13723 overEvent.tree = this.tree;
13724 overEvent.target = targetNode;
13725 overEvent.data = data;
13726 overEvent.point = pt;
13727 overEvent.source = dd;
13728 overEvent.rawEvent = e;
13729 overEvent.dropNode = dropNode;
13730 overEvent.cancel = false;
13731 var result = this.tree.fireEvent("nodedragover", overEvent);
13732 return overEvent.cancel === false && result !== false;
13735 getDropPoint : function(e, n, dd)
13739 return tn.allowChildren !== false ? "append" : false; // always append for root
13741 var dragEl = n.ddel;
13742 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13743 var y = Roo.lib.Event.getPageY(e);
13744 //var noAppend = tn.allowChildren === false || tn.isLeaf();
13746 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13747 var noAppend = tn.allowChildren === false;
13748 if(this.appendOnly || tn.parentNode.allowChildren === false){
13749 return noAppend ? false : "append";
13751 var noBelow = false;
13752 if(!this.allowParentInsert){
13753 noBelow = tn.hasChildNodes() && tn.isExpanded();
13755 var q = (b - t) / (noAppend ? 2 : 3);
13756 if(y >= t && y < (t + q)){
13758 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13765 onNodeEnter : function(n, dd, e, data)
13767 this.cancelExpand();
13770 onNodeOver : function(n, dd, e, data)
13773 var pt = this.getDropPoint(e, n, dd);
13776 // auto node expand check
13777 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13778 this.queueExpand(node);
13779 }else if(pt != "append"){
13780 this.cancelExpand();
13783 // set the insert point style on the target node
13784 var returnCls = this.dropNotAllowed;
13785 if(this.isValidDropPoint(n, pt, dd, e, data)){
13790 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13791 cls = "x-tree-drag-insert-above";
13792 }else if(pt == "below"){
13793 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13794 cls = "x-tree-drag-insert-below";
13796 returnCls = "x-tree-drop-ok-append";
13797 cls = "x-tree-drag-append";
13799 if(this.lastInsertClass != cls){
13800 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13801 this.lastInsertClass = cls;
13808 onNodeOut : function(n, dd, e, data){
13810 this.cancelExpand();
13811 this.removeDropIndicators(n);
13814 onNodeDrop : function(n, dd, e, data){
13815 var point = this.getDropPoint(e, n, dd);
13816 var targetNode = n.node;
13817 targetNode.ui.startDrop();
13818 if(!this.isValidDropPoint(n, point, dd, e, data)){
13819 targetNode.ui.endDrop();
13822 // first try to find the drop node
13823 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13826 target: targetNode,
13831 dropNode: dropNode,
13834 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13835 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13836 targetNode.ui.endDrop();
13839 // allow target changing
13840 targetNode = dropEvent.target;
13841 if(point == "append" && !targetNode.isExpanded()){
13842 targetNode.expand(false, null, function(){
13843 this.completeDrop(dropEvent);
13844 }.createDelegate(this));
13846 this.completeDrop(dropEvent);
13851 completeDrop : function(de){
13852 var ns = de.dropNode, p = de.point, t = de.target;
13853 if(!(ns instanceof Array)){
13857 for(var i = 0, len = ns.length; i < len; i++){
13860 t.parentNode.insertBefore(n, t);
13861 }else if(p == "below"){
13862 t.parentNode.insertBefore(n, t.nextSibling);
13868 if(this.tree.hlDrop){
13872 this.tree.fireEvent("nodedrop", de);
13875 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13876 if(this.tree.hlDrop){
13877 dropNode.ui.focus();
13878 dropNode.ui.highlight();
13880 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13883 getTree : function(){
13887 removeDropIndicators : function(n){
13890 Roo.fly(el).removeClass([
13891 "x-tree-drag-insert-above",
13892 "x-tree-drag-insert-below",
13893 "x-tree-drag-append"]);
13894 this.lastInsertClass = "_noclass";
13898 beforeDragDrop : function(target, e, id){
13899 this.cancelExpand();
13903 afterRepair : function(data){
13904 if(data && Roo.enableFx){
13905 data.node.ui.highlight();
13915 * Ext JS Library 1.1.1
13916 * Copyright(c) 2006-2007, Ext JS, LLC.
13918 * Originally Released Under LGPL - original licence link has changed is not relivant.
13921 * <script type="text/javascript">
13925 if(Roo.dd.DragZone){
13926 Roo.tree.TreeDragZone = function(tree, config){
13927 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13931 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13932 ddGroup : "TreeDD",
13934 onBeforeDrag : function(data, e){
13936 return n && n.draggable && !n.disabled;
13940 onInitDrag : function(e){
13941 var data = this.dragData;
13942 this.tree.getSelectionModel().select(data.node);
13943 this.proxy.update("");
13944 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13945 this.tree.fireEvent("startdrag", this.tree, data.node, e);
13948 getRepairXY : function(e, data){
13949 return data.node.ui.getDDRepairXY();
13952 onEndDrag : function(data, e){
13953 this.tree.fireEvent("enddrag", this.tree, data.node, e);
13958 onValidDrop : function(dd, e, id){
13959 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13963 beforeInvalidDrop : function(e, id){
13964 // this scrolls the original position back into view
13965 var sm = this.tree.getSelectionModel();
13966 sm.clearSelections();
13967 sm.select(this.dragData.node);
13972 * Ext JS Library 1.1.1
13973 * Copyright(c) 2006-2007, Ext JS, LLC.
13975 * Originally Released Under LGPL - original licence link has changed is not relivant.
13978 * <script type="text/javascript">
13981 * @class Roo.tree.TreeEditor
13982 * @extends Roo.Editor
13983 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
13984 * as the editor field.
13986 * @param {Object} config (used to be the tree panel.)
13987 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
13989 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
13990 * @cfg {Roo.form.TextField} field [required] The field configuration
13994 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
13997 if (oldconfig) { // old style..
13998 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14001 tree = config.tree;
14002 config.field = config.field || {};
14003 config.field.xtype = 'TextField';
14004 field = Roo.factory(config.field, Roo.form);
14006 config = config || {};
14011 * @event beforenodeedit
14012 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
14013 * false from the handler of this event.
14014 * @param {Editor} this
14015 * @param {Roo.tree.Node} node
14017 "beforenodeedit" : true
14021 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14025 tree.on('beforeclick', this.beforeNodeClick, this);
14026 tree.getTreeEl().on('mousedown', this.hide, this);
14027 this.on('complete', this.updateNode, this);
14028 this.on('beforestartedit', this.fitToTree, this);
14029 this.on('startedit', this.bindScroll, this, {delay:10});
14030 this.on('specialkey', this.onSpecialKey, this);
14033 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14035 * @cfg {String} alignment
14036 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14042 * @cfg {Boolean} hideEl
14043 * True to hide the bound element while the editor is displayed (defaults to false)
14047 * @cfg {String} cls
14048 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14050 cls: "x-small-editor x-tree-editor",
14052 * @cfg {Boolean} shim
14053 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14059 * @cfg {Number} maxWidth
14060 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
14061 * the containing tree element's size, it will be automatically limited for you to the container width, taking
14062 * scroll and client offsets into account prior to each edit.
14069 fitToTree : function(ed, el){
14070 var td = this.tree.getTreeEl().dom, nd = el.dom;
14071 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
14072 td.scrollLeft = nd.offsetLeft;
14076 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14077 this.setSize(w, '');
14079 return this.fireEvent('beforenodeedit', this, this.editNode);
14084 triggerEdit : function(node){
14085 this.completeEdit();
14086 this.editNode = node;
14087 this.startEdit(node.ui.textNode, node.text);
14091 bindScroll : function(){
14092 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14096 beforeNodeClick : function(node, e){
14097 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14098 this.lastClick = new Date();
14099 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14101 this.triggerEdit(node);
14108 updateNode : function(ed, value){
14109 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14110 this.editNode.setText(value);
14114 onHide : function(){
14115 Roo.tree.TreeEditor.superclass.onHide.call(this);
14117 this.editNode.ui.focus();
14122 onSpecialKey : function(field, e){
14123 var k = e.getKey();
14127 }else if(k == e.ENTER && !e.hasModifier()){
14129 this.completeEdit();
14132 });//<Script type="text/javascript">
14135 * Ext JS Library 1.1.1
14136 * Copyright(c) 2006-2007, Ext JS, LLC.
14138 * Originally Released Under LGPL - original licence link has changed is not relivant.
14141 * <script type="text/javascript">
14145 * Not documented??? - probably should be...
14148 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14149 //focus: Roo.emptyFn, // prevent odd scrolling behavior
14151 renderElements : function(n, a, targetNode, bulkRender){
14152 //consel.log("renderElements?");
14153 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14155 var t = n.getOwnerTree();
14156 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14158 var cols = t.columns;
14159 var bw = t.borderWidth;
14161 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14162 var cb = typeof a.checked == "boolean";
14163 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14164 var colcls = 'x-t-' + tid + '-c0';
14166 '<li class="x-tree-node">',
14169 '<div class="x-tree-node-el ', a.cls,'">',
14171 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14174 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14175 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
14176 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14177 (a.icon ? ' x-tree-node-inline-icon' : ''),
14178 (a.iconCls ? ' '+a.iconCls : ''),
14179 '" unselectable="on" />',
14180 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
14181 (a.checked ? 'checked="checked" />' : ' />')) : ''),
14183 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14184 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14185 '<span unselectable="on" qtip="' + tx + '">',
14189 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14190 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14192 for(var i = 1, len = cols.length; i < len; i++){
14194 colcls = 'x-t-' + tid + '-c' +i;
14195 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14196 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14197 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14203 '<div class="x-clear"></div></div>',
14204 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14207 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14208 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14209 n.nextSibling.ui.getEl(), buf.join(""));
14211 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14213 var el = this.wrap.firstChild;
14215 this.elNode = el.firstChild;
14216 this.ranchor = el.childNodes[1];
14217 this.ctNode = this.wrap.childNodes[1];
14218 var cs = el.firstChild.childNodes;
14219 this.indentNode = cs[0];
14220 this.ecNode = cs[1];
14221 this.iconNode = cs[2];
14224 this.checkbox = cs[3];
14227 this.anchor = cs[index];
14229 this.textNode = cs[index].firstChild;
14231 //el.on("click", this.onClick, this);
14232 //el.on("dblclick", this.onDblClick, this);
14235 // console.log(this);
14237 initEvents : function(){
14238 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14241 var a = this.ranchor;
14243 var el = Roo.get(a);
14245 if(Roo.isOpera){ // opera render bug ignores the CSS
14246 el.setStyle("text-decoration", "none");
14249 el.on("click", this.onClick, this);
14250 el.on("dblclick", this.onDblClick, this);
14251 el.on("contextmenu", this.onContextMenu, this);
14255 /*onSelectedChange : function(state){
14258 this.addClass("x-tree-selected");
14261 this.removeClass("x-tree-selected");
14264 addClass : function(cls){
14266 Roo.fly(this.elRow).addClass(cls);
14272 removeClass : function(cls){
14274 Roo.fly(this.elRow).removeClass(cls);
14280 });//<Script type="text/javascript">
14284 * Ext JS Library 1.1.1
14285 * Copyright(c) 2006-2007, Ext JS, LLC.
14287 * Originally Released Under LGPL - original licence link has changed is not relivant.
14290 * <script type="text/javascript">
14295 * @class Roo.tree.ColumnTree
14296 * @extends Roo.tree.TreePanel
14297 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
14298 * @cfg {int} borderWidth compined right/left border allowance
14300 * @param {String/HTMLElement/Element} el The container element
14301 * @param {Object} config
14303 Roo.tree.ColumnTree = function(el, config)
14305 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14309 * Fire this event on a container when it resizes
14310 * @param {int} w Width
14311 * @param {int} h Height
14315 this.on('resize', this.onResize, this);
14318 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14322 borderWidth: Roo.isBorderBox ? 0 : 2,
14325 render : function(){
14326 // add the header.....
14328 Roo.tree.ColumnTree.superclass.render.apply(this);
14330 this.el.addClass('x-column-tree');
14332 this.headers = this.el.createChild(
14333 {cls:'x-tree-headers'},this.innerCt.dom);
14335 var cols = this.columns, c;
14336 var totalWidth = 0;
14338 var len = cols.length;
14339 for(var i = 0; i < len; i++){
14341 totalWidth += c.width;
14342 this.headEls.push(this.headers.createChild({
14343 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14345 cls:'x-tree-hd-text',
14348 style:'width:'+(c.width-this.borderWidth)+'px;'
14351 this.headers.createChild({cls:'x-clear'});
14352 // prevent floats from wrapping when clipped
14353 this.headers.setWidth(totalWidth);
14354 //this.innerCt.setWidth(totalWidth);
14355 this.innerCt.setStyle({ overflow: 'auto' });
14356 this.onResize(this.width, this.height);
14360 onResize : function(w,h)
14365 this.innerCt.setWidth(this.width);
14366 this.innerCt.setHeight(this.height-20);
14369 var cols = this.columns, c;
14370 var totalWidth = 0;
14372 var len = cols.length;
14373 for(var i = 0; i < len; i++){
14375 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14376 // it's the expander..
14377 expEl = this.headEls[i];
14380 totalWidth += c.width;
14384 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
14386 this.headers.setWidth(w-20);
14395 * Ext JS Library 1.1.1
14396 * Copyright(c) 2006-2007, Ext JS, LLC.
14398 * Originally Released Under LGPL - original licence link has changed is not relivant.
14401 * <script type="text/javascript">
14405 * @class Roo.menu.Menu
14406 * @extends Roo.util.Observable
14407 * @children Roo.menu.BaseItem
14408 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
14409 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14411 * Creates a new Menu
14412 * @param {Object} config Configuration options
14414 Roo.menu.Menu = function(config){
14416 Roo.menu.Menu.superclass.constructor.call(this, config);
14418 this.id = this.id || Roo.id();
14421 * @event beforeshow
14422 * Fires before this menu is displayed
14423 * @param {Roo.menu.Menu} this
14427 * @event beforehide
14428 * Fires before this menu is hidden
14429 * @param {Roo.menu.Menu} this
14434 * Fires after this menu is displayed
14435 * @param {Roo.menu.Menu} this
14440 * Fires after this menu is hidden
14441 * @param {Roo.menu.Menu} this
14446 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14447 * @param {Roo.menu.Menu} this
14448 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14449 * @param {Roo.EventObject} e
14454 * Fires when the mouse is hovering over this menu
14455 * @param {Roo.menu.Menu} this
14456 * @param {Roo.EventObject} e
14457 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14462 * Fires when the mouse exits this menu
14463 * @param {Roo.menu.Menu} this
14464 * @param {Roo.EventObject} e
14465 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14470 * Fires when a menu item contained in this menu is clicked
14471 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14472 * @param {Roo.EventObject} e
14476 if (this.registerMenu) {
14477 Roo.menu.MenuMgr.register(this);
14480 var mis = this.items;
14481 this.items = new Roo.util.MixedCollection();
14483 this.add.apply(this, mis);
14487 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14489 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14493 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14494 * for bottom-right shadow (defaults to "sides")
14498 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14499 * this menu (defaults to "tl-tr?")
14501 subMenuAlign : "tl-tr?",
14503 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14504 * relative to its element of origin (defaults to "tl-bl?")
14506 defaultAlign : "tl-bl?",
14508 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14510 allowOtherMenus : false,
14512 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14514 registerMenu : true,
14519 render : function(){
14523 var el = this.el = new Roo.Layer({
14525 shadow:this.shadow,
14527 parentEl: this.parentEl || document.body,
14531 this.keyNav = new Roo.menu.MenuNav(this);
14534 el.addClass("x-menu-plain");
14537 el.addClass(this.cls);
14539 // generic focus element
14540 this.focusEl = el.createChild({
14541 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14543 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14544 //disabling touch- as it's causing issues ..
14545 //ul.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
14546 ul.on('click' , this.onClick, this);
14549 ul.on("mouseover", this.onMouseOver, this);
14550 ul.on("mouseout", this.onMouseOut, this);
14551 this.items.each(function(item){
14556 var li = document.createElement("li");
14557 li.className = "x-menu-list-item";
14558 ul.dom.appendChild(li);
14559 item.render(li, this);
14566 autoWidth : function(){
14567 var el = this.el, ul = this.ul;
14571 var w = this.width;
14574 }else if(Roo.isIE){
14575 el.setWidth(this.minWidth);
14576 var t = el.dom.offsetWidth; // force recalc
14577 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14582 delayAutoWidth : function(){
14585 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14587 this.awTask.delay(20);
14592 findTargetItem : function(e){
14593 var t = e.getTarget(".x-menu-list-item", this.ul, true);
14594 if(t && t.menuItemId){
14595 return this.items.get(t.menuItemId);
14600 onClick : function(e){
14601 Roo.log("menu.onClick");
14602 var t = this.findTargetItem(e);
14607 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
14608 if(t == this.activeItem && t.shouldDeactivate(e)){
14609 this.activeItem.deactivate();
14610 delete this.activeItem;
14614 this.setActiveItem(t, true);
14622 this.fireEvent("click", this, t, e);
14626 setActiveItem : function(item, autoExpand){
14627 if(item != this.activeItem){
14628 if(this.activeItem){
14629 this.activeItem.deactivate();
14631 this.activeItem = item;
14632 item.activate(autoExpand);
14633 }else if(autoExpand){
14639 tryActivate : function(start, step){
14640 var items = this.items;
14641 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14642 var item = items.get(i);
14643 if(!item.disabled && item.canActivate){
14644 this.setActiveItem(item, false);
14652 onMouseOver : function(e){
14654 if(t = this.findTargetItem(e)){
14655 if(t.canActivate && !t.disabled){
14656 this.setActiveItem(t, true);
14659 this.fireEvent("mouseover", this, e, t);
14663 onMouseOut : function(e){
14665 if(t = this.findTargetItem(e)){
14666 if(t == this.activeItem && t.shouldDeactivate(e)){
14667 this.activeItem.deactivate();
14668 delete this.activeItem;
14671 this.fireEvent("mouseout", this, e, t);
14675 * Read-only. Returns true if the menu is currently displayed, else false.
14678 isVisible : function(){
14679 return this.el && !this.hidden;
14683 * Displays this menu relative to another element
14684 * @param {String/HTMLElement/Roo.Element} element The element to align to
14685 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14686 * the element (defaults to this.defaultAlign)
14687 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14689 show : function(el, pos, parentMenu){
14690 this.parentMenu = parentMenu;
14694 this.fireEvent("beforeshow", this);
14695 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14699 * Displays this menu at a specific xy position
14700 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14701 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14703 showAt : function(xy, parentMenu, /* private: */_e){
14704 this.parentMenu = parentMenu;
14709 this.fireEvent("beforeshow", this);
14710 xy = this.el.adjustForConstraints(xy);
14714 this.hidden = false;
14716 this.fireEvent("show", this);
14719 focus : function(){
14721 this.doFocus.defer(50, this);
14725 doFocus : function(){
14727 this.focusEl.focus();
14732 * Hides this menu and optionally all parent menus
14733 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14735 hide : function(deep){
14736 if(this.el && this.isVisible()){
14737 this.fireEvent("beforehide", this);
14738 if(this.activeItem){
14739 this.activeItem.deactivate();
14740 this.activeItem = null;
14743 this.hidden = true;
14744 this.fireEvent("hide", this);
14746 if(deep === true && this.parentMenu){
14747 this.parentMenu.hide(true);
14752 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14753 * Any of the following are valid:
14755 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14756 * <li>An HTMLElement object which will be converted to a menu item</li>
14757 * <li>A menu item config object that will be created as a new menu item</li>
14758 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14759 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14764 var menu = new Roo.menu.Menu();
14766 // Create a menu item to add by reference
14767 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14769 // Add a bunch of items at once using different methods.
14770 // Only the last item added will be returned.
14771 var item = menu.add(
14772 menuItem, // add existing item by ref
14773 'Dynamic Item', // new TextItem
14774 '-', // new separator
14775 { text: 'Config Item' } // new item by config
14778 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14779 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14782 var a = arguments, l = a.length, item;
14783 for(var i = 0; i < l; i++){
14785 if ((typeof(el) == "object") && el.xtype && el.xns) {
14786 el = Roo.factory(el, Roo.menu);
14789 if(el.render){ // some kind of Item
14790 item = this.addItem(el);
14791 }else if(typeof el == "string"){ // string
14792 if(el == "separator" || el == "-"){
14793 item = this.addSeparator();
14795 item = this.addText(el);
14797 }else if(el.tagName || el.el){ // element
14798 item = this.addElement(el);
14799 }else if(typeof el == "object"){ // must be menu item config?
14800 item = this.addMenuItem(el);
14807 * Returns this menu's underlying {@link Roo.Element} object
14808 * @return {Roo.Element} The element
14810 getEl : function(){
14818 * Adds a separator bar to the menu
14819 * @return {Roo.menu.Item} The menu item that was added
14821 addSeparator : function(){
14822 return this.addItem(new Roo.menu.Separator());
14826 * Adds an {@link Roo.Element} object to the menu
14827 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14828 * @return {Roo.menu.Item} The menu item that was added
14830 addElement : function(el){
14831 return this.addItem(new Roo.menu.BaseItem(el));
14835 * Adds an existing object based on {@link Roo.menu.Item} to the menu
14836 * @param {Roo.menu.Item} item The menu item to add
14837 * @return {Roo.menu.Item} The menu item that was added
14839 addItem : function(item){
14840 this.items.add(item);
14842 var li = document.createElement("li");
14843 li.className = "x-menu-list-item";
14844 this.ul.dom.appendChild(li);
14845 item.render(li, this);
14846 this.delayAutoWidth();
14852 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14853 * @param {Object} config A MenuItem config object
14854 * @return {Roo.menu.Item} The menu item that was added
14856 addMenuItem : function(config){
14857 if(!(config instanceof Roo.menu.Item)){
14858 if(typeof config.checked == "boolean"){ // must be check menu item config?
14859 config = new Roo.menu.CheckItem(config);
14861 config = new Roo.menu.Item(config);
14864 return this.addItem(config);
14868 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14869 * @param {String} text The text to display in the menu item
14870 * @return {Roo.menu.Item} The menu item that was added
14872 addText : function(text){
14873 return this.addItem(new Roo.menu.TextItem({ text : text }));
14877 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14878 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14879 * @param {Roo.menu.Item} item The menu item to add
14880 * @return {Roo.menu.Item} The menu item that was added
14882 insert : function(index, item){
14883 this.items.insert(index, item);
14885 var li = document.createElement("li");
14886 li.className = "x-menu-list-item";
14887 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14888 item.render(li, this);
14889 this.delayAutoWidth();
14895 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14896 * @param {Roo.menu.Item} item The menu item to remove
14898 remove : function(item){
14899 this.items.removeKey(item.id);
14904 * Removes and destroys all items in the menu
14906 removeAll : function(){
14908 while(f = this.items.first()){
14914 // MenuNav is a private utility class used internally by the Menu
14915 Roo.menu.MenuNav = function(menu){
14916 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14917 this.scope = this.menu = menu;
14920 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14921 doRelay : function(e, h){
14922 var k = e.getKey();
14923 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14924 this.menu.tryActivate(0, 1);
14927 return h.call(this.scope || this, e, this.menu);
14930 up : function(e, m){
14931 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14932 m.tryActivate(m.items.length-1, -1);
14936 down : function(e, m){
14937 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14938 m.tryActivate(0, 1);
14942 right : function(e, m){
14944 m.activeItem.expandMenu(true);
14948 left : function(e, m){
14950 if(m.parentMenu && m.parentMenu.activeItem){
14951 m.parentMenu.activeItem.activate();
14955 enter : function(e, m){
14957 e.stopPropagation();
14958 m.activeItem.onClick(e);
14959 m.fireEvent("click", this, m.activeItem);
14965 * Ext JS Library 1.1.1
14966 * Copyright(c) 2006-2007, Ext JS, LLC.
14968 * Originally Released Under LGPL - original licence link has changed is not relivant.
14971 * <script type="text/javascript">
14975 * @class Roo.menu.MenuMgr
14976 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
14979 Roo.menu.MenuMgr = function(){
14980 var menus, active, groups = {}, attached = false, lastShow = new Date();
14982 // private - called when first menu is created
14985 active = new Roo.util.MixedCollection();
14986 Roo.get(document).addKeyListener(27, function(){
14987 if(active.length > 0){
14994 function hideAll(){
14995 if(active && active.length > 0){
14996 var c = active.clone();
14997 c.each(function(m){
15004 function onHide(m){
15006 if(active.length < 1){
15007 Roo.get(document).un("mousedown", onMouseDown);
15013 function onShow(m){
15014 var last = active.last();
15015 lastShow = new Date();
15018 Roo.get(document).on("mousedown", onMouseDown);
15022 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15023 m.parentMenu.activeChild = m;
15024 }else if(last && last.isVisible()){
15025 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15030 function onBeforeHide(m){
15032 m.activeChild.hide();
15034 if(m.autoHideTimer){
15035 clearTimeout(m.autoHideTimer);
15036 delete m.autoHideTimer;
15041 function onBeforeShow(m){
15042 var pm = m.parentMenu;
15043 if(!pm && !m.allowOtherMenus){
15045 }else if(pm && pm.activeChild && active != m){
15046 pm.activeChild.hide();
15051 function onMouseDown(e){
15052 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15058 function onBeforeCheck(mi, state){
15060 var g = groups[mi.group];
15061 for(var i = 0, l = g.length; i < l; i++){
15063 g[i].setChecked(false);
15072 * Hides all menus that are currently visible
15074 hideAll : function(){
15079 register : function(menu){
15083 menus[menu.id] = menu;
15084 menu.on("beforehide", onBeforeHide);
15085 menu.on("hide", onHide);
15086 menu.on("beforeshow", onBeforeShow);
15087 menu.on("show", onShow);
15088 var g = menu.group;
15089 if(g && menu.events["checkchange"]){
15093 groups[g].push(menu);
15094 menu.on("checkchange", onCheck);
15099 * Returns a {@link Roo.menu.Menu} object
15100 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15101 * be used to generate and return a new Menu instance.
15103 get : function(menu){
15104 if(typeof menu == "string"){ // menu id
15105 return menus[menu];
15106 }else if(menu.events){ // menu instance
15108 }else if(typeof menu.length == 'number'){ // array of menu items?
15109 return new Roo.menu.Menu({items:menu});
15110 }else{ // otherwise, must be a config
15111 return new Roo.menu.Menu(menu);
15116 unregister : function(menu){
15117 delete menus[menu.id];
15118 menu.un("beforehide", onBeforeHide);
15119 menu.un("hide", onHide);
15120 menu.un("beforeshow", onBeforeShow);
15121 menu.un("show", onShow);
15122 var g = menu.group;
15123 if(g && menu.events["checkchange"]){
15124 groups[g].remove(menu);
15125 menu.un("checkchange", onCheck);
15130 registerCheckable : function(menuItem){
15131 var g = menuItem.group;
15136 groups[g].push(menuItem);
15137 menuItem.on("beforecheckchange", onBeforeCheck);
15142 unregisterCheckable : function(menuItem){
15143 var g = menuItem.group;
15145 groups[g].remove(menuItem);
15146 menuItem.un("beforecheckchange", onBeforeCheck);
15152 * Ext JS Library 1.1.1
15153 * Copyright(c) 2006-2007, Ext JS, LLC.
15155 * Originally Released Under LGPL - original licence link has changed is not relivant.
15158 * <script type="text/javascript">
15163 * @class Roo.menu.BaseItem
15164 * @extends Roo.Component
15166 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
15167 * management and base configuration options shared by all menu components.
15169 * Creates a new BaseItem
15170 * @param {Object} config Configuration options
15172 Roo.menu.BaseItem = function(config){
15173 Roo.menu.BaseItem.superclass.constructor.call(this, config);
15178 * Fires when this item is clicked
15179 * @param {Roo.menu.BaseItem} this
15180 * @param {Roo.EventObject} e
15185 * Fires when this item is activated
15186 * @param {Roo.menu.BaseItem} this
15190 * @event deactivate
15191 * Fires when this item is deactivated
15192 * @param {Roo.menu.BaseItem} this
15198 this.on("click", this.handler, this.scope, true);
15202 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15204 * @cfg {Function} handler
15205 * A function that will handle the click event of this menu item (defaults to undefined)
15208 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15210 canActivate : false,
15213 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15218 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15220 activeClass : "x-menu-item-active",
15222 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15224 hideOnClick : true,
15226 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15231 ctype: "Roo.menu.BaseItem",
15234 actionMode : "container",
15237 render : function(container, parentMenu){
15238 this.parentMenu = parentMenu;
15239 Roo.menu.BaseItem.superclass.render.call(this, container);
15240 this.container.menuItemId = this.id;
15244 onRender : function(container, position){
15245 this.el = Roo.get(this.el);
15246 container.dom.appendChild(this.el.dom);
15250 onClick : function(e){
15251 if(!this.disabled && this.fireEvent("click", this, e) !== false
15252 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15253 this.handleClick(e);
15260 activate : function(){
15264 var li = this.container;
15265 li.addClass(this.activeClass);
15266 this.region = li.getRegion().adjust(2, 2, -2, -2);
15267 this.fireEvent("activate", this);
15272 deactivate : function(){
15273 this.container.removeClass(this.activeClass);
15274 this.fireEvent("deactivate", this);
15278 shouldDeactivate : function(e){
15279 return !this.region || !this.region.contains(e.getPoint());
15283 handleClick : function(e){
15284 if(this.hideOnClick){
15285 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15290 expandMenu : function(autoActivate){
15295 hideMenu : function(){
15300 * Ext JS Library 1.1.1
15301 * Copyright(c) 2006-2007, Ext JS, LLC.
15303 * Originally Released Under LGPL - original licence link has changed is not relivant.
15306 * <script type="text/javascript">
15310 * @class Roo.menu.Adapter
15311 * @extends Roo.menu.BaseItem
15313 * 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.
15314 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15316 * Creates a new Adapter
15317 * @param {Object} config Configuration options
15319 Roo.menu.Adapter = function(component, config){
15320 Roo.menu.Adapter.superclass.constructor.call(this, config);
15321 this.component = component;
15323 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15325 canActivate : true,
15328 onRender : function(container, position){
15329 this.component.render(container);
15330 this.el = this.component.getEl();
15334 activate : function(){
15338 this.component.focus();
15339 this.fireEvent("activate", this);
15344 deactivate : function(){
15345 this.fireEvent("deactivate", this);
15349 disable : function(){
15350 this.component.disable();
15351 Roo.menu.Adapter.superclass.disable.call(this);
15355 enable : function(){
15356 this.component.enable();
15357 Roo.menu.Adapter.superclass.enable.call(this);
15361 * Ext JS Library 1.1.1
15362 * Copyright(c) 2006-2007, Ext JS, LLC.
15364 * Originally Released Under LGPL - original licence link has changed is not relivant.
15367 * <script type="text/javascript">
15371 * @class Roo.menu.TextItem
15372 * @extends Roo.menu.BaseItem
15373 * Adds a static text string to a menu, usually used as either a heading or group separator.
15374 * Note: old style constructor with text is still supported.
15377 * Creates a new TextItem
15378 * @param {Object} cfg Configuration
15380 Roo.menu.TextItem = function(cfg){
15381 if (typeof(cfg) == 'string') {
15384 Roo.apply(this,cfg);
15387 Roo.menu.TextItem.superclass.constructor.call(this);
15390 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15392 * @cfg {String} text Text to show on item.
15397 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15399 hideOnClick : false,
15401 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15403 itemCls : "x-menu-text",
15406 onRender : function(){
15407 var s = document.createElement("span");
15408 s.className = this.itemCls;
15409 s.innerHTML = this.text;
15411 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15415 * Ext JS Library 1.1.1
15416 * Copyright(c) 2006-2007, Ext JS, LLC.
15418 * Originally Released Under LGPL - original licence link has changed is not relivant.
15421 * <script type="text/javascript">
15425 * @class Roo.menu.Separator
15426 * @extends Roo.menu.BaseItem
15427 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15428 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15430 * @param {Object} config Configuration options
15432 Roo.menu.Separator = function(config){
15433 Roo.menu.Separator.superclass.constructor.call(this, config);
15436 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15438 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15440 itemCls : "x-menu-sep",
15442 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15444 hideOnClick : false,
15447 onRender : function(li){
15448 var s = document.createElement("span");
15449 s.className = this.itemCls;
15450 s.innerHTML = " ";
15452 li.addClass("x-menu-sep-li");
15453 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15457 * Ext JS Library 1.1.1
15458 * Copyright(c) 2006-2007, Ext JS, LLC.
15460 * Originally Released Under LGPL - original licence link has changed is not relivant.
15463 * <script type="text/javascript">
15466 * @class Roo.menu.Item
15467 * @extends Roo.menu.BaseItem
15468 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15469 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15470 * activation and click handling.
15472 * Creates a new Item
15473 * @param {Object} config Configuration options
15475 Roo.menu.Item = function(config){
15476 Roo.menu.Item.superclass.constructor.call(this, config);
15478 this.menu = Roo.menu.MenuMgr.get(this.menu);
15481 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15483 * @cfg {Roo.menu.Menu} menu
15487 * @cfg {String} text
15488 * The text to show on the menu item.
15492 * @cfg {String} HTML to render in menu
15493 * The text to show on the menu item (HTML version).
15497 * @cfg {String} icon
15498 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15502 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15504 itemCls : "x-menu-item",
15506 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15508 canActivate : true,
15510 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15513 // doc'd in BaseItem
15517 ctype: "Roo.menu.Item",
15520 onRender : function(container, position){
15521 var el = document.createElement("a");
15522 el.hideFocus = true;
15523 el.unselectable = "on";
15524 el.href = this.href || "#";
15525 if(this.hrefTarget){
15526 el.target = this.hrefTarget;
15528 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
15530 var html = this.html.length ? this.html : String.format('{0}',this.text);
15532 el.innerHTML = String.format(
15533 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15534 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15536 Roo.menu.Item.superclass.onRender.call(this, container, position);
15540 * Sets the text to display in this menu item
15541 * @param {String} text The text to display
15542 * @param {Boolean} isHTML true to indicate text is pure html.
15544 setText : function(text, isHTML){
15552 var html = this.html.length ? this.html : String.format('{0}',this.text);
15554 this.el.update(String.format(
15555 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15556 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15557 this.parentMenu.autoWidth();
15562 handleClick : function(e){
15563 if(!this.href){ // if no link defined, stop the event automatically
15566 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15570 activate : function(autoExpand){
15571 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15581 shouldDeactivate : function(e){
15582 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15583 if(this.menu && this.menu.isVisible()){
15584 return !this.menu.getEl().getRegion().contains(e.getPoint());
15592 deactivate : function(){
15593 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15598 expandMenu : function(autoActivate){
15599 if(!this.disabled && this.menu){
15600 clearTimeout(this.hideTimer);
15601 delete this.hideTimer;
15602 if(!this.menu.isVisible() && !this.showTimer){
15603 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15604 }else if (this.menu.isVisible() && autoActivate){
15605 this.menu.tryActivate(0, 1);
15611 deferExpand : function(autoActivate){
15612 delete this.showTimer;
15613 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15615 this.menu.tryActivate(0, 1);
15620 hideMenu : function(){
15621 clearTimeout(this.showTimer);
15622 delete this.showTimer;
15623 if(!this.hideTimer && this.menu && this.menu.isVisible()){
15624 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15629 deferHide : function(){
15630 delete this.hideTimer;
15635 * Ext JS Library 1.1.1
15636 * Copyright(c) 2006-2007, Ext JS, LLC.
15638 * Originally Released Under LGPL - original licence link has changed is not relivant.
15641 * <script type="text/javascript">
15645 * @class Roo.menu.CheckItem
15646 * @extends Roo.menu.Item
15647 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15649 * Creates a new CheckItem
15650 * @param {Object} config Configuration options
15652 Roo.menu.CheckItem = function(config){
15653 Roo.menu.CheckItem.superclass.constructor.call(this, config);
15656 * @event beforecheckchange
15657 * Fires before the checked value is set, providing an opportunity to cancel if needed
15658 * @param {Roo.menu.CheckItem} this
15659 * @param {Boolean} checked The new checked value that will be set
15661 "beforecheckchange" : true,
15663 * @event checkchange
15664 * Fires after the checked value has been set
15665 * @param {Roo.menu.CheckItem} this
15666 * @param {Boolean} checked The checked value that was set
15668 "checkchange" : true
15670 if(this.checkHandler){
15671 this.on('checkchange', this.checkHandler, this.scope);
15674 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15676 * @cfg {String} group
15677 * All check items with the same group name will automatically be grouped into a single-select
15678 * radio button group (defaults to '')
15681 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15683 itemCls : "x-menu-item x-menu-check-item",
15685 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15687 groupClass : "x-menu-group-item",
15690 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
15691 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15692 * initialized with checked = true will be rendered as checked.
15697 ctype: "Roo.menu.CheckItem",
15700 onRender : function(c){
15701 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15703 this.el.addClass(this.groupClass);
15705 Roo.menu.MenuMgr.registerCheckable(this);
15707 this.checked = false;
15708 this.setChecked(true, true);
15713 destroy : function(){
15715 Roo.menu.MenuMgr.unregisterCheckable(this);
15717 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15721 * Set the checked state of this item
15722 * @param {Boolean} checked The new checked value
15723 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15725 setChecked : function(state, suppressEvent){
15726 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15727 if(this.container){
15728 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15730 this.checked = state;
15731 if(suppressEvent !== true){
15732 this.fireEvent("checkchange", this, state);
15738 handleClick : function(e){
15739 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15740 this.setChecked(!this.checked);
15742 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15746 * Ext JS Library 1.1.1
15747 * Copyright(c) 2006-2007, Ext JS, LLC.
15749 * Originally Released Under LGPL - original licence link has changed is not relivant.
15752 * <script type="text/javascript">
15756 * @class Roo.menu.DateItem
15757 * @extends Roo.menu.Adapter
15758 * A menu item that wraps the {@link Roo.DatPicker} component.
15760 * Creates a new DateItem
15761 * @param {Object} config Configuration options
15763 Roo.menu.DateItem = function(config){
15764 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15765 /** The Roo.DatePicker object @type Roo.DatePicker */
15766 this.picker = this.component;
15767 this.addEvents({select: true});
15769 this.picker.on("render", function(picker){
15770 picker.getEl().swallowEvent("click");
15771 picker.container.addClass("x-menu-date-item");
15774 this.picker.on("select", this.onSelect, this);
15777 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15779 onSelect : function(picker, date){
15780 this.fireEvent("select", this, date, picker);
15781 Roo.menu.DateItem.superclass.handleClick.call(this);
15785 * Ext JS Library 1.1.1
15786 * Copyright(c) 2006-2007, Ext JS, LLC.
15788 * Originally Released Under LGPL - original licence link has changed is not relivant.
15791 * <script type="text/javascript">
15795 * @class Roo.menu.ColorItem
15796 * @extends Roo.menu.Adapter
15797 * A menu item that wraps the {@link Roo.ColorPalette} component.
15799 * Creates a new ColorItem
15800 * @param {Object} config Configuration options
15802 Roo.menu.ColorItem = function(config){
15803 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15804 /** The Roo.ColorPalette object @type Roo.ColorPalette */
15805 this.palette = this.component;
15806 this.relayEvents(this.palette, ["select"]);
15807 if(this.selectHandler){
15808 this.on('select', this.selectHandler, this.scope);
15811 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15813 * Ext JS Library 1.1.1
15814 * Copyright(c) 2006-2007, Ext JS, LLC.
15816 * Originally Released Under LGPL - original licence link has changed is not relivant.
15819 * <script type="text/javascript">
15824 * @class Roo.menu.DateMenu
15825 * @extends Roo.menu.Menu
15826 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15828 * Creates a new DateMenu
15829 * @param {Object} config Configuration options
15831 Roo.menu.DateMenu = function(config){
15832 Roo.menu.DateMenu.superclass.constructor.call(this, config);
15834 var di = new Roo.menu.DateItem(config);
15837 * The {@link Roo.DatePicker} instance for this DateMenu
15840 this.picker = di.picker;
15843 * @param {DatePicker} picker
15844 * @param {Date} date
15846 this.relayEvents(di, ["select"]);
15847 this.on('beforeshow', function(){
15849 this.picker.hideMonthPicker(false);
15853 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15857 * Ext JS Library 1.1.1
15858 * Copyright(c) 2006-2007, Ext JS, LLC.
15860 * Originally Released Under LGPL - original licence link has changed is not relivant.
15863 * <script type="text/javascript">
15868 * @class Roo.menu.ColorMenu
15869 * @extends Roo.menu.Menu
15870 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15872 * Creates a new ColorMenu
15873 * @param {Object} config Configuration options
15875 Roo.menu.ColorMenu = function(config){
15876 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15878 var ci = new Roo.menu.ColorItem(config);
15881 * The {@link Roo.ColorPalette} instance for this ColorMenu
15882 * @type ColorPalette
15884 this.palette = ci.palette;
15887 * @param {ColorPalette} palette
15888 * @param {String} color
15890 this.relayEvents(ci, ["select"]);
15892 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15894 * Ext JS Library 1.1.1
15895 * Copyright(c) 2006-2007, Ext JS, LLC.
15897 * Originally Released Under LGPL - original licence link has changed is not relivant.
15900 * <script type="text/javascript">
15904 * @class Roo.form.TextItem
15905 * @extends Roo.BoxComponent
15906 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15908 * Creates a new TextItem
15909 * @param {Object} config Configuration options
15911 Roo.form.TextItem = function(config){
15912 Roo.form.TextItem.superclass.constructor.call(this, config);
15915 Roo.extend(Roo.form.TextItem, Roo.BoxComponent, {
15918 * @cfg {String} tag the tag for this item (default div)
15922 * @cfg {String} html the content for this item
15926 getAutoCreate : function()
15939 onRender : function(ct, position)
15941 Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15944 var cfg = this.getAutoCreate();
15946 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15948 if (!cfg.name.length) {
15951 this.el = ct.createChild(cfg, position);
15956 * @param {String} html update the Contents of the element.
15958 setHTML : function(html)
15960 this.fieldEl.dom.innerHTML = html;
15965 * Ext JS Library 1.1.1
15966 * Copyright(c) 2006-2007, Ext JS, LLC.
15968 * Originally Released Under LGPL - original licence link has changed is not relivant.
15971 * <script type="text/javascript">
15975 * @class Roo.form.Field
15976 * @extends Roo.BoxComponent
15977 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15979 * Creates a new Field
15980 * @param {Object} config Configuration options
15982 Roo.form.Field = function(config){
15983 Roo.form.Field.superclass.constructor.call(this, config);
15986 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
15988 * @cfg {String} fieldLabel Label to use when rendering a form.
15991 * @cfg {String} qtip Mouse over tip
15995 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
15997 invalidClass : "x-form-invalid",
15999 * @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")
16001 invalidText : "The value in this field is invalid",
16003 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16005 focusClass : "x-form-focus",
16007 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16008 automatic validation (defaults to "keyup").
16010 validationEvent : "keyup",
16012 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16014 validateOnBlur : true,
16016 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16018 validationDelay : 250,
16020 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16021 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16023 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16025 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16027 fieldClass : "x-form-field",
16029 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
16032 ----------- ----------------------------------------------------------------------
16033 qtip Display a quick tip when the user hovers over the field
16034 title Display a default browser title attribute popup
16035 under Add a block div beneath the field containing the error text
16036 side Add an error icon to the right of the field with a popup on hover
16037 [element id] Add the error text directly to the innerHTML of the specified element
16040 msgTarget : 'qtip',
16042 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16047 * @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.
16052 * @cfg {Boolean} disabled True to disable the field (defaults to false).
16057 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16059 inputType : undefined,
16062 * @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).
16064 tabIndex : undefined,
16067 isFormField : true,
16072 * @property {Roo.Element} fieldEl
16073 * Element Containing the rendered Field (with label etc.)
16076 * @cfg {Mixed} value A value to initialize this field with.
16081 * @cfg {String} name The field's HTML name attribute.
16084 * @cfg {String} cls A CSS class to apply to the field's underlying element.
16087 loadedValue : false,
16091 initComponent : function(){
16092 Roo.form.Field.superclass.initComponent.call(this);
16096 * Fires when this field receives input focus.
16097 * @param {Roo.form.Field} this
16102 * Fires when this field loses input focus.
16103 * @param {Roo.form.Field} this
16107 * @event specialkey
16108 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
16109 * {@link Roo.EventObject#getKey} to determine which key was pressed.
16110 * @param {Roo.form.Field} this
16111 * @param {Roo.EventObject} e The event object
16116 * Fires just before the field blurs if the field value has changed.
16117 * @param {Roo.form.Field} this
16118 * @param {Mixed} newValue The new value
16119 * @param {Mixed} oldValue The original value
16124 * Fires after the field has been marked as invalid.
16125 * @param {Roo.form.Field} this
16126 * @param {String} msg The validation message
16131 * Fires after the field has been validated with no errors.
16132 * @param {Roo.form.Field} this
16137 * Fires after the key up
16138 * @param {Roo.form.Field} this
16139 * @param {Roo.EventObject} e The event Object
16146 * Returns the name attribute of the field if available
16147 * @return {String} name The field name
16149 getName: function(){
16150 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16154 onRender : function(ct, position){
16155 Roo.form.Field.superclass.onRender.call(this, ct, position);
16157 var cfg = this.getAutoCreate();
16159 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16161 if (!cfg.name.length) {
16164 if(this.inputType){
16165 cfg.type = this.inputType;
16167 this.el = ct.createChild(cfg, position);
16169 var type = this.el.dom.type;
16171 if(type == 'password'){
16174 this.el.addClass('x-form-'+type);
16177 this.el.dom.readOnly = true;
16179 if(this.tabIndex !== undefined){
16180 this.el.dom.setAttribute('tabIndex', this.tabIndex);
16183 this.el.addClass([this.fieldClass, this.cls]);
16188 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16189 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16190 * @return {Roo.form.Field} this
16192 applyTo : function(target){
16193 this.allowDomMove = false;
16194 this.el = Roo.get(target);
16195 this.render(this.el.dom.parentNode);
16200 initValue : function(){
16201 if(this.value !== undefined){
16202 this.setValue(this.value);
16203 }else if(this.el.dom.value.length > 0){
16204 this.setValue(this.el.dom.value);
16209 * Returns true if this field has been changed since it was originally loaded and is not disabled.
16210 * DEPRICATED - it never worked well - use hasChanged/resetHasChanged.
16212 isDirty : function() {
16213 if(this.disabled) {
16216 return String(this.getValue()) !== String(this.originalValue);
16220 * stores the current value in loadedValue
16222 resetHasChanged : function()
16224 this.loadedValue = String(this.getValue());
16227 * checks the current value against the 'loaded' value.
16228 * Note - will return false if 'resetHasChanged' has not been called first.
16230 hasChanged : function()
16232 if(this.disabled || this.readOnly) {
16235 return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16241 afterRender : function(){
16242 Roo.form.Field.superclass.afterRender.call(this);
16247 fireKey : function(e){
16248 //Roo.log('field ' + e.getKey());
16249 if(e.isNavKeyPress()){
16250 this.fireEvent("specialkey", this, e);
16255 * Resets the current field value to the originally loaded value and clears any validation messages
16257 reset : function(){
16258 this.setValue(this.resetValue);
16259 this.originalValue = this.getValue();
16260 this.clearInvalid();
16264 initEvents : function(){
16265 // safari killled keypress - so keydown is now used..
16266 this.el.on("keydown" , this.fireKey, this);
16267 this.el.on("focus", this.onFocus, this);
16268 this.el.on("blur", this.onBlur, this);
16269 this.el.relayEvent('keyup', this);
16271 // reference to original value for reset
16272 this.originalValue = this.getValue();
16273 this.resetValue = this.getValue();
16277 onFocus : function(){
16278 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16279 this.el.addClass(this.focusClass);
16281 if(!this.hasFocus){
16282 this.hasFocus = true;
16283 this.startValue = this.getValue();
16284 this.fireEvent("focus", this);
16288 beforeBlur : Roo.emptyFn,
16291 onBlur : function(){
16293 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16294 this.el.removeClass(this.focusClass);
16296 this.hasFocus = false;
16297 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16300 var v = this.getValue();
16301 if(String(v) !== String(this.startValue)){
16302 this.fireEvent('change', this, v, this.startValue);
16304 this.fireEvent("blur", this);
16308 * Returns whether or not the field value is currently valid
16309 * @param {Boolean} preventMark True to disable marking the field invalid
16310 * @return {Boolean} True if the value is valid, else false
16312 isValid : function(preventMark){
16316 var restore = this.preventMark;
16317 this.preventMark = preventMark === true;
16318 var v = this.validateValue(this.processValue(this.getRawValue()));
16319 this.preventMark = restore;
16324 * Validates the field value
16325 * @return {Boolean} True if the value is valid, else false
16327 validate : function(){
16328 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16329 this.clearInvalid();
16335 processValue : function(value){
16340 // Subclasses should provide the validation implementation by overriding this
16341 validateValue : function(value){
16346 * Mark this field as invalid
16347 * @param {String} msg The validation message
16349 markInvalid : function(msg){
16350 if(!this.rendered || this.preventMark){ // not rendered
16354 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16356 obj.el.addClass(this.invalidClass);
16357 msg = msg || this.invalidText;
16358 switch(this.msgTarget){
16360 obj.el.dom.qtip = msg;
16361 obj.el.dom.qclass = 'x-form-invalid-tip';
16362 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16363 Roo.QuickTips.enable();
16367 this.el.dom.title = msg;
16371 var elp = this.el.findParent('.x-form-element', 5, true);
16372 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16373 this.errorEl.setWidth(elp.getWidth(true)-20);
16375 this.errorEl.update(msg);
16376 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16379 if(!this.errorIcon){
16380 var elp = this.el.findParent('.x-form-element', 5, true);
16381 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16383 this.alignErrorIcon();
16384 this.errorIcon.dom.qtip = msg;
16385 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16386 this.errorIcon.show();
16387 this.on('resize', this.alignErrorIcon, this);
16390 var t = Roo.getDom(this.msgTarget);
16392 t.style.display = this.msgDisplay;
16395 this.fireEvent('invalid', this, msg);
16399 alignErrorIcon : function(){
16400 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16404 * Clear any invalid styles/messages for this field
16406 clearInvalid : function(){
16407 if(!this.rendered || this.preventMark){ // not rendered
16410 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16412 obj.el.removeClass(this.invalidClass);
16413 switch(this.msgTarget){
16415 obj.el.dom.qtip = '';
16418 this.el.dom.title = '';
16422 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16426 if(this.errorIcon){
16427 this.errorIcon.dom.qtip = '';
16428 this.errorIcon.hide();
16429 this.un('resize', this.alignErrorIcon, this);
16433 var t = Roo.getDom(this.msgTarget);
16435 t.style.display = 'none';
16438 this.fireEvent('valid', this);
16442 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
16443 * @return {Mixed} value The field value
16445 getRawValue : function(){
16446 var v = this.el.getValue();
16452 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16453 * @return {Mixed} value The field value
16455 getValue : function(){
16456 var v = this.el.getValue();
16462 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
16463 * @param {Mixed} value The value to set
16465 setRawValue : function(v){
16466 return this.el.dom.value = (v === null || v === undefined ? '' : v);
16470 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
16471 * @param {Mixed} value The value to set
16473 setValue : function(v){
16476 this.el.dom.value = (v === null || v === undefined ? '' : v);
16481 adjustSize : function(w, h){
16482 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16483 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16487 adjustWidth : function(tag, w){
16488 tag = tag.toLowerCase();
16489 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16490 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16491 if(tag == 'input'){
16494 if(tag == 'textarea'){
16497 }else if(Roo.isOpera){
16498 if(tag == 'input'){
16501 if(tag == 'textarea'){
16511 // anything other than normal should be considered experimental
16512 Roo.form.Field.msgFx = {
16514 show: function(msgEl, f){
16515 msgEl.setDisplayed('block');
16518 hide : function(msgEl, f){
16519 msgEl.setDisplayed(false).update('');
16524 show: function(msgEl, f){
16525 msgEl.slideIn('t', {stopFx:true});
16528 hide : function(msgEl, f){
16529 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16534 show: function(msgEl, f){
16535 msgEl.fixDisplay();
16536 msgEl.alignTo(f.el, 'tl-tr');
16537 msgEl.slideIn('l', {stopFx:true});
16540 hide : function(msgEl, f){
16541 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16546 * Ext JS Library 1.1.1
16547 * Copyright(c) 2006-2007, Ext JS, LLC.
16549 * Originally Released Under LGPL - original licence link has changed is not relivant.
16552 * <script type="text/javascript">
16557 * @class Roo.form.TextField
16558 * @extends Roo.form.Field
16559 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
16560 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16562 * Creates a new TextField
16563 * @param {Object} config Configuration options
16565 Roo.form.TextField = function(config){
16566 Roo.form.TextField.superclass.constructor.call(this, config);
16570 * Fires when the autosize function is triggered. The field may or may not have actually changed size
16571 * according to the default logic, but this event provides a hook for the developer to apply additional
16572 * logic at runtime to resize the field if needed.
16573 * @param {Roo.form.Field} this This text field
16574 * @param {Number} width The new field width
16580 Roo.extend(Roo.form.TextField, Roo.form.Field, {
16582 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16586 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16590 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16594 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16598 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16602 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16604 disableKeyFilter : false,
16606 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16610 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16614 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16616 maxLength : Number.MAX_VALUE,
16618 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16620 minLengthText : "The minimum length for this field is {0}",
16622 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16624 maxLengthText : "The maximum length for this field is {0}",
16626 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16628 selectOnFocus : false,
16630 * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space
16632 allowLeadingSpace : false,
16634 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16636 blankText : "This field is required",
16638 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16639 * If available, this function will be called only after the basic validators all return true, and will be passed the
16640 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16644 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16645 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16646 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
16650 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16654 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16660 initEvents : function()
16662 if (this.emptyText) {
16663 this.el.attr('placeholder', this.emptyText);
16666 Roo.form.TextField.superclass.initEvents.call(this);
16667 if(this.validationEvent == 'keyup'){
16668 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16669 this.el.on('keyup', this.filterValidation, this);
16671 else if(this.validationEvent !== false){
16672 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16675 if(this.selectOnFocus){
16676 this.on("focus", this.preFocus, this);
16678 if (!this.allowLeadingSpace) {
16679 this.on('blur', this.cleanLeadingSpace, this);
16682 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16683 this.el.on("keypress", this.filterKeys, this);
16686 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
16687 this.el.on("click", this.autoSize, this);
16689 if(this.el.is('input[type=password]') && Roo.isSafari){
16690 this.el.on('keydown', this.SafariOnKeyDown, this);
16694 processValue : function(value){
16695 if(this.stripCharsRe){
16696 var newValue = value.replace(this.stripCharsRe, '');
16697 if(newValue !== value){
16698 this.setRawValue(newValue);
16705 filterValidation : function(e){
16706 if(!e.isNavKeyPress()){
16707 this.validationTask.delay(this.validationDelay);
16712 onKeyUp : function(e){
16713 if(!e.isNavKeyPress()){
16717 // private - clean the leading white space
16718 cleanLeadingSpace : function(e)
16720 if ( this.inputType == 'file') {
16724 this.setValue((this.getValue() + '').replace(/^\s+/,''));
16727 * Resets the current field value to the originally-loaded value and clears any validation messages.
16730 reset : function(){
16731 Roo.form.TextField.superclass.reset.call(this);
16735 preFocus : function(){
16737 if(this.selectOnFocus){
16738 this.el.dom.select();
16744 filterKeys : function(e){
16745 var k = e.getKey();
16746 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16749 var c = e.getCharCode(), cc = String.fromCharCode(c);
16750 if(Roo.isIE && (e.isSpecialKey() || !cc)){
16753 if(!this.maskRe.test(cc)){
16758 setValue : function(v){
16760 Roo.form.TextField.superclass.setValue.apply(this, arguments);
16766 * Validates a value according to the field's validation rules and marks the field as invalid
16767 * if the validation fails
16768 * @param {Mixed} value The value to validate
16769 * @return {Boolean} True if the value is valid, else false
16771 validateValue : function(value){
16772 if(value.length < 1) { // if it's blank
16773 if(this.allowBlank){
16774 this.clearInvalid();
16777 this.markInvalid(this.blankText);
16781 if(value.length < this.minLength){
16782 this.markInvalid(String.format(this.minLengthText, this.minLength));
16785 if(value.length > this.maxLength){
16786 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16790 var vt = Roo.form.VTypes;
16791 if(!vt[this.vtype](value, this)){
16792 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16796 if(typeof this.validator == "function"){
16797 var msg = this.validator(value);
16799 this.markInvalid(msg);
16803 if(this.regex && !this.regex.test(value)){
16804 this.markInvalid(this.regexText);
16811 * Selects text in this field
16812 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16813 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16815 selectText : function(start, end){
16816 var v = this.getRawValue();
16818 start = start === undefined ? 0 : start;
16819 end = end === undefined ? v.length : end;
16820 var d = this.el.dom;
16821 if(d.setSelectionRange){
16822 d.setSelectionRange(start, end);
16823 }else if(d.createTextRange){
16824 var range = d.createTextRange();
16825 range.moveStart("character", start);
16826 range.moveEnd("character", v.length-end);
16833 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16834 * This only takes effect if grow = true, and fires the autosize event.
16836 autoSize : function(){
16837 if(!this.grow || !this.rendered){
16841 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16844 var v = el.dom.value;
16845 var d = document.createElement('div');
16846 d.appendChild(document.createTextNode(v));
16850 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16851 this.el.setWidth(w);
16852 this.fireEvent("autosize", this, w);
16856 SafariOnKeyDown : function(event)
16858 // this is a workaround for a password hang bug on chrome/ webkit.
16860 var isSelectAll = false;
16862 if(this.el.dom.selectionEnd > 0){
16863 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16865 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16866 event.preventDefault();
16871 if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16873 event.preventDefault();
16874 // this is very hacky as keydown always get's upper case.
16876 var cc = String.fromCharCode(event.getCharCode());
16879 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
16887 * Ext JS Library 1.1.1
16888 * Copyright(c) 2006-2007, Ext JS, LLC.
16890 * Originally Released Under LGPL - original licence link has changed is not relivant.
16893 * <script type="text/javascript">
16897 * @class Roo.form.Hidden
16898 * @extends Roo.form.TextField
16899 * Simple Hidden element used on forms
16901 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16904 * Creates a new Hidden form element.
16905 * @param {Object} config Configuration options
16910 // easy hidden field...
16911 Roo.form.Hidden = function(config){
16912 Roo.form.Hidden.superclass.constructor.call(this, config);
16915 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16917 inputType: 'hidden',
16920 labelSeparator: '',
16922 itemCls : 'x-form-item-display-none'
16930 * Ext JS Library 1.1.1
16931 * Copyright(c) 2006-2007, Ext JS, LLC.
16933 * Originally Released Under LGPL - original licence link has changed is not relivant.
16936 * <script type="text/javascript">
16940 * @class Roo.form.TriggerField
16941 * @extends Roo.form.TextField
16942 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16943 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16944 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16945 * for which you can provide a custom implementation. For example:
16947 var trigger = new Roo.form.TriggerField();
16948 trigger.onTriggerClick = myTriggerFn;
16949 trigger.applyTo('my-field');
16952 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16953 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16954 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
16955 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16957 * Create a new TriggerField.
16958 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16959 * to the base TextField)
16961 Roo.form.TriggerField = function(config){
16962 this.mimicing = false;
16963 Roo.form.TriggerField.superclass.constructor.call(this, config);
16966 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
16968 * @cfg {String} triggerClass A CSS class to apply to the trigger
16971 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16972 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
16974 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
16976 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
16980 /** @cfg {Boolean} grow @hide */
16981 /** @cfg {Number} growMin @hide */
16982 /** @cfg {Number} growMax @hide */
16988 autoSize: Roo.emptyFn,
16992 deferHeight : true,
16995 actionMode : 'wrap',
16997 onResize : function(w, h){
16998 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
16999 if(typeof w == 'number'){
17000 var x = w - this.trigger.getWidth();
17001 this.el.setWidth(this.adjustWidth('input', x));
17002 this.trigger.setStyle('left', x+'px');
17007 adjustSize : Roo.BoxComponent.prototype.adjustSize,
17010 getResizeEl : function(){
17015 getPositionEl : function(){
17020 alignErrorIcon : function(){
17021 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17025 onRender : function(ct, position){
17026 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17027 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17028 this.trigger = this.wrap.createChild(this.triggerConfig ||
17029 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17030 if(this.hideTrigger){
17031 this.trigger.setDisplayed(false);
17033 this.initTrigger();
17035 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17040 initTrigger : function(){
17041 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17042 this.trigger.addClassOnOver('x-form-trigger-over');
17043 this.trigger.addClassOnClick('x-form-trigger-click');
17047 onDestroy : function(){
17049 this.trigger.removeAllListeners();
17050 this.trigger.remove();
17053 this.wrap.remove();
17055 Roo.form.TriggerField.superclass.onDestroy.call(this);
17059 onFocus : function(){
17060 Roo.form.TriggerField.superclass.onFocus.call(this);
17061 if(!this.mimicing){
17062 this.wrap.addClass('x-trigger-wrap-focus');
17063 this.mimicing = true;
17064 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17065 if(this.monitorTab){
17066 this.el.on("keydown", this.checkTab, this);
17072 checkTab : function(e){
17073 if(e.getKey() == e.TAB){
17074 this.triggerBlur();
17079 onBlur : function(){
17084 mimicBlur : function(e, t){
17085 if(!this.wrap.contains(t) && this.validateBlur()){
17086 this.triggerBlur();
17091 triggerBlur : function(){
17092 this.mimicing = false;
17093 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17094 if(this.monitorTab){
17095 this.el.un("keydown", this.checkTab, this);
17097 this.wrap.removeClass('x-trigger-wrap-focus');
17098 Roo.form.TriggerField.superclass.onBlur.call(this);
17102 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17103 validateBlur : function(e, t){
17108 onDisable : function(){
17109 Roo.form.TriggerField.superclass.onDisable.call(this);
17111 this.wrap.addClass('x-item-disabled');
17116 onEnable : function(){
17117 Roo.form.TriggerField.superclass.onEnable.call(this);
17119 this.wrap.removeClass('x-item-disabled');
17124 onShow : function(){
17125 var ae = this.getActionEl();
17128 ae.dom.style.display = '';
17129 ae.dom.style.visibility = 'visible';
17135 onHide : function(){
17136 var ae = this.getActionEl();
17137 ae.dom.style.display = 'none';
17141 * The function that should handle the trigger's click event. This method does nothing by default until overridden
17142 * by an implementing function.
17144 * @param {EventObject} e
17146 onTriggerClick : Roo.emptyFn
17149 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
17150 // to be extended by an implementing class. For an example of implementing this class, see the custom
17151 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17152 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17153 initComponent : function(){
17154 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17156 this.triggerConfig = {
17157 tag:'span', cls:'x-form-twin-triggers', cn:[
17158 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17159 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17163 getTrigger : function(index){
17164 return this.triggers[index];
17167 initTrigger : function(){
17168 var ts = this.trigger.select('.x-form-trigger', true);
17169 this.wrap.setStyle('overflow', 'hidden');
17170 var triggerField = this;
17171 ts.each(function(t, all, index){
17172 t.hide = function(){
17173 var w = triggerField.wrap.getWidth();
17174 this.dom.style.display = 'none';
17175 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17177 t.show = function(){
17178 var w = triggerField.wrap.getWidth();
17179 this.dom.style.display = '';
17180 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17182 var triggerIndex = 'Trigger'+(index+1);
17184 if(this['hide'+triggerIndex]){
17185 t.dom.style.display = 'none';
17187 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17188 t.addClassOnOver('x-form-trigger-over');
17189 t.addClassOnClick('x-form-trigger-click');
17191 this.triggers = ts.elements;
17194 onTrigger1Click : Roo.emptyFn,
17195 onTrigger2Click : Roo.emptyFn
17198 * Ext JS Library 1.1.1
17199 * Copyright(c) 2006-2007, Ext JS, LLC.
17201 * Originally Released Under LGPL - original licence link has changed is not relivant.
17204 * <script type="text/javascript">
17208 * @class Roo.form.TextArea
17209 * @extends Roo.form.TextField
17210 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
17211 * support for auto-sizing.
17213 * Creates a new TextArea
17214 * @param {Object} config Configuration options
17216 Roo.form.TextArea = function(config){
17217 Roo.form.TextArea.superclass.constructor.call(this, config);
17218 // these are provided exchanges for backwards compat
17219 // minHeight/maxHeight were replaced by growMin/growMax to be
17220 // compatible with TextField growing config values
17221 if(this.minHeight !== undefined){
17222 this.growMin = this.minHeight;
17224 if(this.maxHeight !== undefined){
17225 this.growMax = this.maxHeight;
17229 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
17231 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17235 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17239 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17240 * in the field (equivalent to setting overflow: hidden, defaults to false)
17242 preventScrollbars: false,
17244 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17245 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17249 onRender : function(ct, position){
17251 this.defaultAutoCreate = {
17253 style:"width:300px;height:60px;",
17254 autocomplete: "new-password"
17257 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17259 this.textSizeEl = Roo.DomHelper.append(document.body, {
17260 tag: "pre", cls: "x-form-grow-sizer"
17262 if(this.preventScrollbars){
17263 this.el.setStyle("overflow", "hidden");
17265 this.el.setHeight(this.growMin);
17269 onDestroy : function(){
17270 if(this.textSizeEl){
17271 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17273 Roo.form.TextArea.superclass.onDestroy.call(this);
17277 onKeyUp : function(e){
17278 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17284 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17285 * This only takes effect if grow = true, and fires the autosize event if the height changes.
17287 autoSize : function(){
17288 if(!this.grow || !this.textSizeEl){
17292 var v = el.dom.value;
17293 var ts = this.textSizeEl;
17296 ts.appendChild(document.createTextNode(v));
17299 Roo.fly(ts).setWidth(this.el.getWidth());
17301 v = "  ";
17304 v = v.replace(/\n/g, '<p> </p>');
17306 v += " \n ";
17309 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17310 if(h != this.lastHeight){
17311 this.lastHeight = h;
17312 this.el.setHeight(h);
17313 this.fireEvent("autosize", this, h);
17318 * Ext JS Library 1.1.1
17319 * Copyright(c) 2006-2007, Ext JS, LLC.
17321 * Originally Released Under LGPL - original licence link has changed is not relivant.
17324 * <script type="text/javascript">
17329 * @class Roo.form.NumberField
17330 * @extends Roo.form.TextField
17331 * Numeric text field that provides automatic keystroke filtering and numeric validation.
17333 * Creates a new NumberField
17334 * @param {Object} config Configuration options
17336 Roo.form.NumberField = function(config){
17337 Roo.form.NumberField.superclass.constructor.call(this, config);
17340 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
17342 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17344 fieldClass: "x-form-field x-form-num-field",
17346 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17348 allowDecimals : true,
17350 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17352 decimalSeparator : ".",
17354 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17356 decimalPrecision : 2,
17358 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17360 allowNegative : true,
17362 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17364 minValue : Number.NEGATIVE_INFINITY,
17366 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17368 maxValue : Number.MAX_VALUE,
17370 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17372 minText : "The minimum value for this field is {0}",
17374 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17376 maxText : "The maximum value for this field is {0}",
17378 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
17379 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17381 nanText : "{0} is not a valid number",
17384 initEvents : function(){
17385 Roo.form.NumberField.superclass.initEvents.call(this);
17386 var allowed = "0123456789";
17387 if(this.allowDecimals){
17388 allowed += this.decimalSeparator;
17390 if(this.allowNegative){
17393 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17394 var keyPress = function(e){
17395 var k = e.getKey();
17396 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17399 var c = e.getCharCode();
17400 if(allowed.indexOf(String.fromCharCode(c)) === -1){
17404 this.el.on("keypress", keyPress, this);
17408 validateValue : function(value){
17409 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17412 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17415 var num = this.parseValue(value);
17417 this.markInvalid(String.format(this.nanText, value));
17420 if(num < this.minValue){
17421 this.markInvalid(String.format(this.minText, this.minValue));
17424 if(num > this.maxValue){
17425 this.markInvalid(String.format(this.maxText, this.maxValue));
17431 getValue : function(){
17432 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17436 parseValue : function(value){
17437 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17438 return isNaN(value) ? '' : value;
17442 fixPrecision : function(value){
17443 var nan = isNaN(value);
17444 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17445 return nan ? '' : value;
17447 return parseFloat(value).toFixed(this.decimalPrecision);
17450 setValue : function(v){
17451 v = this.fixPrecision(v);
17452 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17456 decimalPrecisionFcn : function(v){
17457 return Math.floor(v);
17460 beforeBlur : function(){
17461 var v = this.parseValue(this.getRawValue());
17468 * Ext JS Library 1.1.1
17469 * Copyright(c) 2006-2007, Ext JS, LLC.
17471 * Originally Released Under LGPL - original licence link has changed is not relivant.
17474 * <script type="text/javascript">
17478 * @class Roo.form.DateField
17479 * @extends Roo.form.TriggerField
17480 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17482 * Create a new DateField
17483 * @param {Object} config
17485 Roo.form.DateField = function(config)
17487 Roo.form.DateField.superclass.constructor.call(this, config);
17493 * Fires when a date is selected
17494 * @param {Roo.form.DateField} combo This combo box
17495 * @param {Date} date The date selected
17502 if(typeof this.minValue == "string") {
17503 this.minValue = this.parseDate(this.minValue);
17505 if(typeof this.maxValue == "string") {
17506 this.maxValue = this.parseDate(this.maxValue);
17508 this.ddMatch = null;
17509 if(this.disabledDates){
17510 var dd = this.disabledDates;
17512 for(var i = 0; i < dd.length; i++){
17514 if(i != dd.length-1) {
17518 this.ddMatch = new RegExp(re + ")");
17522 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
17524 * @cfg {String} format
17525 * The default date format string which can be overriden for localization support. The format must be
17526 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17530 * @cfg {String} altFormats
17531 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17532 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17534 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17536 * @cfg {Array} disabledDays
17537 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17539 disabledDays : null,
17541 * @cfg {String} disabledDaysText
17542 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17544 disabledDaysText : "Disabled",
17546 * @cfg {Array} disabledDates
17547 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17548 * expression so they are very powerful. Some examples:
17550 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17551 * <li>["03/08", "09/16"] would disable those days for every year</li>
17552 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17553 * <li>["03/../2006"] would disable every day in March 2006</li>
17554 * <li>["^03"] would disable every day in every March</li>
17556 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17557 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17559 disabledDates : null,
17561 * @cfg {String} disabledDatesText
17562 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17564 disabledDatesText : "Disabled",
17566 * @cfg {Date/String} minValue
17567 * The minimum allowed date. Can be either a Javascript date object or a string date in a
17568 * valid format (defaults to null).
17572 * @cfg {Date/String} maxValue
17573 * The maximum allowed date. Can be either a Javascript date object or a string date in a
17574 * valid format (defaults to null).
17578 * @cfg {String} minText
17579 * The error text to display when the date in the cell is before minValue (defaults to
17580 * 'The date in this field must be after {minValue}').
17582 minText : "The date in this field must be equal to or after {0}",
17584 * @cfg {String} maxText
17585 * The error text to display when the date in the cell is after maxValue (defaults to
17586 * 'The date in this field must be before {maxValue}').
17588 maxText : "The date in this field must be equal to or before {0}",
17590 * @cfg {String} invalidText
17591 * The error text to display when the date in the field is invalid (defaults to
17592 * '{value} is not a valid date - it must be in the format {format}').
17594 invalidText : "{0} is not a valid date - it must be in the format {1}",
17596 * @cfg {String} triggerClass
17597 * An additional CSS class used to style the trigger button. The trigger will always get the
17598 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17599 * which displays a calendar icon).
17601 triggerClass : 'x-form-date-trigger',
17605 * @cfg {Boolean} useIso
17606 * if enabled, then the date field will use a hidden field to store the
17607 * real value as iso formated date. default (false)
17611 * @cfg {String/Object} autoCreate
17612 * A DomHelper element spec, or true for a default element spec (defaults to
17613 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17616 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17619 hiddenField: false,
17621 onRender : function(ct, position)
17623 Roo.form.DateField.superclass.onRender.call(this, ct, position);
17625 //this.el.dom.removeAttribute('name');
17626 Roo.log("Changing name?");
17627 this.el.dom.setAttribute('name', this.name + '____hidden___' );
17628 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17630 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17631 // prevent input submission
17632 this.hiddenName = this.name;
17639 validateValue : function(value)
17641 value = this.formatDate(value);
17642 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17643 Roo.log('super failed');
17646 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17649 var svalue = value;
17650 value = this.parseDate(value);
17652 Roo.log('parse date failed' + svalue);
17653 this.markInvalid(String.format(this.invalidText, svalue, this.format));
17656 var time = value.getTime();
17657 if(this.minValue && time < this.minValue.getTime()){
17658 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17661 if(this.maxValue && time > this.maxValue.getTime()){
17662 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17665 if(this.disabledDays){
17666 var day = value.getDay();
17667 for(var i = 0; i < this.disabledDays.length; i++) {
17668 if(day === this.disabledDays[i]){
17669 this.markInvalid(this.disabledDaysText);
17674 var fvalue = this.formatDate(value);
17675 if(this.ddMatch && this.ddMatch.test(fvalue)){
17676 this.markInvalid(String.format(this.disabledDatesText, fvalue));
17683 // Provides logic to override the default TriggerField.validateBlur which just returns true
17684 validateBlur : function(){
17685 return !this.menu || !this.menu.isVisible();
17688 getName: function()
17690 // returns hidden if it's set..
17691 if (!this.rendered) {return ''};
17692 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
17697 * Returns the current date value of the date field.
17698 * @return {Date} The date value
17700 getValue : function(){
17702 return this.hiddenField ?
17703 this.hiddenField.value :
17704 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17708 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
17709 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17710 * (the default format used is "m/d/y").
17713 //All of these calls set the same date value (May 4, 2006)
17715 //Pass a date object:
17716 var dt = new Date('5/4/06');
17717 dateField.setValue(dt);
17719 //Pass a date string (default format):
17720 dateField.setValue('5/4/06');
17722 //Pass a date string (custom format):
17723 dateField.format = 'Y-m-d';
17724 dateField.setValue('2006-5-4');
17726 * @param {String/Date} date The date or valid date string
17728 setValue : function(date){
17729 if (this.hiddenField) {
17730 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17732 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17733 // make sure the value field is always stored as a date..
17734 this.value = this.parseDate(date);
17740 parseDate : function(value){
17741 if(!value || value instanceof Date){
17744 var v = Date.parseDate(value, this.format);
17745 if (!v && this.useIso) {
17746 v = Date.parseDate(value, 'Y-m-d');
17748 if(!v && this.altFormats){
17749 if(!this.altFormatsArray){
17750 this.altFormatsArray = this.altFormats.split("|");
17752 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17753 v = Date.parseDate(value, this.altFormatsArray[i]);
17760 formatDate : function(date, fmt){
17761 return (!date || !(date instanceof Date)) ?
17762 date : date.dateFormat(fmt || this.format);
17767 select: function(m, d){
17770 this.fireEvent('select', this, d);
17772 show : function(){ // retain focus styling
17776 this.focus.defer(10, this);
17777 var ml = this.menuListeners;
17778 this.menu.un("select", ml.select, this);
17779 this.menu.un("show", ml.show, this);
17780 this.menu.un("hide", ml.hide, this);
17785 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17786 onTriggerClick : function(){
17790 if(this.menu == null){
17791 this.menu = new Roo.menu.DateMenu();
17793 Roo.apply(this.menu.picker, {
17794 showClear: this.allowBlank,
17795 minDate : this.minValue,
17796 maxDate : this.maxValue,
17797 disabledDatesRE : this.ddMatch,
17798 disabledDatesText : this.disabledDatesText,
17799 disabledDays : this.disabledDays,
17800 disabledDaysText : this.disabledDaysText,
17801 format : this.useIso ? 'Y-m-d' : this.format,
17802 minText : String.format(this.minText, this.formatDate(this.minValue)),
17803 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17805 this.menu.on(Roo.apply({}, this.menuListeners, {
17808 this.menu.picker.setValue(this.getValue() || new Date());
17809 this.menu.show(this.el, "tl-bl?");
17812 beforeBlur : function(){
17813 var v = this.parseDate(this.getRawValue());
17823 isDirty : function() {
17824 if(this.disabled) {
17828 if(typeof(this.startValue) === 'undefined'){
17832 return String(this.getValue()) !== String(this.startValue);
17836 cleanLeadingSpace : function(e)
17843 * Ext JS Library 1.1.1
17844 * Copyright(c) 2006-2007, Ext JS, LLC.
17846 * Originally Released Under LGPL - original licence link has changed is not relivant.
17849 * <script type="text/javascript">
17853 * @class Roo.form.MonthField
17854 * @extends Roo.form.TriggerField
17855 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17857 * Create a new MonthField
17858 * @param {Object} config
17860 Roo.form.MonthField = function(config){
17862 Roo.form.MonthField.superclass.constructor.call(this, config);
17868 * Fires when a date is selected
17869 * @param {Roo.form.MonthFieeld} combo This combo box
17870 * @param {Date} date The date selected
17877 if(typeof this.minValue == "string") {
17878 this.minValue = this.parseDate(this.minValue);
17880 if(typeof this.maxValue == "string") {
17881 this.maxValue = this.parseDate(this.maxValue);
17883 this.ddMatch = null;
17884 if(this.disabledDates){
17885 var dd = this.disabledDates;
17887 for(var i = 0; i < dd.length; i++){
17889 if(i != dd.length-1) {
17893 this.ddMatch = new RegExp(re + ")");
17897 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
17899 * @cfg {String} format
17900 * The default date format string which can be overriden for localization support. The format must be
17901 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17905 * @cfg {String} altFormats
17906 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17907 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17909 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17911 * @cfg {Array} disabledDays
17912 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17914 disabledDays : [0,1,2,3,4,5,6],
17916 * @cfg {String} disabledDaysText
17917 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17919 disabledDaysText : "Disabled",
17921 * @cfg {Array} disabledDates
17922 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17923 * expression so they are very powerful. Some examples:
17925 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17926 * <li>["03/08", "09/16"] would disable those days for every year</li>
17927 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17928 * <li>["03/../2006"] would disable every day in March 2006</li>
17929 * <li>["^03"] would disable every day in every March</li>
17931 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17932 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17934 disabledDates : null,
17936 * @cfg {String} disabledDatesText
17937 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17939 disabledDatesText : "Disabled",
17941 * @cfg {Date/String} minValue
17942 * The minimum allowed date. Can be either a Javascript date object or a string date in a
17943 * valid format (defaults to null).
17947 * @cfg {Date/String} maxValue
17948 * The maximum allowed date. Can be either a Javascript date object or a string date in a
17949 * valid format (defaults to null).
17953 * @cfg {String} minText
17954 * The error text to display when the date in the cell is before minValue (defaults to
17955 * 'The date in this field must be after {minValue}').
17957 minText : "The date in this field must be equal to or after {0}",
17959 * @cfg {String} maxTextf
17960 * The error text to display when the date in the cell is after maxValue (defaults to
17961 * 'The date in this field must be before {maxValue}').
17963 maxText : "The date in this field must be equal to or before {0}",
17965 * @cfg {String} invalidText
17966 * The error text to display when the date in the field is invalid (defaults to
17967 * '{value} is not a valid date - it must be in the format {format}').
17969 invalidText : "{0} is not a valid date - it must be in the format {1}",
17971 * @cfg {String} triggerClass
17972 * An additional CSS class used to style the trigger button. The trigger will always get the
17973 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17974 * which displays a calendar icon).
17976 triggerClass : 'x-form-date-trigger',
17980 * @cfg {Boolean} useIso
17981 * if enabled, then the date field will use a hidden field to store the
17982 * real value as iso formated date. default (true)
17986 * @cfg {String/Object} autoCreate
17987 * A DomHelper element spec, or true for a default element spec (defaults to
17988 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17991 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
17994 hiddenField: false,
17996 hideMonthPicker : false,
17998 onRender : function(ct, position)
18000 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18002 this.el.dom.removeAttribute('name');
18003 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18005 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18006 // prevent input submission
18007 this.hiddenName = this.name;
18014 validateValue : function(value)
18016 value = this.formatDate(value);
18017 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18020 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18023 var svalue = value;
18024 value = this.parseDate(value);
18026 this.markInvalid(String.format(this.invalidText, svalue, this.format));
18029 var time = value.getTime();
18030 if(this.minValue && time < this.minValue.getTime()){
18031 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18034 if(this.maxValue && time > this.maxValue.getTime()){
18035 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18038 /*if(this.disabledDays){
18039 var day = value.getDay();
18040 for(var i = 0; i < this.disabledDays.length; i++) {
18041 if(day === this.disabledDays[i]){
18042 this.markInvalid(this.disabledDaysText);
18048 var fvalue = this.formatDate(value);
18049 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18050 this.markInvalid(String.format(this.disabledDatesText, fvalue));
18058 // Provides logic to override the default TriggerField.validateBlur which just returns true
18059 validateBlur : function(){
18060 return !this.menu || !this.menu.isVisible();
18064 * Returns the current date value of the date field.
18065 * @return {Date} The date value
18067 getValue : function(){
18071 return this.hiddenField ?
18072 this.hiddenField.value :
18073 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18077 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
18078 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18079 * (the default format used is "m/d/y").
18082 //All of these calls set the same date value (May 4, 2006)
18084 //Pass a date object:
18085 var dt = new Date('5/4/06');
18086 monthField.setValue(dt);
18088 //Pass a date string (default format):
18089 monthField.setValue('5/4/06');
18091 //Pass a date string (custom format):
18092 monthField.format = 'Y-m-d';
18093 monthField.setValue('2006-5-4');
18095 * @param {String/Date} date The date or valid date string
18097 setValue : function(date){
18098 Roo.log('month setValue' + date);
18099 // can only be first of month..
18101 var val = this.parseDate(date);
18103 if (this.hiddenField) {
18104 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18106 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18107 this.value = this.parseDate(date);
18111 parseDate : function(value){
18112 if(!value || value instanceof Date){
18113 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18116 var v = Date.parseDate(value, this.format);
18117 if (!v && this.useIso) {
18118 v = Date.parseDate(value, 'Y-m-d');
18122 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18126 if(!v && this.altFormats){
18127 if(!this.altFormatsArray){
18128 this.altFormatsArray = this.altFormats.split("|");
18130 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18131 v = Date.parseDate(value, this.altFormatsArray[i]);
18138 formatDate : function(date, fmt){
18139 return (!date || !(date instanceof Date)) ?
18140 date : date.dateFormat(fmt || this.format);
18145 select: function(m, d){
18147 this.fireEvent('select', this, d);
18149 show : function(){ // retain focus styling
18153 this.focus.defer(10, this);
18154 var ml = this.menuListeners;
18155 this.menu.un("select", ml.select, this);
18156 this.menu.un("show", ml.show, this);
18157 this.menu.un("hide", ml.hide, this);
18161 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18162 onTriggerClick : function(){
18166 if(this.menu == null){
18167 this.menu = new Roo.menu.DateMenu();
18171 Roo.apply(this.menu.picker, {
18173 showClear: this.allowBlank,
18174 minDate : this.minValue,
18175 maxDate : this.maxValue,
18176 disabledDatesRE : this.ddMatch,
18177 disabledDatesText : this.disabledDatesText,
18179 format : this.useIso ? 'Y-m-d' : this.format,
18180 minText : String.format(this.minText, this.formatDate(this.minValue)),
18181 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18184 this.menu.on(Roo.apply({}, this.menuListeners, {
18192 // hide month picker get's called when we called by 'before hide';
18194 var ignorehide = true;
18195 p.hideMonthPicker = function(disableAnim){
18199 if(this.monthPicker){
18200 Roo.log("hideMonthPicker called");
18201 if(disableAnim === true){
18202 this.monthPicker.hide();
18204 this.monthPicker.slideOut('t', {duration:.2});
18205 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18206 p.fireEvent("select", this, this.value);
18212 Roo.log('picker set value');
18213 Roo.log(this.getValue());
18214 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18215 m.show(this.el, 'tl-bl?');
18216 ignorehide = false;
18217 // this will trigger hideMonthPicker..
18220 // hidden the day picker
18221 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18227 p.showMonthPicker.defer(100, p);
18233 beforeBlur : function(){
18234 var v = this.parseDate(this.getRawValue());
18240 /** @cfg {Boolean} grow @hide */
18241 /** @cfg {Number} growMin @hide */
18242 /** @cfg {Number} growMax @hide */
18249 * Ext JS Library 1.1.1
18250 * Copyright(c) 2006-2007, Ext JS, LLC.
18252 * Originally Released Under LGPL - original licence link has changed is not relivant.
18255 * <script type="text/javascript">
18260 * @class Roo.form.ComboBox
18261 * @extends Roo.form.TriggerField
18262 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18264 * Create a new ComboBox.
18265 * @param {Object} config Configuration options
18267 Roo.form.ComboBox = function(config){
18268 Roo.form.ComboBox.superclass.constructor.call(this, config);
18272 * Fires when the dropdown list is expanded
18273 * @param {Roo.form.ComboBox} combo This combo box
18278 * Fires when the dropdown list is collapsed
18279 * @param {Roo.form.ComboBox} combo This combo box
18283 * @event beforeselect
18284 * Fires before a list item is selected. Return false to cancel the selection.
18285 * @param {Roo.form.ComboBox} combo This combo box
18286 * @param {Roo.data.Record} record The data record returned from the underlying store
18287 * @param {Number} index The index of the selected item in the dropdown list
18289 'beforeselect' : true,
18292 * Fires when a list item is selected
18293 * @param {Roo.form.ComboBox} combo This combo box
18294 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18295 * @param {Number} index The index of the selected item in the dropdown list
18299 * @event beforequery
18300 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18301 * The event object passed has these properties:
18302 * @param {Roo.form.ComboBox} combo This combo box
18303 * @param {String} query The query
18304 * @param {Boolean} forceAll true to force "all" query
18305 * @param {Boolean} cancel true to cancel the query
18306 * @param {Object} e The query event object
18308 'beforequery': true,
18311 * Fires when the 'add' icon is pressed (add a listener to enable add button)
18312 * @param {Roo.form.ComboBox} combo This combo box
18317 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18318 * @param {Roo.form.ComboBox} combo This combo box
18319 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18325 if(this.transform){
18326 this.allowDomMove = false;
18327 var s = Roo.getDom(this.transform);
18328 if(!this.hiddenName){
18329 this.hiddenName = s.name;
18332 this.mode = 'local';
18333 var d = [], opts = s.options;
18334 for(var i = 0, len = opts.length;i < len; i++){
18336 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18338 this.value = value;
18340 d.push([value, o.text]);
18342 this.store = new Roo.data.SimpleStore({
18344 fields: ['value', 'text'],
18347 this.valueField = 'value';
18348 this.displayField = 'text';
18350 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18351 if(!this.lazyRender){
18352 this.target = true;
18353 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18354 s.parentNode.removeChild(s); // remove it
18355 this.render(this.el.parentNode);
18357 s.parentNode.removeChild(s); // remove it
18362 this.store = Roo.factory(this.store, Roo.data);
18365 this.selectedIndex = -1;
18366 if(this.mode == 'local'){
18367 if(config.queryDelay === undefined){
18368 this.queryDelay = 10;
18370 if(config.minChars === undefined){
18376 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18378 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18381 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18382 * rendering into an Roo.Editor, defaults to false)
18385 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18386 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18389 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18392 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18393 * the dropdown list (defaults to undefined, with no header element)
18397 * @cfg {String/Roo.Template} tpl The template to use to render the output
18401 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18403 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18405 listWidth: undefined,
18407 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18408 * mode = 'remote' or 'text' if mode = 'local')
18410 displayField: undefined,
18412 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18413 * mode = 'remote' or 'value' if mode = 'local').
18414 * Note: use of a valueField requires the user make a selection
18415 * in order for a value to be mapped.
18417 valueField: undefined,
18421 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18422 * field's data value (defaults to the underlying DOM element's name)
18424 hiddenName: undefined,
18426 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18430 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18432 selectedClass: 'x-combo-selected',
18434 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
18435 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18436 * which displays a downward arrow icon).
18438 triggerClass : 'x-form-arrow-trigger',
18440 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18444 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18445 * anchor positions (defaults to 'tl-bl')
18447 listAlign: 'tl-bl?',
18449 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18453 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
18454 * query specified by the allQuery config option (defaults to 'query')
18456 triggerAction: 'query',
18458 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18459 * (defaults to 4, does not apply if editable = false)
18463 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18464 * delay (typeAheadDelay) if it matches a known value (defaults to false)
18468 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18469 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18473 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18474 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
18478 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
18479 * when editable = true (defaults to false)
18481 selectOnFocus:false,
18483 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18485 queryParam: 'query',
18487 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
18488 * when mode = 'remote' (defaults to 'Loading...')
18490 loadingText: 'Loading...',
18492 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18496 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18500 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18501 * traditional select (defaults to true)
18505 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18509 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18513 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18514 * listWidth has a higher value)
18518 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18519 * allow the user to set arbitrary text into the field (defaults to false)
18521 forceSelection:false,
18523 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18524 * if typeAhead = true (defaults to 250)
18526 typeAheadDelay : 250,
18528 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18529 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18531 valueNotFoundText : undefined,
18533 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18535 blockFocus : false,
18538 * @cfg {Boolean} disableClear Disable showing of clear button.
18540 disableClear : false,
18542 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
18544 alwaysQuery : false,
18550 // element that contains real text value.. (when hidden is used..)
18553 onRender : function(ct, position)
18555 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18557 if(this.hiddenName){
18558 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
18560 this.hiddenField.value =
18561 this.hiddenValue !== undefined ? this.hiddenValue :
18562 this.value !== undefined ? this.value : '';
18564 // prevent input submission
18565 this.el.dom.removeAttribute('name');
18571 this.el.dom.setAttribute('autocomplete', 'off');
18574 var cls = 'x-combo-list';
18576 this.list = new Roo.Layer({
18577 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18580 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18581 this.list.setWidth(lw);
18582 this.list.swallowEvent('mousewheel');
18583 this.assetHeight = 0;
18586 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18587 this.assetHeight += this.header.getHeight();
18590 this.innerList = this.list.createChild({cls:cls+'-inner'});
18591 this.innerList.on('mouseover', this.onViewOver, this);
18592 this.innerList.on('mousemove', this.onViewMove, this);
18593 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18595 if(this.allowBlank && !this.pageSize && !this.disableClear){
18596 this.footer = this.list.createChild({cls:cls+'-ft'});
18597 this.pageTb = new Roo.Toolbar(this.footer);
18601 this.footer = this.list.createChild({cls:cls+'-ft'});
18602 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18603 {pageSize: this.pageSize});
18607 if (this.pageTb && this.allowBlank && !this.disableClear) {
18609 this.pageTb.add(new Roo.Toolbar.Fill(), {
18610 cls: 'x-btn-icon x-btn-clear',
18612 handler: function()
18615 _this.clearValue();
18616 _this.onSelect(false, -1);
18621 this.assetHeight += this.footer.getHeight();
18626 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18629 this.view = new Roo.View(this.innerList, this.tpl, {
18632 selectedClass: this.selectedClass
18635 this.view.on('click', this.onViewClick, this);
18637 this.store.on('beforeload', this.onBeforeLoad, this);
18638 this.store.on('load', this.onLoad, this);
18639 this.store.on('loadexception', this.onLoadException, this);
18641 if(this.resizable){
18642 this.resizer = new Roo.Resizable(this.list, {
18643 pinned:true, handles:'se'
18645 this.resizer.on('resize', function(r, w, h){
18646 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18647 this.listWidth = w;
18648 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18649 this.restrictHeight();
18651 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18653 if(!this.editable){
18654 this.editable = true;
18655 this.setEditable(false);
18659 if (typeof(this.events.add.listeners) != 'undefined') {
18661 this.addicon = this.wrap.createChild(
18662 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
18664 this.addicon.on('click', function(e) {
18665 this.fireEvent('add', this);
18668 if (typeof(this.events.edit.listeners) != 'undefined') {
18670 this.editicon = this.wrap.createChild(
18671 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
18672 if (this.addicon) {
18673 this.editicon.setStyle('margin-left', '40px');
18675 this.editicon.on('click', function(e) {
18677 // we fire even if inothing is selected..
18678 this.fireEvent('edit', this, this.lastData );
18688 initEvents : function(){
18689 Roo.form.ComboBox.superclass.initEvents.call(this);
18691 this.keyNav = new Roo.KeyNav(this.el, {
18692 "up" : function(e){
18693 this.inKeyMode = true;
18697 "down" : function(e){
18698 if(!this.isExpanded()){
18699 this.onTriggerClick();
18701 this.inKeyMode = true;
18706 "enter" : function(e){
18707 this.onViewClick();
18711 "esc" : function(e){
18715 "tab" : function(e){
18716 this.onViewClick(false);
18717 this.fireEvent("specialkey", this, e);
18723 doRelay : function(foo, bar, hname){
18724 if(hname == 'down' || this.scope.isExpanded()){
18725 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18732 this.queryDelay = Math.max(this.queryDelay || 10,
18733 this.mode == 'local' ? 10 : 250);
18734 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18735 if(this.typeAhead){
18736 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18738 if(this.editable !== false){
18739 this.el.on("keyup", this.onKeyUp, this);
18741 if(this.forceSelection){
18742 this.on('blur', this.doForce, this);
18746 onDestroy : function(){
18748 this.view.setStore(null);
18749 this.view.el.removeAllListeners();
18750 this.view.el.remove();
18751 this.view.purgeListeners();
18754 this.list.destroy();
18757 this.store.un('beforeload', this.onBeforeLoad, this);
18758 this.store.un('load', this.onLoad, this);
18759 this.store.un('loadexception', this.onLoadException, this);
18761 Roo.form.ComboBox.superclass.onDestroy.call(this);
18765 fireKey : function(e){
18766 if(e.isNavKeyPress() && !this.list.isVisible()){
18767 this.fireEvent("specialkey", this, e);
18772 onResize: function(w, h){
18773 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18775 if(typeof w != 'number'){
18776 // we do not handle it!?!?
18779 var tw = this.trigger.getWidth();
18780 tw += this.addicon ? this.addicon.getWidth() : 0;
18781 tw += this.editicon ? this.editicon.getWidth() : 0;
18783 this.el.setWidth( this.adjustWidth('input', x));
18785 this.trigger.setStyle('left', x+'px');
18787 if(this.list && this.listWidth === undefined){
18788 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18789 this.list.setWidth(lw);
18790 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18798 * Allow or prevent the user from directly editing the field text. If false is passed,
18799 * the user will only be able to select from the items defined in the dropdown list. This method
18800 * is the runtime equivalent of setting the 'editable' config option at config time.
18801 * @param {Boolean} value True to allow the user to directly edit the field text
18803 setEditable : function(value){
18804 if(value == this.editable){
18807 this.editable = value;
18809 this.el.dom.setAttribute('readOnly', true);
18810 this.el.on('mousedown', this.onTriggerClick, this);
18811 this.el.addClass('x-combo-noedit');
18813 this.el.dom.setAttribute('readOnly', false);
18814 this.el.un('mousedown', this.onTriggerClick, this);
18815 this.el.removeClass('x-combo-noedit');
18820 onBeforeLoad : function(){
18821 if(!this.hasFocus){
18824 this.innerList.update(this.loadingText ?
18825 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18826 this.restrictHeight();
18827 this.selectedIndex = -1;
18831 onLoad : function(){
18832 if(!this.hasFocus){
18835 if(this.store.getCount() > 0){
18837 this.restrictHeight();
18838 if(this.lastQuery == this.allQuery){
18840 this.el.dom.select();
18842 if(!this.selectByValue(this.value, true)){
18843 this.select(0, true);
18847 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18848 this.taTask.delay(this.typeAheadDelay);
18852 this.onEmptyResults();
18857 onLoadException : function()
18860 Roo.log(this.store.reader.jsonData);
18861 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18862 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18868 onTypeAhead : function(){
18869 if(this.store.getCount() > 0){
18870 var r = this.store.getAt(0);
18871 var newValue = r.data[this.displayField];
18872 var len = newValue.length;
18873 var selStart = this.getRawValue().length;
18874 if(selStart != len){
18875 this.setRawValue(newValue);
18876 this.selectText(selStart, newValue.length);
18882 onSelect : function(record, index){
18883 if(this.fireEvent('beforeselect', this, record, index) !== false){
18884 this.setFromData(index > -1 ? record.data : false);
18886 this.fireEvent('select', this, record, index);
18891 * Returns the currently selected field value or empty string if no value is set.
18892 * @return {String} value The selected value
18894 getValue : function(){
18895 if(this.valueField){
18896 return typeof this.value != 'undefined' ? this.value : '';
18898 return Roo.form.ComboBox.superclass.getValue.call(this);
18902 * Clears any text/value currently set in the field
18904 clearValue : function(){
18905 if(this.hiddenField){
18906 this.hiddenField.value = '';
18909 this.setRawValue('');
18910 this.lastSelectionText = '';
18915 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18916 * will be displayed in the field. If the value does not match the data value of an existing item,
18917 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18918 * Otherwise the field will be blank (although the value will still be set).
18919 * @param {String} value The value to match
18921 setValue : function(v){
18923 if(this.valueField){
18924 var r = this.findRecord(this.valueField, v);
18926 text = r.data[this.displayField];
18927 }else if(this.valueNotFoundText !== undefined){
18928 text = this.valueNotFoundText;
18931 this.lastSelectionText = text;
18932 if(this.hiddenField){
18933 this.hiddenField.value = v;
18935 Roo.form.ComboBox.superclass.setValue.call(this, text);
18939 * @property {Object} the last set data for the element
18944 * Sets the value of the field based on a object which is related to the record format for the store.
18945 * @param {Object} value the value to set as. or false on reset?
18947 setFromData : function(o){
18948 var dv = ''; // display value
18949 var vv = ''; // value value..
18951 if (this.displayField) {
18952 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18954 // this is an error condition!!!
18955 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18958 if(this.valueField){
18959 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18961 if(this.hiddenField){
18962 this.hiddenField.value = vv;
18964 this.lastSelectionText = dv;
18965 Roo.form.ComboBox.superclass.setValue.call(this, dv);
18969 // no hidden field.. - we store the value in 'value', but still display
18970 // display field!!!!
18971 this.lastSelectionText = dv;
18972 Roo.form.ComboBox.superclass.setValue.call(this, dv);
18978 reset : function(){
18979 // overridden so that last data is reset..
18980 this.setValue(this.resetValue);
18981 this.originalValue = this.getValue();
18982 this.clearInvalid();
18983 this.lastData = false;
18985 this.view.clearSelections();
18989 findRecord : function(prop, value){
18991 if(this.store.getCount() > 0){
18992 this.store.each(function(r){
18993 if(r.data[prop] == value){
19003 getName: function()
19005 // returns hidden if it's set..
19006 if (!this.rendered) {return ''};
19007 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
19011 onViewMove : function(e, t){
19012 this.inKeyMode = false;
19016 onViewOver : function(e, t){
19017 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19020 var item = this.view.findItemFromChild(t);
19022 var index = this.view.indexOf(item);
19023 this.select(index, false);
19028 onViewClick : function(doFocus)
19030 var index = this.view.getSelectedIndexes()[0];
19031 var r = this.store.getAt(index);
19033 this.onSelect(r, index);
19035 if(doFocus !== false && !this.blockFocus){
19041 restrictHeight : function(){
19042 this.innerList.dom.style.height = '';
19043 var inner = this.innerList.dom;
19044 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19045 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19046 this.list.beginUpdate();
19047 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19048 this.list.alignTo(this.el, this.listAlign);
19049 this.list.endUpdate();
19053 onEmptyResults : function(){
19058 * Returns true if the dropdown list is expanded, else false.
19060 isExpanded : function(){
19061 return this.list.isVisible();
19065 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19066 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19067 * @param {String} value The data value of the item to select
19068 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19069 * selected item if it is not currently in view (defaults to true)
19070 * @return {Boolean} True if the value matched an item in the list, else false
19072 selectByValue : function(v, scrollIntoView){
19073 if(v !== undefined && v !== null){
19074 var r = this.findRecord(this.valueField || this.displayField, v);
19076 this.select(this.store.indexOf(r), scrollIntoView);
19084 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19085 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19086 * @param {Number} index The zero-based index of the list item to select
19087 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19088 * selected item if it is not currently in view (defaults to true)
19090 select : function(index, scrollIntoView){
19091 this.selectedIndex = index;
19092 this.view.select(index);
19093 if(scrollIntoView !== false){
19094 var el = this.view.getNode(index);
19096 this.innerList.scrollChildIntoView(el, false);
19102 selectNext : function(){
19103 var ct = this.store.getCount();
19105 if(this.selectedIndex == -1){
19107 }else if(this.selectedIndex < ct-1){
19108 this.select(this.selectedIndex+1);
19114 selectPrev : function(){
19115 var ct = this.store.getCount();
19117 if(this.selectedIndex == -1){
19119 }else if(this.selectedIndex != 0){
19120 this.select(this.selectedIndex-1);
19126 onKeyUp : function(e){
19127 if(this.editable !== false && !e.isSpecialKey()){
19128 this.lastKey = e.getKey();
19129 this.dqTask.delay(this.queryDelay);
19134 validateBlur : function(){
19135 return !this.list || !this.list.isVisible();
19139 initQuery : function(){
19140 this.doQuery(this.getRawValue());
19144 doForce : function(){
19145 if(this.el.dom.value.length > 0){
19146 this.el.dom.value =
19147 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19153 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
19154 * query allowing the query action to be canceled if needed.
19155 * @param {String} query The SQL query to execute
19156 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19157 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
19158 * saved in the current store (defaults to false)
19160 doQuery : function(q, forceAll){
19161 if(q === undefined || q === null){
19166 forceAll: forceAll,
19170 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19174 forceAll = qe.forceAll;
19175 if(forceAll === true || (q.length >= this.minChars)){
19176 if(this.lastQuery != q || this.alwaysQuery){
19177 this.lastQuery = q;
19178 if(this.mode == 'local'){
19179 this.selectedIndex = -1;
19181 this.store.clearFilter();
19183 this.store.filter(this.displayField, q);
19187 this.store.baseParams[this.queryParam] = q;
19189 params: this.getParams(q)
19194 this.selectedIndex = -1;
19201 getParams : function(q){
19203 //p[this.queryParam] = q;
19206 p.limit = this.pageSize;
19212 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19214 collapse : function(){
19215 if(!this.isExpanded()){
19219 Roo.get(document).un('mousedown', this.collapseIf, this);
19220 Roo.get(document).un('mousewheel', this.collapseIf, this);
19221 if (!this.editable) {
19222 Roo.get(document).un('keydown', this.listKeyPress, this);
19224 this.fireEvent('collapse', this);
19228 collapseIf : function(e){
19229 if(!e.within(this.wrap) && !e.within(this.list)){
19235 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19237 expand : function(){
19238 if(this.isExpanded() || !this.hasFocus){
19241 this.list.alignTo(this.el, this.listAlign);
19243 Roo.get(document).on('mousedown', this.collapseIf, this);
19244 Roo.get(document).on('mousewheel', this.collapseIf, this);
19245 if (!this.editable) {
19246 Roo.get(document).on('keydown', this.listKeyPress, this);
19249 this.fireEvent('expand', this);
19253 // Implements the default empty TriggerField.onTriggerClick function
19254 onTriggerClick : function(){
19258 if(this.isExpanded()){
19260 if (!this.blockFocus) {
19265 this.hasFocus = true;
19266 if(this.triggerAction == 'all') {
19267 this.doQuery(this.allQuery, true);
19269 this.doQuery(this.getRawValue());
19271 if (!this.blockFocus) {
19276 listKeyPress : function(e)
19278 //Roo.log('listkeypress');
19279 // scroll to first matching element based on key pres..
19280 if (e.isSpecialKey()) {
19283 var k = String.fromCharCode(e.getKey()).toUpperCase();
19286 var csel = this.view.getSelectedNodes();
19287 var cselitem = false;
19289 var ix = this.view.indexOf(csel[0]);
19290 cselitem = this.store.getAt(ix);
19291 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19297 this.store.each(function(v) {
19299 // start at existing selection.
19300 if (cselitem.id == v.id) {
19306 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19307 match = this.store.indexOf(v);
19312 if (match === false) {
19313 return true; // no more action?
19316 this.view.select(match);
19317 var sn = Roo.get(this.view.getSelectedNodes()[0]);
19318 sn.scrollIntoView(sn.dom.parentNode, false);
19322 * @cfg {Boolean} grow
19326 * @cfg {Number} growMin
19330 * @cfg {Number} growMax
19338 * Copyright(c) 2010-2012, Roo J Solutions Limited
19345 * @class Roo.form.ComboBoxArray
19346 * @extends Roo.form.TextField
19347 * A facebook style adder... for lists of email / people / countries etc...
19348 * pick multiple items from a combo box, and shows each one.
19350 * Fred [x] Brian [x] [Pick another |v]
19353 * For this to work: it needs various extra information
19354 * - normal combo problay has
19356 * + displayField, valueField
19358 * For our purpose...
19361 * If we change from 'extends' to wrapping...
19368 * Create a new ComboBoxArray.
19369 * @param {Object} config Configuration options
19373 Roo.form.ComboBoxArray = function(config)
19377 * @event beforeremove
19378 * Fires before remove the value from the list
19379 * @param {Roo.form.ComboBoxArray} _self This combo box array
19380 * @param {Roo.form.ComboBoxArray.Item} item removed item
19382 'beforeremove' : true,
19385 * Fires when remove the value from the list
19386 * @param {Roo.form.ComboBoxArray} _self This combo box array
19387 * @param {Roo.form.ComboBoxArray.Item} item removed item
19394 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19396 this.items = new Roo.util.MixedCollection(false);
19398 // construct the child combo...
19408 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19411 * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19416 // behavies liek a hiddne field
19417 inputType: 'hidden',
19419 * @cfg {Number} width The width of the box that displays the selected element
19426 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
19430 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
19432 hiddenName : false,
19434 * @cfg {String} seperator The value seperator normally ','
19438 // private the array of items that are displayed..
19440 // private - the hidden field el.
19442 // private - the filed el..
19445 //validateValue : function() { return true; }, // all values are ok!
19446 //onAddClick: function() { },
19448 onRender : function(ct, position)
19451 // create the standard hidden element
19452 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19455 // give fake names to child combo;
19456 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19457 this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19459 this.combo = Roo.factory(this.combo, Roo.form);
19460 this.combo.onRender(ct, position);
19461 if (typeof(this.combo.width) != 'undefined') {
19462 this.combo.onResize(this.combo.width,0);
19465 this.combo.initEvents();
19467 // assigned so form know we need to do this..
19468 this.store = this.combo.store;
19469 this.valueField = this.combo.valueField;
19470 this.displayField = this.combo.displayField ;
19473 this.combo.wrap.addClass('x-cbarray-grp');
19475 var cbwrap = this.combo.wrap.createChild(
19476 {tag: 'div', cls: 'x-cbarray-cb'},
19481 this.hiddenEl = this.combo.wrap.createChild({
19482 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
19484 this.el = this.combo.wrap.createChild({
19485 tag: 'input', type:'hidden' , name: this.name, value : ''
19487 // this.el.dom.removeAttribute("name");
19490 this.outerWrap = this.combo.wrap;
19491 this.wrap = cbwrap;
19493 this.outerWrap.setWidth(this.width);
19494 this.outerWrap.dom.removeChild(this.el.dom);
19496 this.wrap.dom.appendChild(this.el.dom);
19497 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19498 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19500 this.combo.trigger.setStyle('position','relative');
19501 this.combo.trigger.setStyle('left', '0px');
19502 this.combo.trigger.setStyle('top', '2px');
19504 this.combo.el.setStyle('vertical-align', 'text-bottom');
19506 //this.trigger.setStyle('vertical-align', 'top');
19508 // this should use the code from combo really... on('add' ....)
19512 this.adder = this.outerWrap.createChild(
19513 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
19515 this.adder.on('click', function(e) {
19516 _t.fireEvent('adderclick', this, e);
19520 //this.adder.on('click', this.onAddClick, _t);
19523 this.combo.on('select', function(cb, rec, ix) {
19524 this.addItem(rec.data);
19527 cb.el.dom.value = '';
19528 //cb.lastData = rec.data;
19537 getName: function()
19539 // returns hidden if it's set..
19540 if (!this.rendered) {return ''};
19541 return this.hiddenName ? this.hiddenName : this.name;
19546 onResize: function(w, h){
19549 // not sure if this is needed..
19550 //this.combo.onResize(w,h);
19552 if(typeof w != 'number'){
19553 // we do not handle it!?!?
19556 var tw = this.combo.trigger.getWidth();
19557 tw += this.addicon ? this.addicon.getWidth() : 0;
19558 tw += this.editicon ? this.editicon.getWidth() : 0;
19560 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19562 this.combo.trigger.setStyle('left', '0px');
19564 if(this.list && this.listWidth === undefined){
19565 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19566 this.list.setWidth(lw);
19567 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19574 addItem: function(rec)
19576 var valueField = this.combo.valueField;
19577 var displayField = this.combo.displayField;
19579 if (this.items.indexOfKey(rec[valueField]) > -1) {
19580 //console.log("GOT " + rec.data.id);
19584 var x = new Roo.form.ComboBoxArray.Item({
19585 //id : rec[this.idField],
19587 displayField : displayField ,
19588 tipField : displayField ,
19592 this.items.add(rec[valueField],x);
19593 // add it before the element..
19594 this.updateHiddenEl();
19595 x.render(this.outerWrap, this.wrap.dom);
19596 // add the image handler..
19599 updateHiddenEl : function()
19602 if (!this.hiddenEl) {
19606 var idField = this.combo.valueField;
19608 this.items.each(function(f) {
19609 ar.push(f.data[idField]);
19611 this.hiddenEl.dom.value = ar.join(this.seperator);
19617 this.items.clear();
19619 Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19623 this.el.dom.value = '';
19624 if (this.hiddenEl) {
19625 this.hiddenEl.dom.value = '';
19629 getValue: function()
19631 return this.hiddenEl ? this.hiddenEl.dom.value : '';
19633 setValue: function(v) // not a valid action - must use addItems..
19638 if (this.store.isLocal && (typeof(v) == 'string')) {
19639 // then we can use the store to find the values..
19640 // comma seperated at present.. this needs to allow JSON based encoding..
19641 this.hiddenEl.value = v;
19643 Roo.each(v.split(this.seperator), function(k) {
19644 Roo.log("CHECK " + this.valueField + ',' + k);
19645 var li = this.store.query(this.valueField, k);
19650 add[this.valueField] = k;
19651 add[this.displayField] = li.item(0).data[this.displayField];
19657 if (typeof(v) == 'object' ) {
19658 // then let's assume it's an array of objects..
19659 Roo.each(v, function(l) {
19661 if (typeof(l) == 'string') {
19663 add[this.valueField] = l;
19664 add[this.displayField] = l
19673 setFromData: function(v)
19675 // this recieves an object, if setValues is called.
19677 this.el.dom.value = v[this.displayField];
19678 this.hiddenEl.dom.value = v[this.valueField];
19679 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19682 var kv = v[this.valueField];
19683 var dv = v[this.displayField];
19684 kv = typeof(kv) != 'string' ? '' : kv;
19685 dv = typeof(dv) != 'string' ? '' : dv;
19688 var keys = kv.split(this.seperator);
19689 var display = dv.split(this.seperator);
19690 for (var i = 0 ; i < keys.length; i++) {
19692 add[this.valueField] = keys[i];
19693 add[this.displayField] = display[i];
19701 * Validates the combox array value
19702 * @return {Boolean} True if the value is valid, else false
19704 validate : function(){
19705 if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19706 this.clearInvalid();
19712 validateValue : function(value){
19713 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19721 isDirty : function() {
19722 if(this.disabled) {
19727 var d = Roo.decode(String(this.originalValue));
19729 return String(this.getValue()) !== String(this.originalValue);
19732 var originalValue = [];
19734 for (var i = 0; i < d.length; i++){
19735 originalValue.push(d[i][this.valueField]);
19738 return String(this.getValue()) !== String(originalValue.join(this.seperator));
19747 * @class Roo.form.ComboBoxArray.Item
19748 * @extends Roo.BoxComponent
19749 * A selected item in the list
19750 * Fred [x] Brian [x] [Pick another |v]
19753 * Create a new item.
19754 * @param {Object} config Configuration options
19757 Roo.form.ComboBoxArray.Item = function(config) {
19758 config.id = Roo.id();
19759 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19762 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19765 displayField : false,
19769 defaultAutoCreate : {
19771 cls: 'x-cbarray-item',
19778 src : Roo.BLANK_IMAGE_URL ,
19786 onRender : function(ct, position)
19788 Roo.form.Field.superclass.onRender.call(this, ct, position);
19791 var cfg = this.getAutoCreate();
19792 this.el = ct.createChild(cfg, position);
19795 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19797 this.el.child('div').dom.innerHTML = this.cb.renderer ?
19798 this.cb.renderer(this.data) :
19799 String.format('{0}',this.data[this.displayField]);
19802 this.el.child('div').dom.setAttribute('qtip',
19803 String.format('{0}',this.data[this.tipField])
19806 this.el.child('img').on('click', this.remove, this);
19810 remove : function()
19812 if(this.cb.disabled){
19816 if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19817 this.cb.items.remove(this);
19818 this.el.child('img').un('click', this.remove, this);
19820 this.cb.updateHiddenEl();
19822 this.cb.fireEvent('remove', this.cb, this);
19827 * RooJS Library 1.1.1
19828 * Copyright(c) 2008-2011 Alan Knowles
19835 * @class Roo.form.ComboNested
19836 * @extends Roo.form.ComboBox
19837 * A combobox for that allows selection of nested items in a list,
19852 * Create a new ComboNested
19853 * @param {Object} config Configuration options
19855 Roo.form.ComboNested = function(config){
19856 Roo.form.ComboCheck.superclass.constructor.call(this, config);
19857 // should verify some data...
19859 // hiddenName = required..
19860 // displayField = required
19861 // valudField == required
19862 var req= [ 'hiddenName', 'displayField', 'valueField' ];
19864 Roo.each(req, function(e) {
19865 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19866 throw "Roo.form.ComboNested : missing value for: " + e;
19873 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19876 * @config {Number} max Number of columns to show
19881 list : null, // the outermost div..
19882 innerLists : null, // the
19886 loadingChildren : false,
19888 onRender : function(ct, position)
19890 Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19892 if(this.hiddenName){
19893 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
19895 this.hiddenField.value =
19896 this.hiddenValue !== undefined ? this.hiddenValue :
19897 this.value !== undefined ? this.value : '';
19899 // prevent input submission
19900 this.el.dom.removeAttribute('name');
19906 this.el.dom.setAttribute('autocomplete', 'off');
19909 var cls = 'x-combo-list';
19911 this.list = new Roo.Layer({
19912 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19915 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19916 this.list.setWidth(lw);
19917 this.list.swallowEvent('mousewheel');
19918 this.assetHeight = 0;
19921 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19922 this.assetHeight += this.header.getHeight();
19924 this.innerLists = [];
19927 for (var i =0 ; i < this.maxColumns; i++) {
19928 this.onRenderList( cls, i);
19931 // always needs footer, as we are going to have an 'OK' button.
19932 this.footer = this.list.createChild({cls:cls+'-ft'});
19933 this.pageTb = new Roo.Toolbar(this.footer);
19938 handler: function()
19944 if ( this.allowBlank && !this.disableClear) {
19946 this.pageTb.add(new Roo.Toolbar.Fill(), {
19947 cls: 'x-btn-icon x-btn-clear',
19949 handler: function()
19952 _this.clearValue();
19953 _this.onSelect(false, -1);
19958 this.assetHeight += this.footer.getHeight();
19962 onRenderList : function ( cls, i)
19965 var lw = Math.floor(
19966 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
19969 this.list.setWidth(lw); // default to '1'
19971 var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
19972 //il.on('mouseover', this.onViewOver, this, { list: i });
19973 //il.on('mousemove', this.onViewMove, this, { list: i });
19975 il.setStyle({ 'overflow-x' : 'hidden'});
19978 this.tpl = new Roo.Template({
19979 html : '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
19980 isEmpty: function (value, allValues) {
19982 var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
19983 return dl ? 'has-children' : 'no-children'
19988 var store = this.store;
19990 store = new Roo.data.SimpleStore({
19991 //fields : this.store.reader.meta.fields,
19992 reader : this.store.reader,
19996 this.stores[i] = store;
19998 var view = this.views[i] = new Roo.View(
20004 selectedClass: this.selectedClass
20007 view.getEl().setWidth(lw);
20008 view.getEl().setStyle({
20009 position: i < 1 ? 'relative' : 'absolute',
20011 left: (i * lw ) + 'px',
20012 display : i > 0 ? 'none' : 'block'
20014 view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20015 view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20016 //view.on('click', this.onViewClick, this, { list : i });
20018 store.on('beforeload', this.onBeforeLoad, this);
20019 store.on('load', this.onLoad, this, { list : i});
20020 store.on('loadexception', this.onLoadException, this);
20022 // hide the other vies..
20028 restrictHeight : function()
20031 Roo.each(this.innerLists, function(il,i) {
20032 var el = this.views[i].getEl();
20033 el.dom.style.height = '';
20034 var inner = el.dom;
20035 var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20036 // only adjust heights on other ones..
20037 mh = Math.max(h, mh);
20040 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20041 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20048 this.list.beginUpdate();
20049 this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20050 this.list.alignTo(this.el, this.listAlign);
20051 this.list.endUpdate();
20056 // -- store handlers..
20058 onBeforeLoad : function()
20060 if(!this.hasFocus){
20063 this.innerLists[0].update(this.loadingText ?
20064 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20065 this.restrictHeight();
20066 this.selectedIndex = -1;
20069 onLoad : function(a,b,c,d)
20071 if (!this.loadingChildren) {
20072 // then we are loading the top level. - hide the children
20073 for (var i = 1;i < this.views.length; i++) {
20074 this.views[i].getEl().setStyle({ display : 'none' });
20076 var lw = Math.floor(
20077 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20080 this.list.setWidth(lw); // default to '1'
20084 if(!this.hasFocus){
20088 if(this.store.getCount() > 0) {
20090 this.restrictHeight();
20092 this.onEmptyResults();
20095 if (!this.loadingChildren) {
20096 this.selectActive();
20099 this.stores[1].loadData([]);
20100 this.stores[2].loadData([]);
20109 onLoadException : function()
20112 Roo.log(this.store.reader.jsonData);
20113 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20114 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20119 // no cleaning of leading spaces on blur here.
20120 cleanLeadingSpace : function(e) { },
20123 onSelectChange : function (view, sels, opts )
20125 var ix = view.getSelectedIndexes();
20127 if (opts.list > this.maxColumns - 2) {
20128 if (view.store.getCount()< 1) {
20129 this.views[opts.list ].getEl().setStyle({ display : 'none' });
20133 // used to clear ?? but if we are loading unselected
20134 this.setFromData(view.store.getAt(ix[0]).data);
20143 // this get's fired when trigger opens..
20144 // this.setFromData({});
20145 var str = this.stores[opts.list+1];
20146 str.data.clear(); // removeall wihtout the fire events..
20150 var rec = view.store.getAt(ix[0]);
20152 this.setFromData(rec.data);
20153 this.fireEvent('select', this, rec, ix[0]);
20155 var lw = Math.floor(
20157 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20158 ) / this.maxColumns
20160 this.loadingChildren = true;
20161 this.stores[opts.list+1].loadDataFromChildren( rec );
20162 this.loadingChildren = false;
20163 var dl = this.stores[opts.list+1]. getTotalCount();
20165 this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20167 this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20168 for (var i = opts.list+2; i < this.views.length;i++) {
20169 this.views[i].getEl().setStyle({ display : 'none' });
20172 this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20173 this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20175 if (this.isLoading) {
20176 // this.selectActive(opts.list);
20184 onDoubleClick : function()
20186 this.collapse(); //??
20194 recordToStack : function(store, prop, value, stack)
20196 var cstore = new Roo.data.SimpleStore({
20197 //fields : this.store.reader.meta.fields, // we need array reader.. for
20198 reader : this.store.reader,
20202 var record = false;
20204 if(store.getCount() < 1){
20207 store.each(function(r){
20208 if(r.data[prop] == value){
20213 if (r.data.cn && r.data.cn.length) {
20214 cstore.loadDataFromChildren( r);
20215 var cret = _this.recordToStack(cstore, prop, value, stack);
20216 if (cret !== false) {
20225 if (record == false) {
20228 stack.unshift(srec);
20233 * find the stack of stores that match our value.
20238 selectActive : function ()
20240 // if store is not loaded, then we will need to wait for that to happen first.
20242 this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20243 for (var i = 0; i < stack.length; i++ ) {
20244 this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20256 * Ext JS Library 1.1.1
20257 * Copyright(c) 2006-2007, Ext JS, LLC.
20259 * Originally Released Under LGPL - original licence link has changed is not relivant.
20262 * <script type="text/javascript">
20265 * @class Roo.form.Checkbox
20266 * @extends Roo.form.Field
20267 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
20269 * Creates a new Checkbox
20270 * @param {Object} config Configuration options
20272 Roo.form.Checkbox = function(config){
20273 Roo.form.Checkbox.superclass.constructor.call(this, config);
20277 * Fires when the checkbox is checked or unchecked.
20278 * @param {Roo.form.Checkbox} this This checkbox
20279 * @param {Boolean} checked The new checked value
20285 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
20287 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20289 focusClass : undefined,
20291 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20293 fieldClass: "x-form-field",
20295 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20299 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20300 * {tag: "input", type: "checkbox", autocomplete: "off"})
20302 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20304 * @cfg {String} boxLabel The text that appears beside the checkbox
20308 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20312 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20314 valueOff: '0', // value when not checked..
20316 actionMode : 'viewEl',
20319 itemCls : 'x-menu-check-item x-form-item',
20320 groupClass : 'x-menu-group-item',
20321 inputType : 'hidden',
20324 inSetChecked: false, // check that we are not calling self...
20326 inputElement: false, // real input element?
20327 basedOn: false, // ????
20329 isFormField: true, // not sure where this is needed!!!!
20331 onResize : function(){
20332 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20333 if(!this.boxLabel){
20334 this.el.alignTo(this.wrap, 'c-c');
20338 initEvents : function(){
20339 Roo.form.Checkbox.superclass.initEvents.call(this);
20340 this.el.on("click", this.onClick, this);
20341 this.el.on("change", this.onClick, this);
20345 getResizeEl : function(){
20349 getPositionEl : function(){
20354 onRender : function(ct, position){
20355 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20357 if(this.inputValue !== undefined){
20358 this.el.dom.value = this.inputValue;
20361 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20362 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20363 var viewEl = this.wrap.createChild({
20364 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20365 this.viewEl = viewEl;
20366 this.wrap.on('click', this.onClick, this);
20368 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20369 this.el.on('propertychange', this.setFromHidden, this); //ie
20374 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20375 // viewEl.on('click', this.onClick, this);
20377 //if(this.checked){
20378 this.setChecked(this.checked);
20380 //this.checked = this.el.dom;
20386 initValue : Roo.emptyFn,
20389 * Returns the checked state of the checkbox.
20390 * @return {Boolean} True if checked, else false
20392 getValue : function(){
20394 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20396 return this.valueOff;
20401 onClick : function(){
20402 if (this.disabled) {
20405 this.setChecked(!this.checked);
20407 //if(this.el.dom.checked != this.checked){
20408 // this.setValue(this.el.dom.checked);
20413 * Sets the checked state of the checkbox.
20414 * On is always based on a string comparison between inputValue and the param.
20415 * @param {Boolean/String} value - the value to set
20416 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20418 setValue : function(v,suppressEvent){
20421 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20422 //if(this.el && this.el.dom){
20423 // this.el.dom.checked = this.checked;
20424 // this.el.dom.defaultChecked = this.checked;
20426 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20427 //this.fireEvent("check", this, this.checked);
20430 setChecked : function(state,suppressEvent)
20432 if (this.inSetChecked) {
20433 this.checked = state;
20439 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20441 this.checked = state;
20442 if(suppressEvent !== true){
20443 this.fireEvent('check', this, state);
20445 this.inSetChecked = true;
20446 this.el.dom.value = state ? this.inputValue : this.valueOff;
20447 this.inSetChecked = false;
20450 // handle setting of hidden value by some other method!!?!?
20451 setFromHidden: function()
20456 //console.log("SET FROM HIDDEN");
20457 //alert('setFrom hidden');
20458 this.setValue(this.el.dom.value);
20461 onDestroy : function()
20464 Roo.get(this.viewEl).remove();
20467 Roo.form.Checkbox.superclass.onDestroy.call(this);
20470 setBoxLabel : function(str)
20472 this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20477 * Ext JS Library 1.1.1
20478 * Copyright(c) 2006-2007, Ext JS, LLC.
20480 * Originally Released Under LGPL - original licence link has changed is not relivant.
20483 * <script type="text/javascript">
20487 * @class Roo.form.Radio
20488 * @extends Roo.form.Checkbox
20489 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
20490 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20492 * Creates a new Radio
20493 * @param {Object} config Configuration options
20495 Roo.form.Radio = function(){
20496 Roo.form.Radio.superclass.constructor.apply(this, arguments);
20498 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20499 inputType: 'radio',
20502 * If this radio is part of a group, it will return the selected value
20505 getGroupValue : function(){
20506 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20510 onRender : function(ct, position){
20511 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20513 if(this.inputValue !== undefined){
20514 this.el.dom.value = this.inputValue;
20517 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20518 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20519 //var viewEl = this.wrap.createChild({
20520 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20521 //this.viewEl = viewEl;
20522 //this.wrap.on('click', this.onClick, this);
20524 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20525 //this.el.on('propertychange', this.setFromHidden, this); //ie
20530 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20531 // viewEl.on('click', this.onClick, this);
20534 this.el.dom.checked = 'checked' ;
20540 });//<script type="text/javascript">
20543 * Based Ext JS Library 1.1.1
20544 * Copyright(c) 2006-2007, Ext JS, LLC.
20550 * @class Roo.HtmlEditorCore
20551 * @extends Roo.Component
20552 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20554 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20557 Roo.HtmlEditorCore = function(config){
20560 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20565 * @event initialize
20566 * Fires when the editor is fully initialized (including the iframe)
20567 * @param {Roo.HtmlEditorCore} this
20572 * Fires when the editor is first receives the focus. Any insertion must wait
20573 * until after this event.
20574 * @param {Roo.HtmlEditorCore} this
20578 * @event beforesync
20579 * Fires before the textarea is updated with content from the editor iframe. Return false
20580 * to cancel the sync.
20581 * @param {Roo.HtmlEditorCore} this
20582 * @param {String} html
20586 * @event beforepush
20587 * Fires before the iframe editor is updated with content from the textarea. Return false
20588 * to cancel the push.
20589 * @param {Roo.HtmlEditorCore} this
20590 * @param {String} html
20595 * Fires when the textarea is updated with content from the editor iframe.
20596 * @param {Roo.HtmlEditorCore} this
20597 * @param {String} html
20602 * Fires when the iframe editor is updated with content from the textarea.
20603 * @param {Roo.HtmlEditorCore} this
20604 * @param {String} html
20609 * @event editorevent
20610 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20611 * @param {Roo.HtmlEditorCore} this
20617 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20619 // defaults : white / black...
20620 this.applyBlacklists();
20627 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20631 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20637 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20642 * @cfg {Number} height (in pixels)
20646 * @cfg {Number} width (in pixels)
20651 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20654 stylesheets: false,
20657 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
20659 allowComments: false,
20663 // private properties
20664 validationEvent : false,
20666 initialized : false,
20668 sourceEditMode : false,
20669 onFocus : Roo.emptyFn,
20671 hideMode:'offsets',
20675 // blacklist + whitelisted elements..
20682 * Protected method that will not generally be called directly. It
20683 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20684 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20686 getDocMarkup : function(){
20690 // inherit styels from page...??
20691 if (this.stylesheets === false) {
20693 Roo.get(document.head).select('style').each(function(node) {
20694 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20697 Roo.get(document.head).select('link').each(function(node) {
20698 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20701 } else if (!this.stylesheets.length) {
20703 st = '<style type="text/css">' +
20704 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20707 for (var i in this.stylesheets) {
20708 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
20713 st += '<style type="text/css">' +
20714 'IMG { cursor: pointer } ' +
20717 var cls = 'roo-htmleditor-body';
20719 if(this.bodyCls.length){
20720 cls += ' ' + this.bodyCls;
20723 return '<html><head>' + st +
20724 //<style type="text/css">' +
20725 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20727 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
20731 onRender : function(ct, position)
20734 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20735 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20738 this.el.dom.style.border = '0 none';
20739 this.el.dom.setAttribute('tabIndex', -1);
20740 this.el.addClass('x-hidden hide');
20744 if(Roo.isIE){ // fix IE 1px bogus margin
20745 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20749 this.frameId = Roo.id();
20753 var iframe = this.owner.wrap.createChild({
20755 cls: 'form-control', // bootstrap..
20757 name: this.frameId,
20758 frameBorder : 'no',
20759 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20764 this.iframe = iframe.dom;
20766 this.assignDocWin();
20768 this.doc.designMode = 'on';
20771 this.doc.write(this.getDocMarkup());
20775 var task = { // must defer to wait for browser to be ready
20777 //console.log("run task?" + this.doc.readyState);
20778 this.assignDocWin();
20779 if(this.doc.body || this.doc.readyState == 'complete'){
20781 this.doc.designMode="on";
20785 Roo.TaskMgr.stop(task);
20786 this.initEditor.defer(10, this);
20793 Roo.TaskMgr.start(task);
20798 onResize : function(w, h)
20800 Roo.log('resize: ' +w + ',' + h );
20801 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20805 if(typeof w == 'number'){
20807 this.iframe.style.width = w + 'px';
20809 if(typeof h == 'number'){
20811 this.iframe.style.height = h + 'px';
20813 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20820 * Toggles the editor between standard and source edit mode.
20821 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20823 toggleSourceEdit : function(sourceEditMode){
20825 this.sourceEditMode = sourceEditMode === true;
20827 if(this.sourceEditMode){
20829 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20832 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20833 //this.iframe.className = '';
20836 //this.setSize(this.owner.wrap.getSize());
20837 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20844 * Protected method that will not generally be called directly. If you need/want
20845 * custom HTML cleanup, this is the method you should override.
20846 * @param {String} html The HTML to be cleaned
20847 * return {String} The cleaned HTML
20849 cleanHtml : function(html){
20850 html = String(html);
20851 if(html.length > 5){
20852 if(Roo.isSafari){ // strip safari nonsense
20853 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20856 if(html == ' '){
20863 * HTML Editor -> Textarea
20864 * Protected method that will not generally be called directly. Syncs the contents
20865 * of the editor iframe with the textarea.
20867 syncValue : function(){
20868 if(this.initialized){
20869 var bd = (this.doc.body || this.doc.documentElement);
20870 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20871 var html = bd.innerHTML;
20873 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20874 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20876 html = '<div style="'+m[0]+'">' + html + '</div>';
20879 html = this.cleanHtml(html);
20880 // fix up the special chars.. normaly like back quotes in word...
20881 // however we do not want to do this with chinese..
20882 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
20884 var cc = match.charCodeAt();
20886 // Get the character value, handling surrogate pairs
20887 if (match.length == 2) {
20888 // It's a surrogate pair, calculate the Unicode code point
20889 var high = match.charCodeAt(0) - 0xD800;
20890 var low = match.charCodeAt(1) - 0xDC00;
20891 cc = (high * 0x400) + low + 0x10000;
20893 (cc >= 0x4E00 && cc < 0xA000 ) ||
20894 (cc >= 0x3400 && cc < 0x4E00 ) ||
20895 (cc >= 0xf900 && cc < 0xfb00 )
20900 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
20901 return "&#" + cc + ";";
20908 if(this.owner.fireEvent('beforesync', this, html) !== false){
20909 this.el.dom.value = html;
20910 this.owner.fireEvent('sync', this, html);
20916 * Protected method that will not generally be called directly. Pushes the value of the textarea
20917 * into the iframe editor.
20919 pushValue : function(){
20920 if(this.initialized){
20921 var v = this.el.dom.value.trim();
20923 // if(v.length < 1){
20927 if(this.owner.fireEvent('beforepush', this, v) !== false){
20928 var d = (this.doc.body || this.doc.documentElement);
20930 this.cleanUpPaste();
20931 this.el.dom.value = d.innerHTML;
20932 this.owner.fireEvent('push', this, v);
20938 deferFocus : function(){
20939 this.focus.defer(10, this);
20943 focus : function(){
20944 if(this.win && !this.sourceEditMode){
20951 assignDocWin: function()
20953 var iframe = this.iframe;
20956 this.doc = iframe.contentWindow.document;
20957 this.win = iframe.contentWindow;
20959 // if (!Roo.get(this.frameId)) {
20962 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20963 // this.win = Roo.get(this.frameId).dom.contentWindow;
20965 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20969 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20970 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20975 initEditor : function(){
20976 //console.log("INIT EDITOR");
20977 this.assignDocWin();
20981 this.doc.designMode="on";
20983 this.doc.write(this.getDocMarkup());
20986 var dbody = (this.doc.body || this.doc.documentElement);
20987 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20988 // this copies styles from the containing element into thsi one..
20989 // not sure why we need all of this..
20990 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20992 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20993 //ss['background-attachment'] = 'fixed'; // w3c
20994 dbody.bgProperties = 'fixed'; // ie
20995 //Roo.DomHelper.applyStyles(dbody, ss);
20996 Roo.EventManager.on(this.doc, {
20997 //'mousedown': this.onEditorEvent,
20998 'mouseup': this.onEditorEvent,
20999 'dblclick': this.onEditorEvent,
21000 'click': this.onEditorEvent,
21001 'keyup': this.onEditorEvent,
21006 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21008 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21009 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21011 this.initialized = true;
21013 this.owner.fireEvent('initialize', this);
21018 onDestroy : function(){
21024 //for (var i =0; i < this.toolbars.length;i++) {
21025 // // fixme - ask toolbars for heights?
21026 // this.toolbars[i].onDestroy();
21029 //this.wrap.dom.innerHTML = '';
21030 //this.wrap.remove();
21035 onFirstFocus : function(){
21037 this.assignDocWin();
21040 this.activated = true;
21043 if(Roo.isGecko){ // prevent silly gecko errors
21045 var s = this.win.getSelection();
21046 if(!s.focusNode || s.focusNode.nodeType != 3){
21047 var r = s.getRangeAt(0);
21048 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21053 this.execCmd('useCSS', true);
21054 this.execCmd('styleWithCSS', false);
21057 this.owner.fireEvent('activate', this);
21061 adjustFont: function(btn){
21062 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21063 //if(Roo.isSafari){ // safari
21066 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21067 if(Roo.isSafari){ // safari
21068 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21069 v = (v < 10) ? 10 : v;
21070 v = (v > 48) ? 48 : v;
21071 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21076 v = Math.max(1, v+adjust);
21078 this.execCmd('FontSize', v );
21081 onEditorEvent : function(e)
21083 this.owner.fireEvent('editorevent', this, e);
21084 // this.updateToolbar();
21085 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21088 insertTag : function(tg)
21090 // could be a bit smarter... -> wrap the current selected tRoo..
21091 if (tg.toLowerCase() == 'span' ||
21092 tg.toLowerCase() == 'code' ||
21093 tg.toLowerCase() == 'sup' ||
21094 tg.toLowerCase() == 'sub'
21097 range = this.createRange(this.getSelection());
21098 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21099 wrappingNode.appendChild(range.extractContents());
21100 range.insertNode(wrappingNode);
21107 this.execCmd("formatblock", tg);
21111 insertText : function(txt)
21115 var range = this.createRange();
21116 range.deleteContents();
21117 //alert(Sender.getAttribute('label'));
21119 range.insertNode(this.doc.createTextNode(txt));
21125 * Executes a Midas editor command on the editor document and performs necessary focus and
21126 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21127 * @param {String} cmd The Midas command
21128 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21130 relayCmd : function(cmd, value){
21132 this.execCmd(cmd, value);
21133 this.owner.fireEvent('editorevent', this);
21134 //this.updateToolbar();
21135 this.owner.deferFocus();
21139 * Executes a Midas editor command directly on the editor document.
21140 * For visual commands, you should use {@link #relayCmd} instead.
21141 * <b>This should only be called after the editor is initialized.</b>
21142 * @param {String} cmd The Midas command
21143 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21145 execCmd : function(cmd, value){
21146 this.doc.execCommand(cmd, false, value === undefined ? null : value);
21153 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21155 * @param {String} text | dom node..
21157 insertAtCursor : function(text)
21160 if(!this.activated){
21166 var r = this.doc.selection.createRange();
21177 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21181 // from jquery ui (MIT licenced)
21183 var win = this.win;
21185 if (win.getSelection && win.getSelection().getRangeAt) {
21186 range = win.getSelection().getRangeAt(0);
21187 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21188 range.insertNode(node);
21189 } else if (win.document.selection && win.document.selection.createRange) {
21190 // no firefox support
21191 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21192 win.document.selection.createRange().pasteHTML(txt);
21194 // no firefox support
21195 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21196 this.execCmd('InsertHTML', txt);
21205 mozKeyPress : function(e){
21207 var c = e.getCharCode(), cmd;
21210 c = String.fromCharCode(c).toLowerCase();
21224 this.cleanUpPaste.defer(100, this);
21232 e.preventDefault();
21240 fixKeys : function(){ // load time branching for fastest keydown performance
21242 return function(e){
21243 var k = e.getKey(), r;
21246 r = this.doc.selection.createRange();
21249 r.pasteHTML('    ');
21256 r = this.doc.selection.createRange();
21258 var target = r.parentElement();
21259 if(!target || target.tagName.toLowerCase() != 'li'){
21261 r.pasteHTML('<br />');
21267 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21268 this.cleanUpPaste.defer(100, this);
21274 }else if(Roo.isOpera){
21275 return function(e){
21276 var k = e.getKey();
21280 this.execCmd('InsertHTML','    ');
21283 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21284 this.cleanUpPaste.defer(100, this);
21289 }else if(Roo.isSafari){
21290 return function(e){
21291 var k = e.getKey();
21295 this.execCmd('InsertText','\t');
21299 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21300 this.cleanUpPaste.defer(100, this);
21308 getAllAncestors: function()
21310 var p = this.getSelectedNode();
21313 a.push(p); // push blank onto stack..
21314 p = this.getParentElement();
21318 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21322 a.push(this.doc.body);
21326 lastSelNode : false,
21329 getSelection : function()
21331 this.assignDocWin();
21332 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21335 getSelectedNode: function()
21337 // this may only work on Gecko!!!
21339 // should we cache this!!!!
21344 var range = this.createRange(this.getSelection()).cloneRange();
21347 var parent = range.parentElement();
21349 var testRange = range.duplicate();
21350 testRange.moveToElementText(parent);
21351 if (testRange.inRange(range)) {
21354 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21357 parent = parent.parentElement;
21362 // is ancestor a text element.
21363 var ac = range.commonAncestorContainer;
21364 if (ac.nodeType == 3) {
21365 ac = ac.parentNode;
21368 var ar = ac.childNodes;
21371 var other_nodes = [];
21372 var has_other_nodes = false;
21373 for (var i=0;i<ar.length;i++) {
21374 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21377 // fullly contained node.
21379 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21384 // probably selected..
21385 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21386 other_nodes.push(ar[i]);
21390 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21395 has_other_nodes = true;
21397 if (!nodes.length && other_nodes.length) {
21398 nodes= other_nodes;
21400 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21406 createRange: function(sel)
21408 // this has strange effects when using with
21409 // top toolbar - not sure if it's a great idea.
21410 //this.editor.contentWindow.focus();
21411 if (typeof sel != "undefined") {
21413 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21415 return this.doc.createRange();
21418 return this.doc.createRange();
21421 getParentElement: function()
21424 this.assignDocWin();
21425 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21427 var range = this.createRange(sel);
21430 var p = range.commonAncestorContainer;
21431 while (p.nodeType == 3) { // text node
21442 * Range intersection.. the hard stuff...
21446 * [ -- selected range --- ]
21450 * if end is before start or hits it. fail.
21451 * if start is after end or hits it fail.
21453 * if either hits (but other is outside. - then it's not
21459 // @see http://www.thismuchiknow.co.uk/?p=64.
21460 rangeIntersectsNode : function(range, node)
21462 var nodeRange = node.ownerDocument.createRange();
21464 nodeRange.selectNode(node);
21466 nodeRange.selectNodeContents(node);
21469 var rangeStartRange = range.cloneRange();
21470 rangeStartRange.collapse(true);
21472 var rangeEndRange = range.cloneRange();
21473 rangeEndRange.collapse(false);
21475 var nodeStartRange = nodeRange.cloneRange();
21476 nodeStartRange.collapse(true);
21478 var nodeEndRange = nodeRange.cloneRange();
21479 nodeEndRange.collapse(false);
21481 return rangeStartRange.compareBoundaryPoints(
21482 Range.START_TO_START, nodeEndRange) == -1 &&
21483 rangeEndRange.compareBoundaryPoints(
21484 Range.START_TO_START, nodeStartRange) == 1;
21488 rangeCompareNode : function(range, node)
21490 var nodeRange = node.ownerDocument.createRange();
21492 nodeRange.selectNode(node);
21494 nodeRange.selectNodeContents(node);
21498 range.collapse(true);
21500 nodeRange.collapse(true);
21502 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21503 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21505 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21507 var nodeIsBefore = ss == 1;
21508 var nodeIsAfter = ee == -1;
21510 if (nodeIsBefore && nodeIsAfter) {
21513 if (!nodeIsBefore && nodeIsAfter) {
21514 return 1; //right trailed.
21517 if (nodeIsBefore && !nodeIsAfter) {
21518 return 2; // left trailed.
21524 // private? - in a new class?
21525 cleanUpPaste : function()
21527 // cleans up the whole document..
21528 Roo.log('cleanuppaste');
21530 this.cleanUpChildren(this.doc.body);
21531 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21532 if (clean != this.doc.body.innerHTML) {
21533 this.doc.body.innerHTML = clean;
21538 cleanWordChars : function(input) {// change the chars to hex code
21539 var he = Roo.HtmlEditorCore;
21541 var output = input;
21542 Roo.each(he.swapCodes, function(sw) {
21543 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21545 output = output.replace(swapper, sw[1]);
21552 cleanUpChildren : function (n)
21554 if (!n.childNodes.length) {
21557 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21558 this.cleanUpChild(n.childNodes[i]);
21565 cleanUpChild : function (node)
21568 //console.log(node);
21569 if (node.nodeName == "#text") {
21570 // clean up silly Windows -- stuff?
21573 if (node.nodeName == "#comment") {
21574 if (!this.allowComments) {
21575 node.parentNode.removeChild(node);
21577 // clean up silly Windows -- stuff?
21580 var lcname = node.tagName.toLowerCase();
21581 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21582 // whitelist of tags..
21584 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21586 node.parentNode.removeChild(node);
21591 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21593 // spans with no attributes - just remove them..
21594 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
21595 remove_keep_children = true;
21598 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21599 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21601 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21602 // remove_keep_children = true;
21605 if (remove_keep_children) {
21606 this.cleanUpChildren(node);
21607 // inserts everything just before this node...
21608 while (node.childNodes.length) {
21609 var cn = node.childNodes[0];
21610 node.removeChild(cn);
21611 node.parentNode.insertBefore(cn, node);
21613 node.parentNode.removeChild(node);
21617 if (!node.attributes || !node.attributes.length) {
21622 this.cleanUpChildren(node);
21626 function cleanAttr(n,v)
21629 if (v.match(/^\./) || v.match(/^\//)) {
21632 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21635 if (v.match(/^#/)) {
21638 if (v.match(/^\{/)) { // allow template editing.
21641 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21642 node.removeAttribute(n);
21646 var cwhite = this.cwhite;
21647 var cblack = this.cblack;
21649 function cleanStyle(n,v)
21651 if (v.match(/expression/)) { //XSS?? should we even bother..
21652 node.removeAttribute(n);
21656 var parts = v.split(/;/);
21659 Roo.each(parts, function(p) {
21660 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21664 var l = p.split(':').shift().replace(/\s+/g,'');
21665 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21667 if ( cwhite.length && cblack.indexOf(l) > -1) {
21668 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21669 //node.removeAttribute(n);
21673 // only allow 'c whitelisted system attributes'
21674 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21675 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21676 //node.removeAttribute(n);
21686 if (clean.length) {
21687 node.setAttribute(n, clean.join(';'));
21689 node.removeAttribute(n);
21695 for (var i = node.attributes.length-1; i > -1 ; i--) {
21696 var a = node.attributes[i];
21699 if (a.name.toLowerCase().substr(0,2)=='on') {
21700 node.removeAttribute(a.name);
21703 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21704 node.removeAttribute(a.name);
21707 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21708 cleanAttr(a.name,a.value); // fixme..
21711 if (a.name == 'style') {
21712 cleanStyle(a.name,a.value);
21715 /// clean up MS crap..
21716 // tecnically this should be a list of valid class'es..
21719 if (a.name == 'class') {
21720 if (a.value.match(/^Mso/)) {
21721 node.removeAttribute('class');
21724 if (a.value.match(/^body$/)) {
21725 node.removeAttribute('class');
21736 this.cleanUpChildren(node);
21742 * Clean up MS wordisms...
21744 cleanWord : function(node)
21747 this.cleanWord(this.doc.body);
21752 node.nodeName == 'SPAN' &&
21753 !node.hasAttributes() &&
21754 node.childNodes.length == 1 &&
21755 node.firstChild.nodeName == "#text"
21757 var textNode = node.firstChild;
21758 node.removeChild(textNode);
21759 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
21760 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21762 node.parentNode.insertBefore(textNode, node);
21763 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
21764 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21766 node.parentNode.removeChild(node);
21769 if (node.nodeName == "#text") {
21770 // clean up silly Windows -- stuff?
21773 if (node.nodeName == "#comment") {
21774 node.parentNode.removeChild(node);
21775 // clean up silly Windows -- stuff?
21779 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21780 node.parentNode.removeChild(node);
21783 //Roo.log(node.tagName);
21784 // remove - but keep children..
21785 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21786 //Roo.log('-- removed');
21787 while (node.childNodes.length) {
21788 var cn = node.childNodes[0];
21789 node.removeChild(cn);
21790 node.parentNode.insertBefore(cn, node);
21791 // move node to parent - and clean it..
21792 this.cleanWord(cn);
21794 node.parentNode.removeChild(node);
21795 /// no need to iterate chidlren = it's got none..
21796 //this.iterateChildren(node, this.cleanWord);
21800 if (node.className.length) {
21802 var cn = node.className.split(/\W+/);
21804 Roo.each(cn, function(cls) {
21805 if (cls.match(/Mso[a-zA-Z]+/)) {
21810 node.className = cna.length ? cna.join(' ') : '';
21812 node.removeAttribute("class");
21816 if (node.hasAttribute("lang")) {
21817 node.removeAttribute("lang");
21820 if (node.hasAttribute("style")) {
21822 var styles = node.getAttribute("style").split(";");
21824 Roo.each(styles, function(s) {
21825 if (!s.match(/:/)) {
21828 var kv = s.split(":");
21829 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21832 // what ever is left... we allow.
21835 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21836 if (!nstyle.length) {
21837 node.removeAttribute('style');
21840 this.iterateChildren(node, this.cleanWord);
21846 * iterateChildren of a Node, calling fn each time, using this as the scole..
21847 * @param {DomNode} node node to iterate children of.
21848 * @param {Function} fn method of this class to call on each item.
21850 iterateChildren : function(node, fn)
21852 if (!node.childNodes.length) {
21855 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21856 fn.call(this, node.childNodes[i])
21862 * cleanTableWidths.
21864 * Quite often pasting from word etc.. results in tables with column and widths.
21865 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21868 cleanTableWidths : function(node)
21873 this.cleanTableWidths(this.doc.body);
21878 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21881 Roo.log(node.tagName);
21882 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21883 this.iterateChildren(node, this.cleanTableWidths);
21886 if (node.hasAttribute('width')) {
21887 node.removeAttribute('width');
21891 if (node.hasAttribute("style")) {
21894 var styles = node.getAttribute("style").split(";");
21896 Roo.each(styles, function(s) {
21897 if (!s.match(/:/)) {
21900 var kv = s.split(":");
21901 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21904 // what ever is left... we allow.
21907 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21908 if (!nstyle.length) {
21909 node.removeAttribute('style');
21913 this.iterateChildren(node, this.cleanTableWidths);
21921 domToHTML : function(currentElement, depth, nopadtext) {
21923 depth = depth || 0;
21924 nopadtext = nopadtext || false;
21926 if (!currentElement) {
21927 return this.domToHTML(this.doc.body);
21930 //Roo.log(currentElement);
21932 var allText = false;
21933 var nodeName = currentElement.nodeName;
21934 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21936 if (nodeName == '#text') {
21938 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21943 if (nodeName != 'BODY') {
21946 // Prints the node tagName, such as <A>, <IMG>, etc
21949 for(i = 0; i < currentElement.attributes.length;i++) {
21951 var aname = currentElement.attributes.item(i).name;
21952 if (!currentElement.attributes.item(i).value.length) {
21955 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21958 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21967 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21970 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21975 // Traverse the tree
21977 var currentElementChild = currentElement.childNodes.item(i);
21978 var allText = true;
21979 var innerHTML = '';
21981 while (currentElementChild) {
21982 // Formatting code (indent the tree so it looks nice on the screen)
21983 var nopad = nopadtext;
21984 if (lastnode == 'SPAN') {
21988 if (currentElementChild.nodeName == '#text') {
21989 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21990 toadd = nopadtext ? toadd : toadd.trim();
21991 if (!nopad && toadd.length > 80) {
21992 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21994 innerHTML += toadd;
21997 currentElementChild = currentElement.childNodes.item(i);
22003 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22005 // Recursively traverse the tree structure of the child node
22006 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22007 lastnode = currentElementChild.nodeName;
22009 currentElementChild=currentElement.childNodes.item(i);
22015 // The remaining code is mostly for formatting the tree
22016 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22021 ret+= "</"+tagName+">";
22027 applyBlacklists : function()
22029 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22030 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22034 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22035 if (b.indexOf(tag) > -1) {
22038 this.white.push(tag);
22042 Roo.each(w, function(tag) {
22043 if (b.indexOf(tag) > -1) {
22046 if (this.white.indexOf(tag) > -1) {
22049 this.white.push(tag);
22054 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22055 if (w.indexOf(tag) > -1) {
22058 this.black.push(tag);
22062 Roo.each(b, function(tag) {
22063 if (w.indexOf(tag) > -1) {
22066 if (this.black.indexOf(tag) > -1) {
22069 this.black.push(tag);
22074 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22075 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22079 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22080 if (b.indexOf(tag) > -1) {
22083 this.cwhite.push(tag);
22087 Roo.each(w, function(tag) {
22088 if (b.indexOf(tag) > -1) {
22091 if (this.cwhite.indexOf(tag) > -1) {
22094 this.cwhite.push(tag);
22099 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22100 if (w.indexOf(tag) > -1) {
22103 this.cblack.push(tag);
22107 Roo.each(b, function(tag) {
22108 if (w.indexOf(tag) > -1) {
22111 if (this.cblack.indexOf(tag) > -1) {
22114 this.cblack.push(tag);
22119 setStylesheets : function(stylesheets)
22121 if(typeof(stylesheets) == 'string'){
22122 Roo.get(this.iframe.contentDocument.head).createChild({
22124 rel : 'stylesheet',
22133 Roo.each(stylesheets, function(s) {
22138 Roo.get(_this.iframe.contentDocument.head).createChild({
22140 rel : 'stylesheet',
22149 removeStylesheets : function()
22153 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22158 setStyle : function(style)
22160 Roo.get(this.iframe.contentDocument.head).createChild({
22169 // hide stuff that is not compatible
22183 * @event specialkey
22187 * @cfg {String} fieldClass @hide
22190 * @cfg {String} focusClass @hide
22193 * @cfg {String} autoCreate @hide
22196 * @cfg {String} inputType @hide
22199 * @cfg {String} invalidClass @hide
22202 * @cfg {String} invalidText @hide
22205 * @cfg {String} msgFx @hide
22208 * @cfg {String} validateOnBlur @hide
22212 Roo.HtmlEditorCore.white = [
22213 'area', 'br', 'img', 'input', 'hr', 'wbr',
22215 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
22216 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
22217 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
22218 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
22219 'table', 'ul', 'xmp',
22221 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
22224 'dir', 'menu', 'ol', 'ul', 'dl',
22230 Roo.HtmlEditorCore.black = [
22231 // 'embed', 'object', // enable - backend responsiblity to clean thiese
22233 'base', 'basefont', 'bgsound', 'blink', 'body',
22234 'frame', 'frameset', 'head', 'html', 'ilayer',
22235 'iframe', 'layer', 'link', 'meta', 'object',
22236 'script', 'style' ,'title', 'xml' // clean later..
22238 Roo.HtmlEditorCore.clean = [
22239 'script', 'style', 'title', 'xml'
22241 Roo.HtmlEditorCore.remove = [
22246 Roo.HtmlEditorCore.ablack = [
22250 Roo.HtmlEditorCore.aclean = [
22251 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
22255 Roo.HtmlEditorCore.pwhite= [
22256 'http', 'https', 'mailto'
22259 // white listed style attributes.
22260 Roo.HtmlEditorCore.cwhite= [
22261 // 'text-align', /// default is to allow most things..
22267 // black listed style attributes.
22268 Roo.HtmlEditorCore.cblack= [
22269 // 'font-size' -- this can be set by the project
22273 Roo.HtmlEditorCore.swapCodes =[
22274 [ 8211, "–" ],
22275 [ 8212, "—" ],
22284 //<script type="text/javascript">
22287 * Ext JS Library 1.1.1
22288 * Copyright(c) 2006-2007, Ext JS, LLC.
22294 Roo.form.HtmlEditor = function(config){
22298 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22300 if (!this.toolbars) {
22301 this.toolbars = [];
22303 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22309 * @class Roo.form.HtmlEditor
22310 * @extends Roo.form.Field
22311 * Provides a lightweight HTML Editor component.
22313 * This has been tested on Fireforx / Chrome.. IE may not be so great..
22315 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22316 * supported by this editor.</b><br/><br/>
22317 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22318 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22320 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22322 * @cfg {Boolean} clearUp
22326 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22331 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22336 * @cfg {Number} height (in pixels)
22340 * @cfg {Number} width (in pixels)
22345 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22348 stylesheets: false,
22352 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22357 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22363 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22368 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22373 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
22375 allowComments: false,
22380 // private properties
22381 validationEvent : false,
22383 initialized : false,
22386 onFocus : Roo.emptyFn,
22388 hideMode:'offsets',
22390 actionMode : 'container', // defaults to hiding it...
22392 defaultAutoCreate : { // modified by initCompnoent..
22394 style:"width:500px;height:300px;",
22395 autocomplete: "new-password"
22399 initComponent : function(){
22402 * @event initialize
22403 * Fires when the editor is fully initialized (including the iframe)
22404 * @param {HtmlEditor} this
22409 * Fires when the editor is first receives the focus. Any insertion must wait
22410 * until after this event.
22411 * @param {HtmlEditor} this
22415 * @event beforesync
22416 * Fires before the textarea is updated with content from the editor iframe. Return false
22417 * to cancel the sync.
22418 * @param {HtmlEditor} this
22419 * @param {String} html
22423 * @event beforepush
22424 * Fires before the iframe editor is updated with content from the textarea. Return false
22425 * to cancel the push.
22426 * @param {HtmlEditor} this
22427 * @param {String} html
22432 * Fires when the textarea is updated with content from the editor iframe.
22433 * @param {HtmlEditor} this
22434 * @param {String} html
22439 * Fires when the iframe editor is updated with content from the textarea.
22440 * @param {HtmlEditor} this
22441 * @param {String} html
22445 * @event editmodechange
22446 * Fires when the editor switches edit modes
22447 * @param {HtmlEditor} this
22448 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22450 editmodechange: true,
22452 * @event editorevent
22453 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22454 * @param {HtmlEditor} this
22458 * @event firstfocus
22459 * Fires when on first focus - needed by toolbars..
22460 * @param {HtmlEditor} this
22465 * Auto save the htmlEditor value as a file into Events
22466 * @param {HtmlEditor} this
22470 * @event savedpreview
22471 * preview the saved version of htmlEditor
22472 * @param {HtmlEditor} this
22474 savedpreview: true,
22477 * @event stylesheetsclick
22478 * Fires when press the Sytlesheets button
22479 * @param {Roo.HtmlEditorCore} this
22481 stylesheetsclick: true
22483 this.defaultAutoCreate = {
22485 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22486 autocomplete: "new-password"
22491 * Protected method that will not generally be called directly. It
22492 * is called when the editor creates its toolbar. Override this method if you need to
22493 * add custom toolbar buttons.
22494 * @param {HtmlEditor} editor
22496 createToolbar : function(editor){
22497 Roo.log("create toolbars");
22498 if (!editor.toolbars || !editor.toolbars.length) {
22499 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22502 for (var i =0 ; i < editor.toolbars.length;i++) {
22503 editor.toolbars[i] = Roo.factory(
22504 typeof(editor.toolbars[i]) == 'string' ?
22505 { xtype: editor.toolbars[i]} : editor.toolbars[i],
22506 Roo.form.HtmlEditor);
22507 editor.toolbars[i].init(editor);
22515 onRender : function(ct, position)
22518 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22520 this.wrap = this.el.wrap({
22521 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22524 this.editorcore.onRender(ct, position);
22526 if (this.resizable) {
22527 this.resizeEl = new Roo.Resizable(this.wrap, {
22531 minHeight : this.height,
22532 height: this.height,
22533 handles : this.resizable,
22536 resize : function(r, w, h) {
22537 _t.onResize(w,h); // -something
22543 this.createToolbar(this);
22547 this.setSize(this.wrap.getSize());
22549 if (this.resizeEl) {
22550 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22551 // should trigger onReize..
22554 this.keyNav = new Roo.KeyNav(this.el, {
22556 "tab" : function(e){
22557 e.preventDefault();
22559 var value = this.getValue();
22561 var start = this.el.dom.selectionStart;
22562 var end = this.el.dom.selectionEnd;
22566 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22567 this.el.dom.setSelectionRange(end + 1, end + 1);
22571 var f = value.substring(0, start).split("\t");
22573 if(f.pop().length != 0){
22577 this.setValue(f.join("\t") + value.substring(end));
22578 this.el.dom.setSelectionRange(start - 1, start - 1);
22582 "home" : function(e){
22583 e.preventDefault();
22585 var curr = this.el.dom.selectionStart;
22586 var lines = this.getValue().split("\n");
22593 this.el.dom.setSelectionRange(0, 0);
22599 for (var i = 0; i < lines.length;i++) {
22600 pos += lines[i].length;
22610 pos -= lines[i].length;
22616 this.el.dom.setSelectionRange(pos, pos);
22620 this.el.dom.selectionStart = pos;
22621 this.el.dom.selectionEnd = curr;
22624 "end" : function(e){
22625 e.preventDefault();
22627 var curr = this.el.dom.selectionStart;
22628 var lines = this.getValue().split("\n");
22635 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22641 for (var i = 0; i < lines.length;i++) {
22643 pos += lines[i].length;
22657 this.el.dom.setSelectionRange(pos, pos);
22661 this.el.dom.selectionStart = curr;
22662 this.el.dom.selectionEnd = pos;
22667 doRelay : function(foo, bar, hname){
22668 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22674 // if(this.autosave && this.w){
22675 // this.autoSaveFn = setInterval(this.autosave, 1000);
22680 onResize : function(w, h)
22682 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22687 if(typeof w == 'number'){
22688 var aw = w - this.wrap.getFrameWidth('lr');
22689 this.el.setWidth(this.adjustWidth('textarea', aw));
22692 if(typeof h == 'number'){
22694 for (var i =0; i < this.toolbars.length;i++) {
22695 // fixme - ask toolbars for heights?
22696 tbh += this.toolbars[i].tb.el.getHeight();
22697 if (this.toolbars[i].footer) {
22698 tbh += this.toolbars[i].footer.el.getHeight();
22705 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22706 ah -= 5; // knock a few pixes off for look..
22708 this.el.setHeight(this.adjustWidth('textarea', ah));
22712 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22713 this.editorcore.onResize(ew,eh);
22718 * Toggles the editor between standard and source edit mode.
22719 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22721 toggleSourceEdit : function(sourceEditMode)
22723 this.editorcore.toggleSourceEdit(sourceEditMode);
22725 if(this.editorcore.sourceEditMode){
22726 Roo.log('editor - showing textarea');
22729 // Roo.log(this.syncValue());
22730 this.editorcore.syncValue();
22731 this.el.removeClass('x-hidden');
22732 this.el.dom.removeAttribute('tabIndex');
22735 for (var i = 0; i < this.toolbars.length; i++) {
22736 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22737 this.toolbars[i].tb.hide();
22738 this.toolbars[i].footer.hide();
22743 Roo.log('editor - hiding textarea');
22745 // Roo.log(this.pushValue());
22746 this.editorcore.pushValue();
22748 this.el.addClass('x-hidden');
22749 this.el.dom.setAttribute('tabIndex', -1);
22751 for (var i = 0; i < this.toolbars.length; i++) {
22752 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22753 this.toolbars[i].tb.show();
22754 this.toolbars[i].footer.show();
22758 //this.deferFocus();
22761 this.setSize(this.wrap.getSize());
22762 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22764 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22767 // private (for BoxComponent)
22768 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22770 // private (for BoxComponent)
22771 getResizeEl : function(){
22775 // private (for BoxComponent)
22776 getPositionEl : function(){
22781 initEvents : function(){
22782 this.originalValue = this.getValue();
22786 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22789 markInvalid : Roo.emptyFn,
22791 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22794 clearInvalid : Roo.emptyFn,
22796 setValue : function(v){
22797 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22798 this.editorcore.pushValue();
22803 deferFocus : function(){
22804 this.focus.defer(10, this);
22808 focus : function(){
22809 this.editorcore.focus();
22815 onDestroy : function(){
22821 for (var i =0; i < this.toolbars.length;i++) {
22822 // fixme - ask toolbars for heights?
22823 this.toolbars[i].onDestroy();
22826 this.wrap.dom.innerHTML = '';
22827 this.wrap.remove();
22832 onFirstFocus : function(){
22833 //Roo.log("onFirstFocus");
22834 this.editorcore.onFirstFocus();
22835 for (var i =0; i < this.toolbars.length;i++) {
22836 this.toolbars[i].onFirstFocus();
22842 syncValue : function()
22844 this.editorcore.syncValue();
22847 pushValue : function()
22849 this.editorcore.pushValue();
22852 setStylesheets : function(stylesheets)
22854 this.editorcore.setStylesheets(stylesheets);
22857 removeStylesheets : function()
22859 this.editorcore.removeStylesheets();
22863 // hide stuff that is not compatible
22877 * @event specialkey
22881 * @cfg {String} fieldClass @hide
22884 * @cfg {String} focusClass @hide
22887 * @cfg {String} autoCreate @hide
22890 * @cfg {String} inputType @hide
22893 * @cfg {String} invalidClass @hide
22896 * @cfg {String} invalidText @hide
22899 * @cfg {String} msgFx @hide
22902 * @cfg {String} validateOnBlur @hide
22906 // <script type="text/javascript">
22909 * Ext JS Library 1.1.1
22910 * Copyright(c) 2006-2007, Ext JS, LLC.
22916 * @class Roo.form.HtmlEditorToolbar1
22921 new Roo.form.HtmlEditor({
22924 new Roo.form.HtmlEditorToolbar1({
22925 disable : { fonts: 1 , format: 1, ..., ... , ...],
22931 * @cfg {Object} disable List of elements to disable..
22932 * @cfg {Array} btns List of additional buttons.
22936 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22939 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22942 Roo.apply(this, config);
22944 // default disabled, based on 'good practice'..
22945 this.disable = this.disable || {};
22946 Roo.applyIf(this.disable, {
22949 specialElements : true
22953 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22954 // dont call parent... till later.
22957 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype, {
22964 editorcore : false,
22966 * @cfg {Object} disable List of toolbar elements to disable
22973 * @cfg {String} createLinkText The default text for the create link prompt
22975 createLinkText : 'Please enter the URL for the link:',
22977 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22979 defaultLinkValue : 'http:/'+'/',
22983 * @cfg {Array} fontFamilies An array of available font families
23001 // "á" , ?? a acute?
23006 "°" // , // degrees
23008 // "é" , // e ecute
23009 // "ú" , // u ecute?
23012 specialElements : [
23014 text: "Insert Table",
23017 ihtml : '<table><tr><td>Cell</td></tr></table>'
23021 text: "Insert Image",
23024 ihtml : '<img src="about:blank"/>'
23033 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
23034 "input:submit", "input:button", "select", "textarea", "label" ],
23037 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
23039 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23048 * @cfg {String} defaultFont default font to use.
23050 defaultFont: 'tahoma',
23052 fontSelect : false,
23055 formatCombo : false,
23057 init : function(editor)
23059 this.editor = editor;
23060 this.editorcore = editor.editorcore ? editor.editorcore : editor;
23061 var editorcore = this.editorcore;
23065 var fid = editorcore.frameId;
23067 function btn(id, toggle, handler){
23068 var xid = fid + '-'+ id ;
23072 cls : 'x-btn-icon x-edit-'+id,
23073 enableToggle:toggle !== false,
23074 scope: _t, // was editor...
23075 handler:handler||_t.relayBtnCmd,
23076 clickEvent:'mousedown',
23077 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23084 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23086 // stop form submits
23087 tb.el.on('click', function(e){
23088 e.preventDefault(); // what does this do?
23091 if(!this.disable.font) { // && !Roo.isSafari){
23092 /* why no safari for fonts
23093 editor.fontSelect = tb.el.createChild({
23096 cls:'x-font-select',
23097 html: this.createFontOptions()
23100 editor.fontSelect.on('change', function(){
23101 var font = editor.fontSelect.dom.value;
23102 editor.relayCmd('fontname', font);
23103 editor.deferFocus();
23107 editor.fontSelect.dom,
23113 if(!this.disable.formats){
23114 this.formatCombo = new Roo.form.ComboBox({
23115 store: new Roo.data.SimpleStore({
23118 data : this.formats // from states.js
23122 //autoCreate : {tag: "div", size: "20"},
23123 displayField:'tag',
23127 triggerAction: 'all',
23128 emptyText:'Add tag',
23129 selectOnFocus:true,
23132 'select': function(c, r, i) {
23133 editorcore.insertTag(r.get('tag'));
23139 tb.addField(this.formatCombo);
23143 if(!this.disable.format){
23148 btn('strikethrough')
23151 if(!this.disable.fontSize){
23156 btn('increasefontsize', false, editorcore.adjustFont),
23157 btn('decreasefontsize', false, editorcore.adjustFont)
23162 if(!this.disable.colors){
23165 id:editorcore.frameId +'-forecolor',
23166 cls:'x-btn-icon x-edit-forecolor',
23167 clickEvent:'mousedown',
23168 tooltip: this.buttonTips['forecolor'] || undefined,
23170 menu : new Roo.menu.ColorMenu({
23171 allowReselect: true,
23172 focus: Roo.emptyFn,
23175 selectHandler: function(cp, color){
23176 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23177 editor.deferFocus();
23180 clickEvent:'mousedown'
23183 id:editorcore.frameId +'backcolor',
23184 cls:'x-btn-icon x-edit-backcolor',
23185 clickEvent:'mousedown',
23186 tooltip: this.buttonTips['backcolor'] || undefined,
23188 menu : new Roo.menu.ColorMenu({
23189 focus: Roo.emptyFn,
23192 allowReselect: true,
23193 selectHandler: function(cp, color){
23195 editorcore.execCmd('useCSS', false);
23196 editorcore.execCmd('hilitecolor', color);
23197 editorcore.execCmd('useCSS', true);
23198 editor.deferFocus();
23200 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
23201 Roo.isSafari || Roo.isIE ? '#'+color : color);
23202 editor.deferFocus();
23206 clickEvent:'mousedown'
23211 // now add all the items...
23214 if(!this.disable.alignments){
23217 btn('justifyleft'),
23218 btn('justifycenter'),
23219 btn('justifyright')
23223 //if(!Roo.isSafari){
23224 if(!this.disable.links){
23227 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
23231 if(!this.disable.lists){
23234 btn('insertorderedlist'),
23235 btn('insertunorderedlist')
23238 if(!this.disable.sourceEdit){
23241 btn('sourceedit', true, function(btn){
23242 this.toggleSourceEdit(btn.pressed);
23249 // special menu.. - needs to be tidied up..
23250 if (!this.disable.special) {
23253 cls: 'x-edit-none',
23259 for (var i =0; i < this.specialChars.length; i++) {
23260 smenu.menu.items.push({
23262 html: this.specialChars[i],
23263 handler: function(a,b) {
23264 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23265 //editor.insertAtCursor(a.html);
23279 if (!this.disable.cleanStyles) {
23281 cls: 'x-btn-icon x-btn-clear',
23287 for (var i =0; i < this.cleanStyles.length; i++) {
23288 cmenu.menu.items.push({
23289 actiontype : this.cleanStyles[i],
23290 html: 'Remove ' + this.cleanStyles[i],
23291 handler: function(a,b) {
23294 var c = Roo.get(editorcore.doc.body);
23295 c.select('[style]').each(function(s) {
23296 s.dom.style.removeProperty(a.actiontype);
23298 editorcore.syncValue();
23303 cmenu.menu.items.push({
23304 actiontype : 'tablewidths',
23305 html: 'Remove Table Widths',
23306 handler: function(a,b) {
23307 editorcore.cleanTableWidths();
23308 editorcore.syncValue();
23312 cmenu.menu.items.push({
23313 actiontype : 'word',
23314 html: 'Remove MS Word Formating',
23315 handler: function(a,b) {
23316 editorcore.cleanWord();
23317 editorcore.syncValue();
23322 cmenu.menu.items.push({
23323 actiontype : 'all',
23324 html: 'Remove All Styles',
23325 handler: function(a,b) {
23327 var c = Roo.get(editorcore.doc.body);
23328 c.select('[style]').each(function(s) {
23329 s.dom.removeAttribute('style');
23331 editorcore.syncValue();
23336 cmenu.menu.items.push({
23337 actiontype : 'all',
23338 html: 'Remove All CSS Classes',
23339 handler: function(a,b) {
23341 var c = Roo.get(editorcore.doc.body);
23342 c.select('[class]').each(function(s) {
23343 s.dom.removeAttribute('class');
23345 editorcore.cleanWord();
23346 editorcore.syncValue();
23351 cmenu.menu.items.push({
23352 actiontype : 'tidy',
23353 html: 'Tidy HTML Source',
23354 handler: function(a,b) {
23355 editorcore.doc.body.innerHTML = editorcore.domToHTML();
23356 editorcore.syncValue();
23365 if (!this.disable.specialElements) {
23368 cls: 'x-edit-none',
23373 for (var i =0; i < this.specialElements.length; i++) {
23374 semenu.menu.items.push(
23376 handler: function(a,b) {
23377 editor.insertAtCursor(this.ihtml);
23379 }, this.specialElements[i])
23391 for(var i =0; i< this.btns.length;i++) {
23392 var b = Roo.factory(this.btns[i],Roo.form);
23393 b.cls = 'x-edit-none';
23395 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23396 b.cls += ' x-init-enable';
23399 b.scope = editorcore;
23407 // disable everything...
23409 this.tb.items.each(function(item){
23412 item.id != editorcore.frameId+ '-sourceedit' &&
23413 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23419 this.rendered = true;
23421 // the all the btns;
23422 editor.on('editorevent', this.updateToolbar, this);
23423 // other toolbars need to implement this..
23424 //editor.on('editmodechange', this.updateToolbar, this);
23428 relayBtnCmd : function(btn) {
23429 this.editorcore.relayCmd(btn.cmd);
23431 // private used internally
23432 createLink : function(){
23433 Roo.log("create link?");
23434 var url = prompt(this.createLinkText, this.defaultLinkValue);
23435 if(url && url != 'http:/'+'/'){
23436 this.editorcore.relayCmd('createlink', url);
23442 * Protected method that will not generally be called directly. It triggers
23443 * a toolbar update by reading the markup state of the current selection in the editor.
23445 updateToolbar: function(){
23447 if(!this.editorcore.activated){
23448 this.editor.onFirstFocus();
23452 var btns = this.tb.items.map,
23453 doc = this.editorcore.doc,
23454 frameId = this.editorcore.frameId;
23456 if(!this.disable.font && !Roo.isSafari){
23458 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23459 if(name != this.fontSelect.dom.value){
23460 this.fontSelect.dom.value = name;
23464 if(!this.disable.format){
23465 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23466 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23467 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23468 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23470 if(!this.disable.alignments){
23471 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23472 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23473 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23475 if(!Roo.isSafari && !this.disable.lists){
23476 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23477 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23480 var ans = this.editorcore.getAllAncestors();
23481 if (this.formatCombo) {
23484 var store = this.formatCombo.store;
23485 this.formatCombo.setValue("");
23486 for (var i =0; i < ans.length;i++) {
23487 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23489 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23497 // hides menus... - so this cant be on a menu...
23498 Roo.menu.MenuMgr.hideAll();
23500 //this.editorsyncValue();
23504 createFontOptions : function(){
23505 var buf = [], fs = this.fontFamilies, ff, lc;
23509 for(var i = 0, len = fs.length; i< len; i++){
23511 lc = ff.toLowerCase();
23513 '<option value="',lc,'" style="font-family:',ff,';"',
23514 (this.defaultFont == lc ? ' selected="true">' : '>'),
23519 return buf.join('');
23522 toggleSourceEdit : function(sourceEditMode){
23524 Roo.log("toolbar toogle");
23525 if(sourceEditMode === undefined){
23526 sourceEditMode = !this.sourceEditMode;
23528 this.sourceEditMode = sourceEditMode === true;
23529 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23530 // just toggle the button?
23531 if(btn.pressed !== this.sourceEditMode){
23532 btn.toggle(this.sourceEditMode);
23536 if(sourceEditMode){
23537 Roo.log("disabling buttons");
23538 this.tb.items.each(function(item){
23539 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23545 Roo.log("enabling buttons");
23546 if(this.editorcore.initialized){
23547 this.tb.items.each(function(item){
23553 Roo.log("calling toggole on editor");
23554 // tell the editor that it's been pressed..
23555 this.editor.toggleSourceEdit(sourceEditMode);
23559 * Object collection of toolbar tooltips for the buttons in the editor. The key
23560 * is the command id associated with that button and the value is a valid QuickTips object.
23565 title: 'Bold (Ctrl+B)',
23566 text: 'Make the selected text bold.',
23567 cls: 'x-html-editor-tip'
23570 title: 'Italic (Ctrl+I)',
23571 text: 'Make the selected text italic.',
23572 cls: 'x-html-editor-tip'
23580 title: 'Bold (Ctrl+B)',
23581 text: 'Make the selected text bold.',
23582 cls: 'x-html-editor-tip'
23585 title: 'Italic (Ctrl+I)',
23586 text: 'Make the selected text italic.',
23587 cls: 'x-html-editor-tip'
23590 title: 'Underline (Ctrl+U)',
23591 text: 'Underline the selected text.',
23592 cls: 'x-html-editor-tip'
23595 title: 'Strikethrough',
23596 text: 'Strikethrough the selected text.',
23597 cls: 'x-html-editor-tip'
23599 increasefontsize : {
23600 title: 'Grow Text',
23601 text: 'Increase the font size.',
23602 cls: 'x-html-editor-tip'
23604 decreasefontsize : {
23605 title: 'Shrink Text',
23606 text: 'Decrease the font size.',
23607 cls: 'x-html-editor-tip'
23610 title: 'Text Highlight Color',
23611 text: 'Change the background color of the selected text.',
23612 cls: 'x-html-editor-tip'
23615 title: 'Font Color',
23616 text: 'Change the color of the selected text.',
23617 cls: 'x-html-editor-tip'
23620 title: 'Align Text Left',
23621 text: 'Align text to the left.',
23622 cls: 'x-html-editor-tip'
23625 title: 'Center Text',
23626 text: 'Center text in the editor.',
23627 cls: 'x-html-editor-tip'
23630 title: 'Align Text Right',
23631 text: 'Align text to the right.',
23632 cls: 'x-html-editor-tip'
23634 insertunorderedlist : {
23635 title: 'Bullet List',
23636 text: 'Start a bulleted list.',
23637 cls: 'x-html-editor-tip'
23639 insertorderedlist : {
23640 title: 'Numbered List',
23641 text: 'Start a numbered list.',
23642 cls: 'x-html-editor-tip'
23645 title: 'Hyperlink',
23646 text: 'Make the selected text a hyperlink.',
23647 cls: 'x-html-editor-tip'
23650 title: 'Source Edit',
23651 text: 'Switch to source editing mode.',
23652 cls: 'x-html-editor-tip'
23656 onDestroy : function(){
23659 this.tb.items.each(function(item){
23661 item.menu.removeAll();
23663 item.menu.el.destroy();
23671 onFirstFocus: function() {
23672 this.tb.items.each(function(item){
23681 // <script type="text/javascript">
23684 * Ext JS Library 1.1.1
23685 * Copyright(c) 2006-2007, Ext JS, LLC.
23692 * @class Roo.form.HtmlEditor.ToolbarContext
23697 new Roo.form.HtmlEditor({
23700 { xtype: 'ToolbarStandard', styles : {} }
23701 { xtype: 'ToolbarContext', disable : {} }
23707 * @config : {Object} disable List of elements to disable.. (not done yet.)
23708 * @config : {Object} styles Map of styles available.
23712 Roo.form.HtmlEditor.ToolbarContext = function(config)
23715 Roo.apply(this, config);
23716 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23717 // dont call parent... till later.
23718 this.styles = this.styles || {};
23723 Roo.form.HtmlEditor.ToolbarContext.types = {
23735 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23801 opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23806 opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23816 style : 'fontFamily',
23817 displayField: 'display',
23818 optname : 'font-family',
23867 // should we really allow this??
23868 // should this just be
23879 style : 'fontFamily',
23880 displayField: 'display',
23881 optname : 'font-family',
23888 style : 'fontFamily',
23889 displayField: 'display',
23890 optname : 'font-family',
23897 style : 'fontFamily',
23898 displayField: 'display',
23899 optname : 'font-family',
23910 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23911 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23913 Roo.form.HtmlEditor.ToolbarContext.options = {
23915 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23916 [ 'Courier New', 'Courier New'],
23917 [ 'Tahoma', 'Tahoma'],
23918 [ 'Times New Roman,serif', 'Times'],
23919 [ 'Verdana','Verdana' ]
23923 // fixme - these need to be configurable..
23926 //Roo.form.HtmlEditor.ToolbarContext.types
23929 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
23936 editorcore : false,
23938 * @cfg {Object} disable List of toolbar elements to disable
23943 * @cfg {Object} styles List of styles
23944 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
23946 * These must be defined in the page, so they get rendered correctly..
23957 init : function(editor)
23959 this.editor = editor;
23960 this.editorcore = editor.editorcore ? editor.editorcore : editor;
23961 var editorcore = this.editorcore;
23963 var fid = editorcore.frameId;
23965 function btn(id, toggle, handler){
23966 var xid = fid + '-'+ id ;
23970 cls : 'x-btn-icon x-edit-'+id,
23971 enableToggle:toggle !== false,
23972 scope: editorcore, // was editor...
23973 handler:handler||editorcore.relayBtnCmd,
23974 clickEvent:'mousedown',
23975 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23979 // create a new element.
23980 var wdiv = editor.wrap.createChild({
23982 }, editor.wrap.dom.firstChild.nextSibling, true);
23984 // can we do this more than once??
23986 // stop form submits
23989 // disable everything...
23990 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23991 this.toolbars = {};
23993 for (var i in ty) {
23995 this.toolbars[i] = this.buildToolbar(ty[i],i);
23997 this.tb = this.toolbars.BODY;
23999 this.buildFooter();
24000 this.footer.show();
24001 editor.on('hide', function( ) { this.footer.hide() }, this);
24002 editor.on('show', function( ) { this.footer.show() }, this);
24005 this.rendered = true;
24007 // the all the btns;
24008 editor.on('editorevent', this.updateToolbar, this);
24009 // other toolbars need to implement this..
24010 //editor.on('editmodechange', this.updateToolbar, this);
24016 * Protected method that will not generally be called directly. It triggers
24017 * a toolbar update by reading the markup state of the current selection in the editor.
24019 * Note you can force an update by calling on('editorevent', scope, false)
24021 updateToolbar: function(editor,ev,sel){
24024 // capture mouse up - this is handy for selecting images..
24025 // perhaps should go somewhere else...
24026 if(!this.editorcore.activated){
24027 this.editor.onFirstFocus();
24033 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24034 // selectNode - might want to handle IE?
24036 (ev.type == 'mouseup' || ev.type == 'click' ) &&
24037 ev.target && ev.target.tagName == 'IMG') {
24038 // they have click on an image...
24039 // let's see if we can change the selection...
24042 var nodeRange = sel.ownerDocument.createRange();
24044 nodeRange.selectNode(sel);
24046 nodeRange.selectNodeContents(sel);
24048 //nodeRange.collapse(true);
24049 var s = this.editorcore.win.getSelection();
24050 s.removeAllRanges();
24051 s.addRange(nodeRange);
24055 var updateFooter = sel ? false : true;
24058 var ans = this.editorcore.getAllAncestors();
24061 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24064 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
24065 sel = sel ? sel : this.editorcore.doc.body;
24066 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24069 // pick a menu that exists..
24070 var tn = sel.tagName.toUpperCase();
24071 //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24073 tn = sel.tagName.toUpperCase();
24075 var lastSel = this.tb.selectedNode;
24077 this.tb.selectedNode = sel;
24079 // if current menu does not match..
24081 if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24084 ///console.log("show: " + tn);
24085 this.tb = typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24088 this.tb.items.first().el.innerHTML = tn + ': ';
24091 // update attributes
24092 if (this.tb.fields) {
24093 this.tb.fields.each(function(e) {
24095 e.setValue(sel.style[e.stylename]);
24098 e.setValue(sel.getAttribute(e.attrname));
24102 var hasStyles = false;
24103 for(var i in this.styles) {
24110 var st = this.tb.fields.item(0);
24112 st.store.removeAll();
24115 var cn = sel.className.split(/\s+/);
24118 if (this.styles['*']) {
24120 Roo.each(this.styles['*'], function(v) {
24121 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
24124 if (this.styles[tn]) {
24125 Roo.each(this.styles[tn], function(v) {
24126 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
24130 st.store.loadData(avs);
24134 // flag our selected Node.
24135 this.tb.selectedNode = sel;
24138 Roo.menu.MenuMgr.hideAll();
24142 if (!updateFooter) {
24143 //this.footDisp.dom.innerHTML = '';
24146 // update the footer
24150 this.footerEls = ans.reverse();
24151 Roo.each(this.footerEls, function(a,i) {
24152 if (!a) { return; }
24153 html += html.length ? ' > ' : '';
24155 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24160 var sz = this.footDisp.up('td').getSize();
24161 this.footDisp.dom.style.width = (sz.width -10) + 'px';
24162 this.footDisp.dom.style.marginLeft = '5px';
24164 this.footDisp.dom.style.overflow = 'hidden';
24166 this.footDisp.dom.innerHTML = html;
24168 //this.editorsyncValue();
24175 onDestroy : function(){
24178 this.tb.items.each(function(item){
24180 item.menu.removeAll();
24182 item.menu.el.destroy();
24190 onFirstFocus: function() {
24191 // need to do this for all the toolbars..
24192 this.tb.items.each(function(item){
24196 buildToolbar: function(tlist, nm)
24198 var editor = this.editor;
24199 var editorcore = this.editorcore;
24200 // create a new element.
24201 var wdiv = editor.wrap.createChild({
24203 }, editor.wrap.dom.firstChild.nextSibling, true);
24206 var tb = new Roo.Toolbar(wdiv);
24209 tb.add(nm+ ": ");
24212 for(var i in this.styles) {
24217 if (styles && styles.length) {
24219 // this needs a multi-select checkbox...
24220 tb.addField( new Roo.form.ComboBox({
24221 store: new Roo.data.SimpleStore({
24223 fields: ['val', 'selected'],
24226 name : '-roo-edit-className',
24227 attrname : 'className',
24228 displayField: 'val',
24232 triggerAction: 'all',
24233 emptyText:'Select Style',
24234 selectOnFocus:true,
24237 'select': function(c, r, i) {
24238 // initial support only for on class per el..
24239 tb.selectedNode.className = r ? r.get('val') : '';
24240 editorcore.syncValue();
24247 var tbc = Roo.form.HtmlEditor.ToolbarContext;
24248 var tbops = tbc.options;
24250 for (var i in tlist) {
24252 var item = tlist[i];
24253 tb.add(item.title + ": ");
24256 //optname == used so you can configure the options available..
24257 var opts = item.opts ? item.opts : false;
24258 if (item.optname) {
24259 opts = tbops[item.optname];
24264 // opts == pulldown..
24265 tb.addField( new Roo.form.ComboBox({
24266 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24268 fields: ['val', 'display'],
24271 name : '-roo-edit-' + i,
24273 stylename : item.style ? item.style : false,
24274 displayField: item.displayField ? item.displayField : 'val',
24275 valueField : 'val',
24277 mode: typeof(tbc.stores[i]) != 'undefined' ? 'remote' : 'local',
24279 triggerAction: 'all',
24280 emptyText:'Select',
24281 selectOnFocus:true,
24282 width: item.width ? item.width : 130,
24284 'select': function(c, r, i) {
24286 tb.selectedNode.style[c.stylename] = r.get('val');
24289 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24298 tb.addField( new Roo.form.TextField({
24301 //allowBlank:false,
24306 tb.addField( new Roo.form.TextField({
24307 name: '-roo-edit-' + i,
24314 'change' : function(f, nv, ov) {
24315 tb.selectedNode.setAttribute(f.attrname, nv);
24316 editorcore.syncValue();
24329 text: 'Stylesheets',
24332 click : function ()
24334 _this.editor.fireEvent('stylesheetsclick', _this.editor);
24342 text: 'Remove Tag',
24345 click : function ()
24348 // undo does not work.
24350 var sn = tb.selectedNode;
24352 var pn = sn.parentNode;
24354 var stn = sn.childNodes[0];
24355 var en = sn.childNodes[sn.childNodes.length - 1 ];
24356 while (sn.childNodes.length) {
24357 var node = sn.childNodes[0];
24358 sn.removeChild(node);
24360 pn.insertBefore(node, sn);
24363 pn.removeChild(sn);
24364 var range = editorcore.createRange();
24366 range.setStart(stn,0);
24367 range.setEnd(en,0); //????
24368 //range.selectNode(sel);
24371 var selection = editorcore.getSelection();
24372 selection.removeAllRanges();
24373 selection.addRange(range);
24377 //_this.updateToolbar(null, null, pn);
24378 _this.updateToolbar(null, null, null);
24379 _this.footDisp.dom.innerHTML = '';
24389 tb.el.on('click', function(e){
24390 e.preventDefault(); // what does this do?
24392 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24395 // dont need to disable them... as they will get hidden
24400 buildFooter : function()
24403 var fel = this.editor.wrap.createChild();
24404 this.footer = new Roo.Toolbar(fel);
24405 // toolbar has scrolly on left / right?
24406 var footDisp= new Roo.Toolbar.Fill();
24412 handler : function() {
24413 _t.footDisp.scrollTo('left',0,true)
24417 this.footer.add( footDisp );
24422 handler : function() {
24424 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24428 var fel = Roo.get(footDisp.el);
24429 fel.addClass('x-editor-context');
24430 this.footDispWrap = fel;
24431 this.footDispWrap.overflow = 'hidden';
24433 this.footDisp = fel.createChild();
24434 this.footDispWrap.on('click', this.onContextClick, this)
24438 onContextClick : function (ev,dom)
24440 ev.preventDefault();
24441 var cn = dom.className;
24443 if (!cn.match(/x-ed-loc-/)) {
24446 var n = cn.split('-').pop();
24447 var ans = this.footerEls;
24451 var range = this.editorcore.createRange();
24453 range.selectNodeContents(sel);
24454 //range.selectNode(sel);
24457 var selection = this.editorcore.getSelection();
24458 selection.removeAllRanges();
24459 selection.addRange(range);
24463 this.updateToolbar(null, null, sel);
24480 * Ext JS Library 1.1.1
24481 * Copyright(c) 2006-2007, Ext JS, LLC.
24483 * Originally Released Under LGPL - original licence link has changed is not relivant.
24486 * <script type="text/javascript">
24490 * @class Roo.form.BasicForm
24491 * @extends Roo.util.Observable
24492 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24494 * @param {String/HTMLElement/Roo.Element} el The form element or its id
24495 * @param {Object} config Configuration options
24497 Roo.form.BasicForm = function(el, config){
24498 this.allItems = [];
24499 this.childForms = [];
24500 Roo.apply(this, config);
24502 * The Roo.form.Field items in this form.
24503 * @type MixedCollection
24507 this.items = new Roo.util.MixedCollection(false, function(o){
24508 return o.id || (o.id = Roo.id());
24512 * @event beforeaction
24513 * Fires before any action is performed. Return false to cancel the action.
24514 * @param {Form} this
24515 * @param {Action} action The action to be performed
24517 beforeaction: true,
24519 * @event actionfailed
24520 * Fires when an action fails.
24521 * @param {Form} this
24522 * @param {Action} action The action that failed
24524 actionfailed : true,
24526 * @event actioncomplete
24527 * Fires when an action is completed.
24528 * @param {Form} this
24529 * @param {Action} action The action that completed
24531 actioncomplete : true
24536 Roo.form.BasicForm.superclass.constructor.call(this);
24538 Roo.form.BasicForm.popover.apply();
24541 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24543 * @cfg {String} method
24544 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24547 * @cfg {DataReader} reader
24548 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24549 * This is optional as there is built-in support for processing JSON.
24552 * @cfg {DataReader} errorReader
24553 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24554 * This is completely optional as there is built-in support for processing JSON.
24557 * @cfg {String} url
24558 * The URL to use for form actions if one isn't supplied in the action options.
24561 * @cfg {Boolean} fileUpload
24562 * Set to true if this form is a file upload.
24566 * @cfg {Object} baseParams
24567 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24572 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24577 activeAction : null,
24580 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24581 * or setValues() data instead of when the form was first created.
24583 trackResetOnLoad : false,
24587 * childForms - used for multi-tab forms
24590 childForms : false,
24593 * allItems - full list of fields.
24599 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24600 * element by passing it or its id or mask the form itself by passing in true.
24603 waitMsgTarget : false,
24608 disableMask : false,
24611 * @cfg {Boolean} errorMask (true|false) default false
24616 * @cfg {Number} maskOffset Default 100
24621 initEl : function(el){
24622 this.el = Roo.get(el);
24623 this.id = this.el.id || Roo.id();
24624 this.el.on('submit', this.onSubmit, this);
24625 this.el.addClass('x-form');
24629 onSubmit : function(e){
24634 * Returns true if client-side validation on the form is successful.
24637 isValid : function(){
24639 var target = false;
24640 this.items.each(function(f){
24647 if(!target && f.el.isVisible(true)){
24652 if(this.errorMask && !valid){
24653 Roo.form.BasicForm.popover.mask(this, target);
24659 * Returns array of invalid form fields.
24663 invalidFields : function()
24666 this.items.each(function(f){
24679 * DEPRICATED Returns true if any fields in this form have changed since their original load.
24682 isDirty : function(){
24684 this.items.each(function(f){
24694 * Returns true if any fields in this form have changed since their original load. (New version)
24698 hasChanged : function()
24701 this.items.each(function(f){
24702 if(f.hasChanged()){
24711 * Resets all hasChanged to 'false' -
24712 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24713 * So hasChanged storage is only to be used for this purpose
24716 resetHasChanged : function()
24718 this.items.each(function(f){
24719 f.resetHasChanged();
24726 * Performs a predefined action (submit or load) or custom actions you define on this form.
24727 * @param {String} actionName The name of the action type
24728 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
24729 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24730 * accept other config options):
24732 Property Type Description
24733 ---------------- --------------- ----------------------------------------------------------------------------------
24734 url String The url for the action (defaults to the form's url)
24735 method String The form method to use (defaults to the form's method, or POST if not defined)
24736 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
24737 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
24738 validate the form on the client (defaults to false)
24740 * @return {BasicForm} this
24742 doAction : function(action, options){
24743 if(typeof action == 'string'){
24744 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24746 if(this.fireEvent('beforeaction', this, action) !== false){
24747 this.beforeAction(action);
24748 action.run.defer(100, action);
24754 * Shortcut to do a submit action.
24755 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24756 * @return {BasicForm} this
24758 submit : function(options){
24759 this.doAction('submit', options);
24764 * Shortcut to do a load action.
24765 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24766 * @return {BasicForm} this
24768 load : function(options){
24769 this.doAction('load', options);
24774 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24775 * @param {Record} record The record to edit
24776 * @return {BasicForm} this
24778 updateRecord : function(record){
24779 record.beginEdit();
24780 var fs = record.fields;
24781 fs.each(function(f){
24782 var field = this.findField(f.name);
24784 record.set(f.name, field.getValue());
24792 * Loads an Roo.data.Record into this form.
24793 * @param {Record} record The record to load
24794 * @return {BasicForm} this
24796 loadRecord : function(record){
24797 this.setValues(record.data);
24802 beforeAction : function(action){
24803 var o = action.options;
24805 if(!this.disableMask) {
24806 if(this.waitMsgTarget === true){
24807 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24808 }else if(this.waitMsgTarget){
24809 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24810 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24812 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24820 afterAction : function(action, success){
24821 this.activeAction = null;
24822 var o = action.options;
24824 if(!this.disableMask) {
24825 if(this.waitMsgTarget === true){
24827 }else if(this.waitMsgTarget){
24828 this.waitMsgTarget.unmask();
24830 Roo.MessageBox.updateProgress(1);
24831 Roo.MessageBox.hide();
24839 Roo.callback(o.success, o.scope, [this, action]);
24840 this.fireEvent('actioncomplete', this, action);
24844 // failure condition..
24845 // we have a scenario where updates need confirming.
24846 // eg. if a locking scenario exists..
24847 // we look for { errors : { needs_confirm : true }} in the response.
24849 (typeof(action.result) != 'undefined') &&
24850 (typeof(action.result.errors) != 'undefined') &&
24851 (typeof(action.result.errors.needs_confirm) != 'undefined')
24854 Roo.MessageBox.confirm(
24855 "Change requires confirmation",
24856 action.result.errorMsg,
24861 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
24871 Roo.callback(o.failure, o.scope, [this, action]);
24872 // show an error message if no failed handler is set..
24873 if (!this.hasListener('actionfailed')) {
24874 Roo.MessageBox.alert("Error",
24875 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24876 action.result.errorMsg :
24877 "Saving Failed, please check your entries or try again"
24881 this.fireEvent('actionfailed', this, action);
24887 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24888 * @param {String} id The value to search for
24891 findField : function(id){
24892 var field = this.items.get(id);
24894 this.items.each(function(f){
24895 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24901 return field || null;
24905 * Add a secondary form to this one,
24906 * Used to provide tabbed forms. One form is primary, with hidden values
24907 * which mirror the elements from the other forms.
24909 * @param {Roo.form.Form} form to add.
24912 addForm : function(form)
24915 if (this.childForms.indexOf(form) > -1) {
24919 this.childForms.push(form);
24921 Roo.each(form.allItems, function (fe) {
24923 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24924 if (this.findField(n)) { // already added..
24927 var add = new Roo.form.Hidden({
24930 add.render(this.el);
24937 * Mark fields in this form invalid in bulk.
24938 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24939 * @return {BasicForm} this
24941 markInvalid : function(errors){
24942 if(errors instanceof Array){
24943 for(var i = 0, len = errors.length; i < len; i++){
24944 var fieldError = errors[i];
24945 var f = this.findField(fieldError.id);
24947 f.markInvalid(fieldError.msg);
24953 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24954 field.markInvalid(errors[id]);
24958 Roo.each(this.childForms || [], function (f) {
24959 f.markInvalid(errors);
24966 * Set values for fields in this form in bulk.
24967 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24968 * @return {BasicForm} this
24970 setValues : function(values){
24971 if(values instanceof Array){ // array of objects
24972 for(var i = 0, len = values.length; i < len; i++){
24974 var f = this.findField(v.id);
24976 f.setValue(v.value);
24977 if(this.trackResetOnLoad){
24978 f.originalValue = f.getValue();
24982 }else{ // object hash
24985 if(typeof values[id] != 'function' && (field = this.findField(id))){
24987 if (field.setFromData &&
24988 field.valueField &&
24989 field.displayField &&
24990 // combos' with local stores can
24991 // be queried via setValue()
24992 // to set their value..
24993 (field.store && !field.store.isLocal)
24997 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24998 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24999 field.setFromData(sd);
25002 field.setValue(values[id]);
25006 if(this.trackResetOnLoad){
25007 field.originalValue = field.getValue();
25012 this.resetHasChanged();
25015 Roo.each(this.childForms || [], function (f) {
25016 f.setValues(values);
25017 f.resetHasChanged();
25024 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25025 * they are returned as an array.
25026 * @param {Boolean} asString
25029 getValues : function(asString){
25030 if (this.childForms) {
25031 // copy values from the child forms
25032 Roo.each(this.childForms, function (f) {
25033 this.setValues(f.getValues());
25038 if (typeof(FormData) != 'undefined' && asString !== true) {
25039 // this relies on a 'recent' version of chrome apparently...
25041 var fd = (new FormData(this.el.dom)).entries();
25043 var ent = fd.next();
25044 while (!ent.done) {
25045 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25056 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25057 if(asString === true){
25060 return Roo.urlDecode(fs);
25064 * Returns the fields in this form as an object with key/value pairs.
25065 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25068 getFieldValues : function(with_hidden)
25070 if (this.childForms) {
25071 // copy values from the child forms
25072 // should this call getFieldValues - probably not as we do not currently copy
25073 // hidden fields when we generate..
25074 Roo.each(this.childForms, function (f) {
25075 this.setValues(f.getValues());
25080 this.items.each(function(f){
25081 if (!f.getName()) {
25084 var v = f.getValue();
25085 if (f.inputType =='radio') {
25086 if (typeof(ret[f.getName()]) == 'undefined') {
25087 ret[f.getName()] = ''; // empty..
25090 if (!f.el.dom.checked) {
25094 v = f.el.dom.value;
25098 // not sure if this supported any more..
25099 if ((typeof(v) == 'object') && f.getRawValue) {
25100 v = f.getRawValue() ; // dates..
25102 // combo boxes where name != hiddenName...
25103 if (f.name != f.getName()) {
25104 ret[f.name] = f.getRawValue();
25106 ret[f.getName()] = v;
25113 * Clears all invalid messages in this form.
25114 * @return {BasicForm} this
25116 clearInvalid : function(){
25117 this.items.each(function(f){
25121 Roo.each(this.childForms || [], function (f) {
25130 * Resets this form.
25131 * @return {BasicForm} this
25133 reset : function(){
25134 this.items.each(function(f){
25138 Roo.each(this.childForms || [], function (f) {
25141 this.resetHasChanged();
25147 * Add Roo.form components to this form.
25148 * @param {Field} field1
25149 * @param {Field} field2 (optional)
25150 * @param {Field} etc (optional)
25151 * @return {BasicForm} this
25154 this.items.addAll(Array.prototype.slice.call(arguments, 0));
25160 * Removes a field from the items collection (does NOT remove its markup).
25161 * @param {Field} field
25162 * @return {BasicForm} this
25164 remove : function(field){
25165 this.items.remove(field);
25170 * Looks at the fields in this form, checks them for an id attribute,
25171 * and calls applyTo on the existing dom element with that id.
25172 * @return {BasicForm} this
25174 render : function(){
25175 this.items.each(function(f){
25176 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25184 * Calls {@link Ext#apply} for all fields in this form with the passed object.
25185 * @param {Object} values
25186 * @return {BasicForm} this
25188 applyToFields : function(o){
25189 this.items.each(function(f){
25196 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25197 * @param {Object} values
25198 * @return {BasicForm} this
25200 applyIfToFields : function(o){
25201 this.items.each(function(f){
25209 Roo.BasicForm = Roo.form.BasicForm;
25211 Roo.apply(Roo.form.BasicForm, {
25225 intervalID : false,
25231 if(this.isApplied){
25236 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25237 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25238 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25239 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25242 this.maskEl.top.enableDisplayMode("block");
25243 this.maskEl.left.enableDisplayMode("block");
25244 this.maskEl.bottom.enableDisplayMode("block");
25245 this.maskEl.right.enableDisplayMode("block");
25247 Roo.get(document.body).on('click', function(){
25251 Roo.get(document.body).on('touchstart', function(){
25255 this.isApplied = true
25258 mask : function(form, target)
25262 this.target = target;
25264 if(!this.form.errorMask || !target.el){
25268 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25270 var ot = this.target.el.calcOffsetsTo(scrollable);
25272 var scrollTo = ot[1] - this.form.maskOffset;
25274 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25276 scrollable.scrollTo('top', scrollTo);
25278 var el = this.target.wrap || this.target.el;
25280 var box = el.getBox();
25282 this.maskEl.top.setStyle('position', 'absolute');
25283 this.maskEl.top.setStyle('z-index', 10000);
25284 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25285 this.maskEl.top.setLeft(0);
25286 this.maskEl.top.setTop(0);
25287 this.maskEl.top.show();
25289 this.maskEl.left.setStyle('position', 'absolute');
25290 this.maskEl.left.setStyle('z-index', 10000);
25291 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25292 this.maskEl.left.setLeft(0);
25293 this.maskEl.left.setTop(box.y - this.padding);
25294 this.maskEl.left.show();
25296 this.maskEl.bottom.setStyle('position', 'absolute');
25297 this.maskEl.bottom.setStyle('z-index', 10000);
25298 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25299 this.maskEl.bottom.setLeft(0);
25300 this.maskEl.bottom.setTop(box.bottom + this.padding);
25301 this.maskEl.bottom.show();
25303 this.maskEl.right.setStyle('position', 'absolute');
25304 this.maskEl.right.setStyle('z-index', 10000);
25305 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25306 this.maskEl.right.setLeft(box.right + this.padding);
25307 this.maskEl.right.setTop(box.y - this.padding);
25308 this.maskEl.right.show();
25310 this.intervalID = window.setInterval(function() {
25311 Roo.form.BasicForm.popover.unmask();
25314 window.onwheel = function(){ return false;};
25316 (function(){ this.isMasked = true; }).defer(500, this);
25320 unmask : function()
25322 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25326 this.maskEl.top.setStyle('position', 'absolute');
25327 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25328 this.maskEl.top.hide();
25330 this.maskEl.left.setStyle('position', 'absolute');
25331 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25332 this.maskEl.left.hide();
25334 this.maskEl.bottom.setStyle('position', 'absolute');
25335 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25336 this.maskEl.bottom.hide();
25338 this.maskEl.right.setStyle('position', 'absolute');
25339 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25340 this.maskEl.right.hide();
25342 window.onwheel = function(){ return true;};
25344 if(this.intervalID){
25345 window.clearInterval(this.intervalID);
25346 this.intervalID = false;
25349 this.isMasked = false;
25357 * Ext JS Library 1.1.1
25358 * Copyright(c) 2006-2007, Ext JS, LLC.
25360 * Originally Released Under LGPL - original licence link has changed is not relivant.
25363 * <script type="text/javascript">
25367 * @class Roo.form.Form
25368 * @extends Roo.form.BasicForm
25369 * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
25370 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25372 * @param {Object} config Configuration options
25374 Roo.form.Form = function(config){
25376 if (config.items) {
25377 xitems = config.items;
25378 delete config.items;
25382 Roo.form.Form.superclass.constructor.call(this, null, config);
25383 this.url = this.url || this.action;
25385 this.root = new Roo.form.Layout(Roo.applyIf({
25389 this.active = this.root;
25391 * Array of all the buttons that have been added to this form via {@link addButton}
25395 this.allItems = [];
25398 * @event clientvalidation
25399 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25400 * @param {Form} this
25401 * @param {Boolean} valid true if the form has passed client-side validation
25403 clientvalidation: true,
25406 * Fires when the form is rendered
25407 * @param {Roo.form.Form} form
25412 if (this.progressUrl) {
25413 // push a hidden field onto the list of fields..
25417 name : 'UPLOAD_IDENTIFIER'
25422 Roo.each(xitems, this.addxtype, this);
25426 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25428 * @cfg {Roo.Button} buttons[] buttons at bottom of form
25432 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25435 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25438 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25440 buttonAlign:'center',
25443 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25448 * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25449 * This property cascades to child containers if not set.
25454 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25455 * fires a looping event with that state. This is required to bind buttons to the valid
25456 * state using the config value formBind:true on the button.
25458 monitorValid : false,
25461 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25466 * @cfg {String} progressUrl - Url to return progress data
25469 progressUrl : false,
25471 * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25472 * sending a formdata with extra parameters - eg uploaded elements.
25478 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25479 * fields are added and the column is closed. If no fields are passed the column remains open
25480 * until end() is called.
25481 * @param {Object} config The config to pass to the column
25482 * @param {Field} field1 (optional)
25483 * @param {Field} field2 (optional)
25484 * @param {Field} etc (optional)
25485 * @return Column The column container object
25487 column : function(c){
25488 var col = new Roo.form.Column(c);
25490 if(arguments.length > 1){ // duplicate code required because of Opera
25491 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25498 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25499 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25500 * until end() is called.
25501 * @param {Object} config The config to pass to the fieldset
25502 * @param {Field} field1 (optional)
25503 * @param {Field} field2 (optional)
25504 * @param {Field} etc (optional)
25505 * @return FieldSet The fieldset container object
25507 fieldset : function(c){
25508 var fs = new Roo.form.FieldSet(c);
25510 if(arguments.length > 1){ // duplicate code required because of Opera
25511 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25518 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25519 * fields are added and the container is closed. If no fields are passed the container remains open
25520 * until end() is called.
25521 * @param {Object} config The config to pass to the Layout
25522 * @param {Field} field1 (optional)
25523 * @param {Field} field2 (optional)
25524 * @param {Field} etc (optional)
25525 * @return Layout The container object
25527 container : function(c){
25528 var l = new Roo.form.Layout(c);
25530 if(arguments.length > 1){ // duplicate code required because of Opera
25531 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25538 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25539 * @param {Object} container A Roo.form.Layout or subclass of Layout
25540 * @return {Form} this
25542 start : function(c){
25543 // cascade label info
25544 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25545 this.active.stack.push(c);
25546 c.ownerCt = this.active;
25552 * Closes the current open container
25553 * @return {Form} this
25556 if(this.active == this.root){
25559 this.active = this.active.ownerCt;
25564 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
25565 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25566 * as the label of the field.
25567 * @param {Field} field1
25568 * @param {Field} field2 (optional)
25569 * @param {Field} etc. (optional)
25570 * @return {Form} this
25573 this.active.stack.push.apply(this.active.stack, arguments);
25574 this.allItems.push.apply(this.allItems,arguments);
25576 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25577 if(a[i].isFormField){
25582 Roo.form.Form.superclass.add.apply(this, r);
25592 * Find any element that has been added to a form, using it's ID or name
25593 * This can include framesets, columns etc. along with regular fields..
25594 * @param {String} id - id or name to find.
25596 * @return {Element} e - or false if nothing found.
25598 findbyId : function(id)
25604 Roo.each(this.allItems, function(f){
25605 if (f.id == id || f.name == id ){
25616 * Render this form into the passed container. This should only be called once!
25617 * @param {String/HTMLElement/Element} container The element this component should be rendered into
25618 * @return {Form} this
25620 render : function(ct)
25626 var o = this.autoCreate || {
25628 method : this.method || 'POST',
25629 id : this.id || Roo.id()
25631 this.initEl(ct.createChild(o));
25633 this.root.render(this.el);
25637 this.items.each(function(f){
25638 f.render('x-form-el-'+f.id);
25641 if(this.buttons.length > 0){
25642 // tables are required to maintain order and for correct IE layout
25643 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25644 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25645 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25647 var tr = tb.getElementsByTagName('tr')[0];
25648 for(var i = 0, len = this.buttons.length; i < len; i++) {
25649 var b = this.buttons[i];
25650 var td = document.createElement('td');
25651 td.className = 'x-form-btn-td';
25652 b.render(tr.appendChild(td));
25655 if(this.monitorValid){ // initialize after render
25656 this.startMonitoring();
25658 this.fireEvent('rendered', this);
25663 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25664 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25665 * object or a valid Roo.DomHelper element config
25666 * @param {Function} handler The function called when the button is clicked
25667 * @param {Object} scope (optional) The scope of the handler function
25668 * @return {Roo.Button}
25670 addButton : function(config, handler, scope){
25674 minWidth: this.minButtonWidth,
25677 if(typeof config == "string"){
25680 Roo.apply(bc, config);
25682 var btn = new Roo.Button(null, bc);
25683 this.buttons.push(btn);
25688 * Adds a series of form elements (using the xtype property as the factory method.
25689 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25690 * @param {Object} config
25693 addxtype : function()
25695 var ar = Array.prototype.slice.call(arguments, 0);
25697 for(var i = 0; i < ar.length; i++) {
25699 continue; // skip -- if this happends something invalid got sent, we
25700 // should ignore it, as basically that interface element will not show up
25701 // and that should be pretty obvious!!
25704 if (Roo.form[ar[i].xtype]) {
25706 var fe = Roo.factory(ar[i], Roo.form);
25712 fe.store.form = this;
25717 this.allItems.push(fe);
25718 if (fe.items && fe.addxtype) {
25719 fe.addxtype.apply(fe, fe.items);
25729 // console.log('adding ' + ar[i].xtype);
25731 if (ar[i].xtype == 'Button') {
25732 //console.log('adding button');
25733 //console.log(ar[i]);
25734 this.addButton(ar[i]);
25735 this.allItems.push(fe);
25739 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25740 alert('end is not supported on xtype any more, use items');
25742 // //console.log('adding end');
25750 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25751 * option "monitorValid"
25753 startMonitoring : function(){
25756 Roo.TaskMgr.start({
25757 run : this.bindHandler,
25758 interval : this.monitorPoll || 200,
25765 * Stops monitoring of the valid state of this form
25767 stopMonitoring : function(){
25768 this.bound = false;
25772 bindHandler : function(){
25774 return false; // stops binding
25777 this.items.each(function(f){
25778 if(!f.isValid(true)){
25783 for(var i = 0, len = this.buttons.length; i < len; i++){
25784 var btn = this.buttons[i];
25785 if(btn.formBind === true && btn.disabled === valid){
25786 btn.setDisabled(!valid);
25789 this.fireEvent('clientvalidation', this, valid);
25803 Roo.Form = Roo.form.Form;
25806 * Ext JS Library 1.1.1
25807 * Copyright(c) 2006-2007, Ext JS, LLC.
25809 * Originally Released Under LGPL - original licence link has changed is not relivant.
25812 * <script type="text/javascript">
25815 // as we use this in bootstrap.
25816 Roo.namespace('Roo.form');
25818 * @class Roo.form.Action
25819 * Internal Class used to handle form actions
25821 * @param {Roo.form.BasicForm} el The form element or its id
25822 * @param {Object} config Configuration options
25827 // define the action interface
25828 Roo.form.Action = function(form, options){
25830 this.options = options || {};
25833 * Client Validation Failed
25836 Roo.form.Action.CLIENT_INVALID = 'client';
25838 * Server Validation Failed
25841 Roo.form.Action.SERVER_INVALID = 'server';
25843 * Connect to Server Failed
25846 Roo.form.Action.CONNECT_FAILURE = 'connect';
25848 * Reading Data from Server Failed
25851 Roo.form.Action.LOAD_FAILURE = 'load';
25853 Roo.form.Action.prototype = {
25855 failureType : undefined,
25856 response : undefined,
25857 result : undefined,
25859 // interface method
25860 run : function(options){
25864 // interface method
25865 success : function(response){
25869 // interface method
25870 handleResponse : function(response){
25874 // default connection failure
25875 failure : function(response){
25877 this.response = response;
25878 this.failureType = Roo.form.Action.CONNECT_FAILURE;
25879 this.form.afterAction(this, false);
25882 processResponse : function(response){
25883 this.response = response;
25884 if(!response.responseText){
25887 this.result = this.handleResponse(response);
25888 return this.result;
25891 // utility functions used internally
25892 getUrl : function(appendParams){
25893 var url = this.options.url || this.form.url || this.form.el.dom.action;
25895 var p = this.getParams();
25897 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25903 getMethod : function(){
25904 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25907 getParams : function(){
25908 var bp = this.form.baseParams;
25909 var p = this.options.params;
25911 if(typeof p == "object"){
25912 p = Roo.urlEncode(Roo.applyIf(p, bp));
25913 }else if(typeof p == 'string' && bp){
25914 p += '&' + Roo.urlEncode(bp);
25917 p = Roo.urlEncode(bp);
25922 createCallback : function(){
25924 success: this.success,
25925 failure: this.failure,
25927 timeout: (this.form.timeout*1000),
25928 upload: this.form.fileUpload ? this.success : undefined
25933 Roo.form.Action.Submit = function(form, options){
25934 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25937 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25940 haveProgress : false,
25941 uploadComplete : false,
25943 // uploadProgress indicator.
25944 uploadProgress : function()
25946 if (!this.form.progressUrl) {
25950 if (!this.haveProgress) {
25951 Roo.MessageBox.progress("Uploading", "Uploading");
25953 if (this.uploadComplete) {
25954 Roo.MessageBox.hide();
25958 this.haveProgress = true;
25960 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25962 var c = new Roo.data.Connection();
25964 url : this.form.progressUrl,
25969 success : function(req){
25970 //console.log(data);
25974 rdata = Roo.decode(req.responseText)
25976 Roo.log("Invalid data from server..");
25980 if (!rdata || !rdata.success) {
25982 Roo.MessageBox.alert(Roo.encode(rdata));
25985 var data = rdata.data;
25987 if (this.uploadComplete) {
25988 Roo.MessageBox.hide();
25993 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25994 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25997 this.uploadProgress.defer(2000,this);
26000 failure: function(data) {
26001 Roo.log('progress url failed ');
26012 // run get Values on the form, so it syncs any secondary forms.
26013 this.form.getValues();
26015 var o = this.options;
26016 var method = this.getMethod();
26017 var isPost = method == 'POST';
26018 if(o.clientValidation === false || this.form.isValid()){
26020 if (this.form.progressUrl) {
26021 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26022 (new Date() * 1) + '' + Math.random());
26027 Roo.Ajax.request(Roo.apply(this.createCallback(), {
26028 form:this.form.el.dom,
26029 url:this.getUrl(!isPost),
26031 params:isPost ? this.getParams() : null,
26032 isUpload: this.form.fileUpload,
26033 formData : this.form.formData
26036 this.uploadProgress();
26038 }else if (o.clientValidation !== false){ // client validation failed
26039 this.failureType = Roo.form.Action.CLIENT_INVALID;
26040 this.form.afterAction(this, false);
26044 success : function(response)
26046 this.uploadComplete= true;
26047 if (this.haveProgress) {
26048 Roo.MessageBox.hide();
26052 var result = this.processResponse(response);
26053 if(result === true || result.success){
26054 this.form.afterAction(this, true);
26058 this.form.markInvalid(result.errors);
26059 this.failureType = Roo.form.Action.SERVER_INVALID;
26061 this.form.afterAction(this, false);
26063 failure : function(response)
26065 this.uploadComplete= true;
26066 if (this.haveProgress) {
26067 Roo.MessageBox.hide();
26070 this.response = response;
26071 this.failureType = Roo.form.Action.CONNECT_FAILURE;
26072 this.form.afterAction(this, false);
26075 handleResponse : function(response){
26076 if(this.form.errorReader){
26077 var rs = this.form.errorReader.read(response);
26080 for(var i = 0, len = rs.records.length; i < len; i++) {
26081 var r = rs.records[i];
26082 errors[i] = r.data;
26085 if(errors.length < 1){
26089 success : rs.success,
26095 ret = Roo.decode(response.responseText);
26099 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26109 Roo.form.Action.Load = function(form, options){
26110 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26111 this.reader = this.form.reader;
26114 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26119 Roo.Ajax.request(Roo.apply(
26120 this.createCallback(), {
26121 method:this.getMethod(),
26122 url:this.getUrl(false),
26123 params:this.getParams()
26127 success : function(response){
26129 var result = this.processResponse(response);
26130 if(result === true || !result.success || !result.data){
26131 this.failureType = Roo.form.Action.LOAD_FAILURE;
26132 this.form.afterAction(this, false);
26135 this.form.clearInvalid();
26136 this.form.setValues(result.data);
26137 this.form.afterAction(this, true);
26140 handleResponse : function(response){
26141 if(this.form.reader){
26142 var rs = this.form.reader.read(response);
26143 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26145 success : rs.success,
26149 return Roo.decode(response.responseText);
26153 Roo.form.Action.ACTION_TYPES = {
26154 'load' : Roo.form.Action.Load,
26155 'submit' : Roo.form.Action.Submit
26158 * Ext JS Library 1.1.1
26159 * Copyright(c) 2006-2007, Ext JS, LLC.
26161 * Originally Released Under LGPL - original licence link has changed is not relivant.
26164 * <script type="text/javascript">
26168 * @class Roo.form.Layout
26169 * @extends Roo.Component
26170 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26171 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26173 * @param {Object} config Configuration options
26175 Roo.form.Layout = function(config){
26177 if (config.items) {
26178 xitems = config.items;
26179 delete config.items;
26181 Roo.form.Layout.superclass.constructor.call(this, config);
26183 Roo.each(xitems, this.addxtype, this);
26187 Roo.extend(Roo.form.Layout, Roo.Component, {
26189 * @cfg {String/Object} autoCreate
26190 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26193 * @cfg {String/Object/Function} style
26194 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26195 * a function which returns such a specification.
26198 * @cfg {String} labelAlign
26199 * Valid values are "left," "top" and "right" (defaults to "left")
26202 * @cfg {Number} labelWidth
26203 * Fixed width in pixels of all field labels (defaults to undefined)
26206 * @cfg {Boolean} clear
26207 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26211 * @cfg {String} labelSeparator
26212 * The separator to use after field labels (defaults to ':')
26214 labelSeparator : ':',
26216 * @cfg {Boolean} hideLabels
26217 * True to suppress the display of field labels in this layout (defaults to false)
26219 hideLabels : false,
26222 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26227 onRender : function(ct, position){
26228 if(this.el){ // from markup
26229 this.el = Roo.get(this.el);
26230 }else { // generate
26231 var cfg = this.getAutoCreate();
26232 this.el = ct.createChild(cfg, position);
26235 this.el.applyStyles(this.style);
26237 if(this.labelAlign){
26238 this.el.addClass('x-form-label-'+this.labelAlign);
26240 if(this.hideLabels){
26241 this.labelStyle = "display:none";
26242 this.elementStyle = "padding-left:0;";
26244 if(typeof this.labelWidth == 'number'){
26245 this.labelStyle = "width:"+this.labelWidth+"px;";
26246 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26248 if(this.labelAlign == 'top'){
26249 this.labelStyle = "width:auto;";
26250 this.elementStyle = "padding-left:0;";
26253 var stack = this.stack;
26254 var slen = stack.length;
26256 if(!this.fieldTpl){
26257 var t = new Roo.Template(
26258 '<div class="x-form-item {5}">',
26259 '<label for="{0}" style="{2}">{1}{4}</label>',
26260 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26262 '</div><div class="x-form-clear-left"></div>'
26264 t.disableFormats = true;
26266 Roo.form.Layout.prototype.fieldTpl = t;
26268 for(var i = 0; i < slen; i++) {
26269 if(stack[i].isFormField){
26270 this.renderField(stack[i]);
26272 this.renderComponent(stack[i]);
26277 this.el.createChild({cls:'x-form-clear'});
26282 renderField : function(f){
26283 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26286 f.labelStyle||this.labelStyle||'', //2
26287 this.elementStyle||'', //3
26288 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26289 f.itemCls||this.itemCls||'' //5
26290 ], true).getPrevSibling());
26294 renderComponent : function(c){
26295 c.render(c.isLayout ? this.el : this.el.createChild());
26298 * Adds a object form elements (using the xtype property as the factory method.)
26299 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
26300 * @param {Object} config
26302 addxtype : function(o)
26304 // create the lement.
26305 o.form = this.form;
26306 var fe = Roo.factory(o, Roo.form);
26307 this.form.allItems.push(fe);
26308 this.stack.push(fe);
26310 if (fe.isFormField) {
26311 this.form.items.add(fe);
26319 * @class Roo.form.Column
26320 * @extends Roo.form.Layout
26321 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26323 * @param {Object} config Configuration options
26325 Roo.form.Column = function(config){
26326 Roo.form.Column.superclass.constructor.call(this, config);
26329 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26331 * @cfg {Number/String} width
26332 * The fixed width of the column in pixels or CSS value (defaults to "auto")
26335 * @cfg {String/Object} autoCreate
26336 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26340 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26343 onRender : function(ct, position){
26344 Roo.form.Column.superclass.onRender.call(this, ct, position);
26346 this.el.setWidth(this.width);
26353 * @class Roo.form.Row
26354 * @extends Roo.form.Layout
26355 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26356 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26358 * @param {Object} config Configuration options
26362 Roo.form.Row = function(config){
26363 Roo.form.Row.superclass.constructor.call(this, config);
26366 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26368 * @cfg {Number/String} width
26369 * The fixed width of the column in pixels or CSS value (defaults to "auto")
26372 * @cfg {Number/String} height
26373 * The fixed height of the column in pixels or CSS value (defaults to "auto")
26375 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26379 onRender : function(ct, position){
26380 //console.log('row render');
26382 var t = new Roo.Template(
26383 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26384 '<label for="{0}" style="{2}">{1}{4}</label>',
26385 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26389 t.disableFormats = true;
26391 Roo.form.Layout.prototype.rowTpl = t;
26393 this.fieldTpl = this.rowTpl;
26395 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26396 var labelWidth = 100;
26398 if ((this.labelAlign != 'top')) {
26399 if (typeof this.labelWidth == 'number') {
26400 labelWidth = this.labelWidth
26402 this.padWidth = 20 + labelWidth;
26406 Roo.form.Column.superclass.onRender.call(this, ct, position);
26408 this.el.setWidth(this.width);
26411 this.el.setHeight(this.height);
26416 renderField : function(f){
26417 f.fieldEl = this.fieldTpl.append(this.el, [
26418 f.id, f.fieldLabel,
26419 f.labelStyle||this.labelStyle||'',
26420 this.elementStyle||'',
26421 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26422 f.itemCls||this.itemCls||'',
26423 f.width ? f.width + this.padWidth : 160 + this.padWidth
26430 * @class Roo.form.FieldSet
26431 * @extends Roo.form.Layout
26432 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
26433 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26435 * @param {Object} config Configuration options
26437 Roo.form.FieldSet = function(config){
26438 Roo.form.FieldSet.superclass.constructor.call(this, config);
26441 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26443 * @cfg {String} legend
26444 * The text to display as the legend for the FieldSet (defaults to '')
26447 * @cfg {String/Object} autoCreate
26448 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26452 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26455 onRender : function(ct, position){
26456 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26458 this.setLegend(this.legend);
26463 setLegend : function(text){
26465 this.el.child('legend').update(text);
26470 * Ext JS Library 1.1.1
26471 * Copyright(c) 2006-2007, Ext JS, LLC.
26473 * Originally Released Under LGPL - original licence link has changed is not relivant.
26476 * <script type="text/javascript">
26479 * @class Roo.form.VTypes
26480 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26483 Roo.form.VTypes = function(){
26484 // closure these in so they are only created once.
26485 var alpha = /^[a-zA-Z_]+$/;
26486 var alphanum = /^[a-zA-Z0-9_]+$/;
26487 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26488 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26490 // All these messages and functions are configurable
26493 * The function used to validate email addresses
26494 * @param {String} value The email address
26496 'email' : function(v){
26497 return email.test(v);
26500 * The error text to display when the email validation function returns false
26503 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26505 * The keystroke filter mask to be applied on email input
26508 'emailMask' : /[a-z0-9_\.\-@]/i,
26511 * The function used to validate URLs
26512 * @param {String} value The URL
26514 'url' : function(v){
26515 return url.test(v);
26518 * The error text to display when the url validation function returns false
26521 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26524 * The function used to validate alpha values
26525 * @param {String} value The value
26527 'alpha' : function(v){
26528 return alpha.test(v);
26531 * The error text to display when the alpha validation function returns false
26534 'alphaText' : 'This field should only contain letters and _',
26536 * The keystroke filter mask to be applied on alpha input
26539 'alphaMask' : /[a-z_]/i,
26542 * The function used to validate alphanumeric values
26543 * @param {String} value The value
26545 'alphanum' : function(v){
26546 return alphanum.test(v);
26549 * The error text to display when the alphanumeric validation function returns false
26552 'alphanumText' : 'This field should only contain letters, numbers and _',
26554 * The keystroke filter mask to be applied on alphanumeric input
26557 'alphanumMask' : /[a-z0-9_]/i
26559 }();//<script type="text/javascript">
26562 * @class Roo.form.FCKeditor
26563 * @extends Roo.form.TextArea
26564 * Wrapper around the FCKEditor http://www.fckeditor.net
26566 * Creates a new FCKeditor
26567 * @param {Object} config Configuration options
26569 Roo.form.FCKeditor = function(config){
26570 Roo.form.FCKeditor.superclass.constructor.call(this, config);
26573 * @event editorinit
26574 * Fired when the editor is initialized - you can add extra handlers here..
26575 * @param {FCKeditor} this
26576 * @param {Object} the FCK object.
26583 Roo.form.FCKeditor.editors = { };
26584 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26586 //defaultAutoCreate : {
26587 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
26591 * @cfg {Object} fck options - see fck manual for details.
26596 * @cfg {Object} fck toolbar set (Basic or Default)
26598 toolbarSet : 'Basic',
26600 * @cfg {Object} fck BasePath
26602 basePath : '/fckeditor/',
26610 onRender : function(ct, position)
26613 this.defaultAutoCreate = {
26615 style:"width:300px;height:60px;",
26616 autocomplete: "new-password"
26619 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26622 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26623 if(this.preventScrollbars){
26624 this.el.setStyle("overflow", "hidden");
26626 this.el.setHeight(this.growMin);
26629 //console.log('onrender' + this.getId() );
26630 Roo.form.FCKeditor.editors[this.getId()] = this;
26633 this.replaceTextarea() ;
26637 getEditor : function() {
26638 return this.fckEditor;
26641 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
26642 * @param {Mixed} value The value to set
26646 setValue : function(value)
26648 //console.log('setValue: ' + value);
26650 if(typeof(value) == 'undefined') { // not sure why this is happending...
26653 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26655 //if(!this.el || !this.getEditor()) {
26656 // this.value = value;
26657 //this.setValue.defer(100,this,[value]);
26661 if(!this.getEditor()) {
26665 this.getEditor().SetData(value);
26672 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
26673 * @return {Mixed} value The field value
26675 getValue : function()
26678 if (this.frame && this.frame.dom.style.display == 'none') {
26679 return Roo.form.FCKeditor.superclass.getValue.call(this);
26682 if(!this.el || !this.getEditor()) {
26684 // this.getValue.defer(100,this);
26689 var value=this.getEditor().GetData();
26690 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26691 return Roo.form.FCKeditor.superclass.getValue.call(this);
26697 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
26698 * @return {Mixed} value The field value
26700 getRawValue : function()
26702 if (this.frame && this.frame.dom.style.display == 'none') {
26703 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26706 if(!this.el || !this.getEditor()) {
26707 //this.getRawValue.defer(100,this);
26714 var value=this.getEditor().GetData();
26715 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26716 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26720 setSize : function(w,h) {
26724 //if (this.frame && this.frame.dom.style.display == 'none') {
26725 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26728 //if(!this.el || !this.getEditor()) {
26729 // this.setSize.defer(100,this, [w,h]);
26735 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26737 this.frame.dom.setAttribute('width', w);
26738 this.frame.dom.setAttribute('height', h);
26739 this.frame.setSize(w,h);
26743 toggleSourceEdit : function(value) {
26747 this.el.dom.style.display = value ? '' : 'none';
26748 this.frame.dom.style.display = value ? 'none' : '';
26753 focus: function(tag)
26755 if (this.frame.dom.style.display == 'none') {
26756 return Roo.form.FCKeditor.superclass.focus.call(this);
26758 if(!this.el || !this.getEditor()) {
26759 this.focus.defer(100,this, [tag]);
26766 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26767 this.getEditor().Focus();
26769 if (!this.getEditor().Selection.GetSelection()) {
26770 this.focus.defer(100,this, [tag]);
26775 var r = this.getEditor().EditorDocument.createRange();
26776 r.setStart(tgs[0],0);
26777 r.setEnd(tgs[0],0);
26778 this.getEditor().Selection.GetSelection().removeAllRanges();
26779 this.getEditor().Selection.GetSelection().addRange(r);
26780 this.getEditor().Focus();
26787 replaceTextarea : function()
26789 if ( document.getElementById( this.getId() + '___Frame' ) ) {
26792 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26794 // We must check the elements firstly using the Id and then the name.
26795 var oTextarea = document.getElementById( this.getId() );
26797 var colElementsByName = document.getElementsByName( this.getId() ) ;
26799 oTextarea.style.display = 'none' ;
26801 if ( oTextarea.tabIndex ) {
26802 this.TabIndex = oTextarea.tabIndex ;
26805 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26806 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26807 this.frame = Roo.get(this.getId() + '___Frame')
26810 _getConfigHtml : function()
26814 for ( var o in this.fckconfig ) {
26815 sConfig += sConfig.length > 0 ? '&' : '';
26816 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26819 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26823 _getIFrameHtml : function()
26825 var sFile = 'fckeditor.html' ;
26826 /* no idea what this is about..
26829 if ( (/fcksource=true/i).test( window.top.location.search ) )
26830 sFile = 'fckeditor.original.html' ;
26835 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26836 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
26839 var html = '<iframe id="' + this.getId() +
26840 '___Frame" src="' + sLink +
26841 '" width="' + this.width +
26842 '" height="' + this.height + '"' +
26843 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
26844 ' frameborder="0" scrolling="no"></iframe>' ;
26849 _insertHtmlBefore : function( html, element )
26851 if ( element.insertAdjacentHTML ) {
26853 element.insertAdjacentHTML( 'beforeBegin', html ) ;
26855 var oRange = document.createRange() ;
26856 oRange.setStartBefore( element ) ;
26857 var oFragment = oRange.createContextualFragment( html );
26858 element.parentNode.insertBefore( oFragment, element ) ;
26871 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26873 function FCKeditor_OnComplete(editorInstance){
26874 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26875 f.fckEditor = editorInstance;
26876 //console.log("loaded");
26877 f.fireEvent('editorinit', f, editorInstance);
26897 //<script type="text/javascript">
26899 * @class Roo.form.GridField
26900 * @extends Roo.form.Field
26901 * Embed a grid (or editable grid into a form)
26904 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26906 * xgrid.store = Roo.data.Store
26907 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26908 * xgrid.store.reader = Roo.data.JsonReader
26912 * Creates a new GridField
26913 * @param {Object} config Configuration options
26915 Roo.form.GridField = function(config){
26916 Roo.form.GridField.superclass.constructor.call(this, config);
26920 Roo.extend(Roo.form.GridField, Roo.form.Field, {
26922 * @cfg {Number} width - used to restrict width of grid..
26926 * @cfg {Number} height - used to restrict height of grid..
26930 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26936 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26937 * {tag: "input", type: "checkbox", autocomplete: "off"})
26939 // defaultAutoCreate : { tag: 'div' },
26940 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26942 * @cfg {String} addTitle Text to include for adding a title.
26946 onResize : function(){
26947 Roo.form.Field.superclass.onResize.apply(this, arguments);
26950 initEvents : function(){
26951 // Roo.form.Checkbox.superclass.initEvents.call(this);
26952 // has no events...
26957 getResizeEl : function(){
26961 getPositionEl : function(){
26966 onRender : function(ct, position){
26968 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26969 var style = this.style;
26972 Roo.form.GridField.superclass.onRender.call(this, ct, position);
26973 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26974 this.viewEl = this.wrap.createChild({ tag: 'div' });
26976 this.viewEl.applyStyles(style);
26979 this.viewEl.setWidth(this.width);
26982 this.viewEl.setHeight(this.height);
26984 //if(this.inputValue !== undefined){
26985 //this.setValue(this.value);
26988 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26991 this.grid.render();
26992 this.grid.getDataSource().on('remove', this.refreshValue, this);
26993 this.grid.getDataSource().on('update', this.refreshValue, this);
26994 this.grid.on('afteredit', this.refreshValue, this);
27000 * Sets the value of the item.
27001 * @param {String} either an object or a string..
27003 setValue : function(v){
27005 v = v || []; // empty set..
27006 // this does not seem smart - it really only affects memoryproxy grids..
27007 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27008 var ds = this.grid.getDataSource();
27009 // assumes a json reader..
27011 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
27012 ds.loadData( data);
27014 // clear selection so it does not get stale.
27015 if (this.grid.sm) {
27016 this.grid.sm.clearSelections();
27019 Roo.form.GridField.superclass.setValue.call(this, v);
27020 this.refreshValue();
27021 // should load data in the grid really....
27025 refreshValue: function() {
27027 this.grid.getDataSource().each(function(r) {
27030 this.el.dom.value = Roo.encode(val);
27038 * Ext JS Library 1.1.1
27039 * Copyright(c) 2006-2007, Ext JS, LLC.
27041 * Originally Released Under LGPL - original licence link has changed is not relivant.
27044 * <script type="text/javascript">
27047 * @class Roo.form.DisplayField
27048 * @extends Roo.form.Field
27049 * A generic Field to display non-editable data.
27050 * @cfg {Boolean} closable (true|false) default false
27052 * Creates a new Display Field item.
27053 * @param {Object} config Configuration options
27055 Roo.form.DisplayField = function(config){
27056 Roo.form.DisplayField.superclass.constructor.call(this, config);
27061 * Fires after the click the close btn
27062 * @param {Roo.form.DisplayField} this
27068 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
27069 inputType: 'hidden',
27075 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27077 focusClass : undefined,
27079 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27081 fieldClass: 'x-form-field',
27084 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27086 valueRenderer: undefined,
27090 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27091 * {tag: "input", type: "checkbox", autocomplete: "off"})
27094 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27098 onResize : function(){
27099 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27103 initEvents : function(){
27104 // Roo.form.Checkbox.superclass.initEvents.call(this);
27105 // has no events...
27108 this.closeEl.on('click', this.onClose, this);
27114 getResizeEl : function(){
27118 getPositionEl : function(){
27123 onRender : function(ct, position){
27125 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27126 //if(this.inputValue !== undefined){
27127 this.wrap = this.el.wrap();
27129 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27132 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27135 if (this.bodyStyle) {
27136 this.viewEl.applyStyles(this.bodyStyle);
27138 //this.viewEl.setStyle('padding', '2px');
27140 this.setValue(this.value);
27145 initValue : Roo.emptyFn,
27150 onClick : function(){
27155 * Sets the checked state of the checkbox.
27156 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27158 setValue : function(v){
27160 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
27161 // this might be called before we have a dom element..
27162 if (!this.viewEl) {
27165 this.viewEl.dom.innerHTML = html;
27166 Roo.form.DisplayField.superclass.setValue.call(this, v);
27170 onClose : function(e)
27172 e.preventDefault();
27174 this.fireEvent('close', this);
27183 * @class Roo.form.DayPicker
27184 * @extends Roo.form.Field
27185 * A Day picker show [M] [T] [W] ....
27187 * Creates a new Day Picker
27188 * @param {Object} config Configuration options
27190 Roo.form.DayPicker= function(config){
27191 Roo.form.DayPicker.superclass.constructor.call(this, config);
27195 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
27197 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27199 focusClass : undefined,
27201 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27203 fieldClass: "x-form-field",
27206 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27207 * {tag: "input", type: "checkbox", autocomplete: "off"})
27209 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27212 actionMode : 'viewEl',
27216 inputType : 'hidden',
27219 inputElement: false, // real input element?
27220 basedOn: false, // ????
27222 isFormField: true, // not sure where this is needed!!!!
27224 onResize : function(){
27225 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27226 if(!this.boxLabel){
27227 this.el.alignTo(this.wrap, 'c-c');
27231 initEvents : function(){
27232 Roo.form.Checkbox.superclass.initEvents.call(this);
27233 this.el.on("click", this.onClick, this);
27234 this.el.on("change", this.onClick, this);
27238 getResizeEl : function(){
27242 getPositionEl : function(){
27248 onRender : function(ct, position){
27249 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27251 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27253 var r1 = '<table><tr>';
27254 var r2 = '<tr class="x-form-daypick-icons">';
27255 for (var i=0; i < 7; i++) {
27256 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27257 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
27260 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27261 viewEl.select('img').on('click', this.onClick, this);
27262 this.viewEl = viewEl;
27265 // this will not work on Chrome!!!
27266 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
27267 this.el.on('propertychange', this.setFromHidden, this); //ie
27275 initValue : Roo.emptyFn,
27278 * Returns the checked state of the checkbox.
27279 * @return {Boolean} True if checked, else false
27281 getValue : function(){
27282 return this.el.dom.value;
27287 onClick : function(e){
27288 //this.setChecked(!this.checked);
27289 Roo.get(e.target).toggleClass('x-menu-item-checked');
27290 this.refreshValue();
27291 //if(this.el.dom.checked != this.checked){
27292 // this.setValue(this.el.dom.checked);
27297 refreshValue : function()
27300 this.viewEl.select('img',true).each(function(e,i,n) {
27301 val += e.is(".x-menu-item-checked") ? String(n) : '';
27303 this.setValue(val, true);
27307 * Sets the checked state of the checkbox.
27308 * On is always based on a string comparison between inputValue and the param.
27309 * @param {Boolean/String} value - the value to set
27310 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27312 setValue : function(v,suppressEvent){
27313 if (!this.el.dom) {
27316 var old = this.el.dom.value ;
27317 this.el.dom.value = v;
27318 if (suppressEvent) {
27322 // update display..
27323 this.viewEl.select('img',true).each(function(e,i,n) {
27325 var on = e.is(".x-menu-item-checked");
27326 var newv = v.indexOf(String(n)) > -1;
27328 e.toggleClass('x-menu-item-checked');
27334 this.fireEvent('change', this, v, old);
27339 // handle setting of hidden value by some other method!!?!?
27340 setFromHidden: function()
27345 //console.log("SET FROM HIDDEN");
27346 //alert('setFrom hidden');
27347 this.setValue(this.el.dom.value);
27350 onDestroy : function()
27353 Roo.get(this.viewEl).remove();
27356 Roo.form.DayPicker.superclass.onDestroy.call(this);
27360 * RooJS Library 1.1.1
27361 * Copyright(c) 2008-2011 Alan Knowles
27368 * @class Roo.form.ComboCheck
27369 * @extends Roo.form.ComboBox
27370 * A combobox for multiple select items.
27372 * FIXME - could do with a reset button..
27375 * Create a new ComboCheck
27376 * @param {Object} config Configuration options
27378 Roo.form.ComboCheck = function(config){
27379 Roo.form.ComboCheck.superclass.constructor.call(this, config);
27380 // should verify some data...
27382 // hiddenName = required..
27383 // displayField = required
27384 // valudField == required
27385 var req= [ 'hiddenName', 'displayField', 'valueField' ];
27387 Roo.each(req, function(e) {
27388 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27389 throw "Roo.form.ComboCheck : missing value for: " + e;
27396 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27401 selectedClass: 'x-menu-item-checked',
27404 onRender : function(ct, position){
27410 var cls = 'x-combo-list';
27413 this.tpl = new Roo.Template({
27414 html : '<div class="'+cls+'-item x-menu-check-item">' +
27415 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
27416 '<span>{' + this.displayField + '}</span>' +
27423 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27424 this.view.singleSelect = false;
27425 this.view.multiSelect = true;
27426 this.view.toggleSelect = true;
27427 this.pageTb.add(new Roo.Toolbar.Fill(), {
27430 handler: function()
27437 onViewOver : function(e, t){
27443 onViewClick : function(doFocus,index){
27447 select: function () {
27448 //Roo.log("SELECT CALLED");
27451 selectByValue : function(xv, scrollIntoView){
27452 var ar = this.getValueArray();
27455 Roo.each(ar, function(v) {
27456 if(v === undefined || v === null){
27459 var r = this.findRecord(this.valueField, v);
27461 sels.push(this.store.indexOf(r))
27465 this.view.select(sels);
27471 onSelect : function(record, index){
27472 // Roo.log("onselect Called");
27473 // this is only called by the clear button now..
27474 this.view.clearSelections();
27475 this.setValue('[]');
27476 if (this.value != this.valueBefore) {
27477 this.fireEvent('change', this, this.value, this.valueBefore);
27478 this.valueBefore = this.value;
27481 getValueArray : function()
27486 //Roo.log(this.value);
27487 if (typeof(this.value) == 'undefined') {
27490 var ar = Roo.decode(this.value);
27491 return ar instanceof Array ? ar : []; //?? valid?
27494 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
27499 expand : function ()
27502 Roo.form.ComboCheck.superclass.expand.call(this);
27503 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27504 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27509 collapse : function(){
27510 Roo.form.ComboCheck.superclass.collapse.call(this);
27511 var sl = this.view.getSelectedIndexes();
27512 var st = this.store;
27516 Roo.each(sl, function(i) {
27518 nv.push(r.get(this.valueField));
27520 this.setValue(Roo.encode(nv));
27521 if (this.value != this.valueBefore) {
27523 this.fireEvent('change', this, this.value, this.valueBefore);
27524 this.valueBefore = this.value;
27529 setValue : function(v){
27533 var vals = this.getValueArray();
27535 Roo.each(vals, function(k) {
27536 var r = this.findRecord(this.valueField, k);
27538 tv.push(r.data[this.displayField]);
27539 }else if(this.valueNotFoundText !== undefined){
27540 tv.push( this.valueNotFoundText );
27545 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27546 this.hiddenField.value = v;
27552 * Ext JS Library 1.1.1
27553 * Copyright(c) 2006-2007, Ext JS, LLC.
27555 * Originally Released Under LGPL - original licence link has changed is not relivant.
27558 * <script type="text/javascript">
27562 * @class Roo.form.Signature
27563 * @extends Roo.form.Field
27567 * @param {Object} config Configuration options
27570 Roo.form.Signature = function(config){
27571 Roo.form.Signature.superclass.constructor.call(this, config);
27573 this.addEvents({// not in used??
27576 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27577 * @param {Roo.form.Signature} combo This combo box
27582 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27583 * @param {Roo.form.ComboBox} combo This combo box
27584 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27590 Roo.extend(Roo.form.Signature, Roo.form.Field, {
27592 * @cfg {Object} labels Label to use when rendering a form.
27596 * confirm : "Confirm"
27601 confirm : "Confirm"
27604 * @cfg {Number} width The signature panel width (defaults to 300)
27608 * @cfg {Number} height The signature panel height (defaults to 100)
27612 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27614 allowBlank : false,
27617 // {Object} signPanel The signature SVG panel element (defaults to {})
27619 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27620 isMouseDown : false,
27621 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27622 isConfirmed : false,
27623 // {String} signatureTmp SVG mapping string (defaults to empty string)
27627 defaultAutoCreate : { // modified by initCompnoent..
27633 onRender : function(ct, position){
27635 Roo.form.Signature.superclass.onRender.call(this, ct, position);
27637 this.wrap = this.el.wrap({
27638 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27641 this.createToolbar(this);
27642 this.signPanel = this.wrap.createChild({
27644 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27648 this.svgID = Roo.id();
27649 this.svgEl = this.signPanel.createChild({
27650 xmlns : 'http://www.w3.org/2000/svg',
27652 id : this.svgID + "-svg",
27654 height: this.height,
27655 viewBox: '0 0 '+this.width+' '+this.height,
27659 id: this.svgID + "-svg-r",
27661 height: this.height,
27666 id: this.svgID + "-svg-l",
27668 y1: (this.height*0.8), // start set the line in 80% of height
27669 x2: this.width, // end
27670 y2: (this.height*0.8), // end set the line in 80% of height
27672 'stroke-width': "1",
27673 'stroke-dasharray': "3",
27674 'shape-rendering': "crispEdges",
27675 'pointer-events': "none"
27679 id: this.svgID + "-svg-p",
27681 'stroke-width': "3",
27683 'pointer-events': 'none'
27688 this.svgBox = this.svgEl.dom.getScreenCTM();
27690 createSVG : function(){
27691 var svg = this.signPanel;
27692 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27695 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27696 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27697 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27698 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27699 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27700 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27701 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27704 isTouchEvent : function(e){
27705 return e.type.match(/^touch/);
27707 getCoords : function (e) {
27708 var pt = this.svgEl.dom.createSVGPoint();
27711 if (this.isTouchEvent(e)) {
27712 pt.x = e.targetTouches[0].clientX;
27713 pt.y = e.targetTouches[0].clientY;
27715 var a = this.svgEl.dom.getScreenCTM();
27716 var b = a.inverse();
27717 var mx = pt.matrixTransform(b);
27718 return mx.x + ',' + mx.y;
27720 //mouse event headler
27721 down : function (e) {
27722 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27723 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27725 this.isMouseDown = true;
27727 e.preventDefault();
27729 move : function (e) {
27730 if (this.isMouseDown) {
27731 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27732 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27735 e.preventDefault();
27737 up : function (e) {
27738 this.isMouseDown = false;
27739 var sp = this.signatureTmp.split(' ');
27742 if(!sp[sp.length-2].match(/^L/)){
27746 this.signatureTmp = sp.join(" ");
27749 if(this.getValue() != this.signatureTmp){
27750 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27751 this.isConfirmed = false;
27753 e.preventDefault();
27757 * Protected method that will not generally be called directly. It
27758 * is called when the editor creates its toolbar. Override this method if you need to
27759 * add custom toolbar buttons.
27760 * @param {HtmlEditor} editor
27762 createToolbar : function(editor){
27763 function btn(id, toggle, handler){
27764 var xid = fid + '-'+ id ;
27768 cls : 'x-btn-icon x-edit-'+id,
27769 enableToggle:toggle !== false,
27770 scope: editor, // was editor...
27771 handler:handler||editor.relayBtnCmd,
27772 clickEvent:'mousedown',
27773 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27779 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27783 cls : ' x-signature-btn x-signature-'+id,
27784 scope: editor, // was editor...
27785 handler: this.reset,
27786 clickEvent:'mousedown',
27787 text: this.labels.clear
27794 cls : ' x-signature-btn x-signature-'+id,
27795 scope: editor, // was editor...
27796 handler: this.confirmHandler,
27797 clickEvent:'mousedown',
27798 text: this.labels.confirm
27805 * when user is clicked confirm then show this image.....
27807 * @return {String} Image Data URI
27809 getImageDataURI : function(){
27810 var svg = this.svgEl.dom.parentNode.innerHTML;
27811 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27816 * @return {Boolean} this.isConfirmed
27818 getConfirmed : function(){
27819 return this.isConfirmed;
27823 * @return {Number} this.width
27825 getWidth : function(){
27830 * @return {Number} this.height
27832 getHeight : function(){
27833 return this.height;
27836 getSignature : function(){
27837 return this.signatureTmp;
27840 reset : function(){
27841 this.signatureTmp = '';
27842 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27843 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27844 this.isConfirmed = false;
27845 Roo.form.Signature.superclass.reset.call(this);
27847 setSignature : function(s){
27848 this.signatureTmp = s;
27849 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27850 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27852 this.isConfirmed = false;
27853 Roo.form.Signature.superclass.reset.call(this);
27856 // Roo.log(this.signPanel.dom.contentWindow.up())
27859 setConfirmed : function(){
27863 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27866 confirmHandler : function(){
27867 if(!this.getSignature()){
27871 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27872 this.setValue(this.getSignature());
27873 this.isConfirmed = true;
27875 this.fireEvent('confirm', this);
27878 // Subclasses should provide the validation implementation by overriding this
27879 validateValue : function(value){
27880 if(this.allowBlank){
27884 if(this.isConfirmed){
27891 * Ext JS Library 1.1.1
27892 * Copyright(c) 2006-2007, Ext JS, LLC.
27894 * Originally Released Under LGPL - original licence link has changed is not relivant.
27897 * <script type="text/javascript">
27902 * @class Roo.form.ComboBox
27903 * @extends Roo.form.TriggerField
27904 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27906 * Create a new ComboBox.
27907 * @param {Object} config Configuration options
27909 Roo.form.Select = function(config){
27910 Roo.form.Select.superclass.constructor.call(this, config);
27914 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27916 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27919 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27920 * rendering into an Roo.Editor, defaults to false)
27923 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27924 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27927 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27930 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27931 * the dropdown list (defaults to undefined, with no header element)
27935 * @cfg {String/Roo.Template} tpl The template to use to render the output
27939 defaultAutoCreate : {tag: "select" },
27941 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27943 listWidth: undefined,
27945 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27946 * mode = 'remote' or 'text' if mode = 'local')
27948 displayField: undefined,
27950 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27951 * mode = 'remote' or 'value' if mode = 'local').
27952 * Note: use of a valueField requires the user make a selection
27953 * in order for a value to be mapped.
27955 valueField: undefined,
27959 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27960 * field's data value (defaults to the underlying DOM element's name)
27962 hiddenName: undefined,
27964 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27968 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27970 selectedClass: 'x-combo-selected',
27972 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
27973 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27974 * which displays a downward arrow icon).
27976 triggerClass : 'x-form-arrow-trigger',
27978 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27982 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27983 * anchor positions (defaults to 'tl-bl')
27985 listAlign: 'tl-bl?',
27987 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27991 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
27992 * query specified by the allQuery config option (defaults to 'query')
27994 triggerAction: 'query',
27996 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27997 * (defaults to 4, does not apply if editable = false)
28001 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
28002 * delay (typeAheadDelay) if it matches a known value (defaults to false)
28006 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
28007 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
28011 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28012 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
28016 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
28017 * when editable = true (defaults to false)
28019 selectOnFocus:false,
28021 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28023 queryParam: 'query',
28025 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
28026 * when mode = 'remote' (defaults to 'Loading...')
28028 loadingText: 'Loading...',
28030 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28034 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28038 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28039 * traditional select (defaults to true)
28043 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28047 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28051 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28052 * listWidth has a higher value)
28056 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28057 * allow the user to set arbitrary text into the field (defaults to false)
28059 forceSelection:false,
28061 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28062 * if typeAhead = true (defaults to 250)
28064 typeAheadDelay : 250,
28066 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28067 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28069 valueNotFoundText : undefined,
28072 * @cfg {String} defaultValue The value displayed after loading the store.
28077 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28079 blockFocus : false,
28082 * @cfg {Boolean} disableClear Disable showing of clear button.
28084 disableClear : false,
28086 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
28088 alwaysQuery : false,
28094 // element that contains real text value.. (when hidden is used..)
28097 onRender : function(ct, position){
28098 Roo.form.Field.prototype.onRender.call(this, ct, position);
28101 this.store.on('beforeload', this.onBeforeLoad, this);
28102 this.store.on('load', this.onLoad, this);
28103 this.store.on('loadexception', this.onLoadException, this);
28104 this.store.load({});
28112 initEvents : function(){
28113 //Roo.form.ComboBox.superclass.initEvents.call(this);
28117 onDestroy : function(){
28120 this.store.un('beforeload', this.onBeforeLoad, this);
28121 this.store.un('load', this.onLoad, this);
28122 this.store.un('loadexception', this.onLoadException, this);
28124 //Roo.form.ComboBox.superclass.onDestroy.call(this);
28128 fireKey : function(e){
28129 if(e.isNavKeyPress() && !this.list.isVisible()){
28130 this.fireEvent("specialkey", this, e);
28135 onResize: function(w, h){
28143 * Allow or prevent the user from directly editing the field text. If false is passed,
28144 * the user will only be able to select from the items defined in the dropdown list. This method
28145 * is the runtime equivalent of setting the 'editable' config option at config time.
28146 * @param {Boolean} value True to allow the user to directly edit the field text
28148 setEditable : function(value){
28153 onBeforeLoad : function(){
28155 Roo.log("Select before load");
28158 this.innerList.update(this.loadingText ?
28159 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28160 //this.restrictHeight();
28161 this.selectedIndex = -1;
28165 onLoad : function(){
28168 var dom = this.el.dom;
28169 dom.innerHTML = '';
28170 var od = dom.ownerDocument;
28172 if (this.emptyText) {
28173 var op = od.createElement('option');
28174 op.setAttribute('value', '');
28175 op.innerHTML = String.format('{0}', this.emptyText);
28176 dom.appendChild(op);
28178 if(this.store.getCount() > 0){
28180 var vf = this.valueField;
28181 var df = this.displayField;
28182 this.store.data.each(function(r) {
28183 // which colmsn to use... testing - cdoe / title..
28184 var op = od.createElement('option');
28185 op.setAttribute('value', r.data[vf]);
28186 op.innerHTML = String.format('{0}', r.data[df]);
28187 dom.appendChild(op);
28189 if (typeof(this.defaultValue != 'undefined')) {
28190 this.setValue(this.defaultValue);
28195 //this.onEmptyResults();
28200 onLoadException : function()
28202 dom.innerHTML = '';
28204 Roo.log("Select on load exception");
28208 Roo.log(this.store.reader.jsonData);
28209 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28210 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28216 onTypeAhead : function(){
28221 onSelect : function(record, index){
28222 Roo.log('on select?');
28224 if(this.fireEvent('beforeselect', this, record, index) !== false){
28225 this.setFromData(index > -1 ? record.data : false);
28227 this.fireEvent('select', this, record, index);
28232 * Returns the currently selected field value or empty string if no value is set.
28233 * @return {String} value The selected value
28235 getValue : function(){
28236 var dom = this.el.dom;
28237 this.value = dom.options[dom.selectedIndex].value;
28243 * Clears any text/value currently set in the field
28245 clearValue : function(){
28247 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28252 * Sets the specified value into the field. If the value finds a match, the corresponding record text
28253 * will be displayed in the field. If the value does not match the data value of an existing item,
28254 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28255 * Otherwise the field will be blank (although the value will still be set).
28256 * @param {String} value The value to match
28258 setValue : function(v){
28259 var d = this.el.dom;
28260 for (var i =0; i < d.options.length;i++) {
28261 if (v == d.options[i].value) {
28262 d.selectedIndex = i;
28270 * @property {Object} the last set data for the element
28275 * Sets the value of the field based on a object which is related to the record format for the store.
28276 * @param {Object} value the value to set as. or false on reset?
28278 setFromData : function(o){
28279 Roo.log('setfrom data?');
28285 reset : function(){
28289 findRecord : function(prop, value){
28294 if(this.store.getCount() > 0){
28295 this.store.each(function(r){
28296 if(r.data[prop] == value){
28306 getName: function()
28308 // returns hidden if it's set..
28309 if (!this.rendered) {return ''};
28310 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
28318 onEmptyResults : function(){
28319 Roo.log('empty results');
28324 * Returns true if the dropdown list is expanded, else false.
28326 isExpanded : function(){
28331 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28332 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28333 * @param {String} value The data value of the item to select
28334 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28335 * selected item if it is not currently in view (defaults to true)
28336 * @return {Boolean} True if the value matched an item in the list, else false
28338 selectByValue : function(v, scrollIntoView){
28339 Roo.log('select By Value');
28342 if(v !== undefined && v !== null){
28343 var r = this.findRecord(this.valueField || this.displayField, v);
28345 this.select(this.store.indexOf(r), scrollIntoView);
28353 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28354 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28355 * @param {Number} index The zero-based index of the list item to select
28356 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28357 * selected item if it is not currently in view (defaults to true)
28359 select : function(index, scrollIntoView){
28360 Roo.log('select ');
28363 this.selectedIndex = index;
28364 this.view.select(index);
28365 if(scrollIntoView !== false){
28366 var el = this.view.getNode(index);
28368 this.innerList.scrollChildIntoView(el, false);
28376 validateBlur : function(){
28383 initQuery : function(){
28384 this.doQuery(this.getRawValue());
28388 doForce : function(){
28389 if(this.el.dom.value.length > 0){
28390 this.el.dom.value =
28391 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28397 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
28398 * query allowing the query action to be canceled if needed.
28399 * @param {String} query The SQL query to execute
28400 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28401 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
28402 * saved in the current store (defaults to false)
28404 doQuery : function(q, forceAll){
28406 Roo.log('doQuery?');
28407 if(q === undefined || q === null){
28412 forceAll: forceAll,
28416 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28420 forceAll = qe.forceAll;
28421 if(forceAll === true || (q.length >= this.minChars)){
28422 if(this.lastQuery != q || this.alwaysQuery){
28423 this.lastQuery = q;
28424 if(this.mode == 'local'){
28425 this.selectedIndex = -1;
28427 this.store.clearFilter();
28429 this.store.filter(this.displayField, q);
28433 this.store.baseParams[this.queryParam] = q;
28435 params: this.getParams(q)
28440 this.selectedIndex = -1;
28447 getParams : function(q){
28449 //p[this.queryParam] = q;
28452 p.limit = this.pageSize;
28458 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28460 collapse : function(){
28465 collapseIf : function(e){
28470 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28472 expand : function(){
28480 * @cfg {Boolean} grow
28484 * @cfg {Number} growMin
28488 * @cfg {Number} growMax
28496 setWidth : function()
28500 getResizeEl : function(){
28503 });//<script type="text/javasscript">
28507 * @class Roo.DDView
28508 * A DnD enabled version of Roo.View.
28509 * @param {Element/String} container The Element in which to create the View.
28510 * @param {String} tpl The template string used to create the markup for each element of the View
28511 * @param {Object} config The configuration properties. These include all the config options of
28512 * {@link Roo.View} plus some specific to this class.<br>
28514 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28515 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28517 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28518 .x-view-drag-insert-above {
28519 border-top:1px dotted #3366cc;
28521 .x-view-drag-insert-below {
28522 border-bottom:1px dotted #3366cc;
28528 Roo.DDView = function(container, tpl, config) {
28529 Roo.DDView.superclass.constructor.apply(this, arguments);
28530 this.getEl().setStyle("outline", "0px none");
28531 this.getEl().unselectable();
28532 if (this.dragGroup) {
28533 this.setDraggable(this.dragGroup.split(","));
28535 if (this.dropGroup) {
28536 this.setDroppable(this.dropGroup.split(","));
28538 if (this.deletable) {
28539 this.setDeletable();
28541 this.isDirtyFlag = false;
28547 Roo.extend(Roo.DDView, Roo.View, {
28548 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28549 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28550 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28551 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28555 reset: Roo.emptyFn,
28557 clearInvalid: Roo.form.Field.prototype.clearInvalid,
28559 validate: function() {
28563 destroy: function() {
28564 this.purgeListeners();
28565 this.getEl.removeAllListeners();
28566 this.getEl().remove();
28567 if (this.dragZone) {
28568 if (this.dragZone.destroy) {
28569 this.dragZone.destroy();
28572 if (this.dropZone) {
28573 if (this.dropZone.destroy) {
28574 this.dropZone.destroy();
28579 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28580 getName: function() {
28584 /** Loads the View from a JSON string representing the Records to put into the Store. */
28585 setValue: function(v) {
28587 throw "DDView.setValue(). DDView must be constructed with a valid Store";
28590 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28591 this.store.proxy = new Roo.data.MemoryProxy(data);
28595 /** @return {String} a parenthesised list of the ids of the Records in the View. */
28596 getValue: function() {
28598 this.store.each(function(rec) {
28599 result += rec.id + ',';
28601 return result.substr(0, result.length - 1) + ')';
28604 getIds: function() {
28605 var i = 0, result = new Array(this.store.getCount());
28606 this.store.each(function(rec) {
28607 result[i++] = rec.id;
28612 isDirty: function() {
28613 return this.isDirtyFlag;
28617 * Part of the Roo.dd.DropZone interface. If no target node is found, the
28618 * whole Element becomes the target, and this causes the drop gesture to append.
28620 getTargetFromEvent : function(e) {
28621 var target = e.getTarget();
28622 while ((target !== null) && (target.parentNode != this.el.dom)) {
28623 target = target.parentNode;
28626 target = this.el.dom.lastChild || this.el.dom;
28632 * Create the drag data which consists of an object which has the property "ddel" as
28633 * the drag proxy element.
28635 getDragData : function(e) {
28636 var target = this.findItemFromChild(e.getTarget());
28638 this.handleSelection(e);
28639 var selNodes = this.getSelectedNodes();
28642 copy: this.copy || (this.allowCopy && e.ctrlKey),
28646 var selectedIndices = this.getSelectedIndexes();
28647 for (var i = 0; i < selectedIndices.length; i++) {
28648 dragData.records.push(this.store.getAt(selectedIndices[i]));
28650 if (selNodes.length == 1) {
28651 dragData.ddel = target.cloneNode(true); // the div element
28653 var div = document.createElement('div'); // create the multi element drag "ghost"
28654 div.className = 'multi-proxy';
28655 for (var i = 0, len = selNodes.length; i < len; i++) {
28656 div.appendChild(selNodes[i].cloneNode(true));
28658 dragData.ddel = div;
28660 //console.log(dragData)
28661 //console.log(dragData.ddel.innerHTML)
28664 //console.log('nodragData')
28668 /** Specify to which ddGroup items in this DDView may be dragged. */
28669 setDraggable: function(ddGroup) {
28670 if (ddGroup instanceof Array) {
28671 Roo.each(ddGroup, this.setDraggable, this);
28674 if (this.dragZone) {
28675 this.dragZone.addToGroup(ddGroup);
28677 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28678 containerScroll: true,
28682 // Draggability implies selection. DragZone's mousedown selects the element.
28683 if (!this.multiSelect) { this.singleSelect = true; }
28685 // Wire the DragZone's handlers up to methods in *this*
28686 this.dragZone.getDragData = this.getDragData.createDelegate(this);
28690 /** Specify from which ddGroup this DDView accepts drops. */
28691 setDroppable: function(ddGroup) {
28692 if (ddGroup instanceof Array) {
28693 Roo.each(ddGroup, this.setDroppable, this);
28696 if (this.dropZone) {
28697 this.dropZone.addToGroup(ddGroup);
28699 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28700 containerScroll: true,
28704 // Wire the DropZone's handlers up to methods in *this*
28705 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28706 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28707 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28708 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28709 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28713 /** Decide whether to drop above or below a View node. */
28714 getDropPoint : function(e, n, dd){
28715 if (n == this.el.dom) { return "above"; }
28716 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28717 var c = t + (b - t) / 2;
28718 var y = Roo.lib.Event.getPageY(e);
28726 onNodeEnter : function(n, dd, e, data){
28730 onNodeOver : function(n, dd, e, data){
28731 var pt = this.getDropPoint(e, n, dd);
28732 // set the insert point style on the target node
28733 var dragElClass = this.dropNotAllowed;
28736 if (pt == "above"){
28737 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28738 targetElClass = "x-view-drag-insert-above";
28740 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28741 targetElClass = "x-view-drag-insert-below";
28743 if (this.lastInsertClass != targetElClass){
28744 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28745 this.lastInsertClass = targetElClass;
28748 return dragElClass;
28751 onNodeOut : function(n, dd, e, data){
28752 this.removeDropIndicators(n);
28755 onNodeDrop : function(n, dd, e, data){
28756 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28759 var pt = this.getDropPoint(e, n, dd);
28760 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28761 if (pt == "below") { insertAt++; }
28762 for (var i = 0; i < data.records.length; i++) {
28763 var r = data.records[i];
28764 var dup = this.store.getById(r.id);
28765 if (dup && (dd != this.dragZone)) {
28766 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28769 this.store.insert(insertAt++, r.copy());
28771 data.source.isDirtyFlag = true;
28773 this.store.insert(insertAt++, r);
28775 this.isDirtyFlag = true;
28778 this.dragZone.cachedTarget = null;
28782 removeDropIndicators : function(n){
28784 Roo.fly(n).removeClass([
28785 "x-view-drag-insert-above",
28786 "x-view-drag-insert-below"]);
28787 this.lastInsertClass = "_noclass";
28792 * Utility method. Add a delete option to the DDView's context menu.
28793 * @param {String} imageUrl The URL of the "delete" icon image.
28795 setDeletable: function(imageUrl) {
28796 if (!this.singleSelect && !this.multiSelect) {
28797 this.singleSelect = true;
28799 var c = this.getContextMenu();
28800 this.contextMenu.on("itemclick", function(item) {
28803 this.remove(this.getSelectedIndexes());
28807 this.contextMenu.add({
28814 /** Return the context menu for this DDView. */
28815 getContextMenu: function() {
28816 if (!this.contextMenu) {
28817 // Create the View's context menu
28818 this.contextMenu = new Roo.menu.Menu({
28819 id: this.id + "-contextmenu"
28821 this.el.on("contextmenu", this.showContextMenu, this);
28823 return this.contextMenu;
28826 disableContextMenu: function() {
28827 if (this.contextMenu) {
28828 this.el.un("contextmenu", this.showContextMenu, this);
28832 showContextMenu: function(e, item) {
28833 item = this.findItemFromChild(e.getTarget());
28836 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28837 this.contextMenu.showAt(e.getXY());
28842 * Remove {@link Roo.data.Record}s at the specified indices.
28843 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28845 remove: function(selectedIndices) {
28846 selectedIndices = [].concat(selectedIndices);
28847 for (var i = 0; i < selectedIndices.length; i++) {
28848 var rec = this.store.getAt(selectedIndices[i]);
28849 this.store.remove(rec);
28854 * Double click fires the event, but also, if this is draggable, and there is only one other
28855 * related DropZone, it transfers the selected node.
28857 onDblClick : function(e){
28858 var item = this.findItemFromChild(e.getTarget());
28860 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28863 if (this.dragGroup) {
28864 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28865 while (targets.indexOf(this.dropZone) > -1) {
28866 targets.remove(this.dropZone);
28868 if (targets.length == 1) {
28869 this.dragZone.cachedTarget = null;
28870 var el = Roo.get(targets[0].getEl());
28871 var box = el.getBox(true);
28872 targets[0].onNodeDrop(el.dom, {
28874 xy: [box.x, box.y + box.height - 1]
28875 }, null, this.getDragData(e));
28881 handleSelection: function(e) {
28882 this.dragZone.cachedTarget = null;
28883 var item = this.findItemFromChild(e.getTarget());
28885 this.clearSelections(true);
28888 if (item && (this.multiSelect || this.singleSelect)){
28889 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28890 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28891 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28892 this.unselect(item);
28894 this.select(item, this.multiSelect && e.ctrlKey);
28895 this.lastSelection = item;
28900 onItemClick : function(item, index, e){
28901 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28907 unselect : function(nodeInfo, suppressEvent){
28908 var node = this.getNode(nodeInfo);
28909 if(node && this.isSelected(node)){
28910 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28911 Roo.fly(node).removeClass(this.selectedClass);
28912 this.selections.remove(node);
28913 if(!suppressEvent){
28914 this.fireEvent("selectionchange", this, this.selections);
28922 * Ext JS Library 1.1.1
28923 * Copyright(c) 2006-2007, Ext JS, LLC.
28925 * Originally Released Under LGPL - original licence link has changed is not relivant.
28928 * <script type="text/javascript">
28932 * @class Roo.LayoutManager
28933 * @extends Roo.util.Observable
28934 * Base class for layout managers.
28936 Roo.LayoutManager = function(container, config){
28937 Roo.LayoutManager.superclass.constructor.call(this);
28938 this.el = Roo.get(container);
28939 // ie scrollbar fix
28940 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28941 document.body.scroll = "no";
28942 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28943 this.el.position('relative');
28945 this.id = this.el.id;
28946 this.el.addClass("x-layout-container");
28947 /** false to disable window resize monitoring @type Boolean */
28948 this.monitorWindowResize = true;
28953 * Fires when a layout is performed.
28954 * @param {Roo.LayoutManager} this
28958 * @event regionresized
28959 * Fires when the user resizes a region.
28960 * @param {Roo.LayoutRegion} region The resized region
28961 * @param {Number} newSize The new size (width for east/west, height for north/south)
28963 "regionresized" : true,
28965 * @event regioncollapsed
28966 * Fires when a region is collapsed.
28967 * @param {Roo.LayoutRegion} region The collapsed region
28969 "regioncollapsed" : true,
28971 * @event regionexpanded
28972 * Fires when a region is expanded.
28973 * @param {Roo.LayoutRegion} region The expanded region
28975 "regionexpanded" : true
28977 this.updating = false;
28978 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28981 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28983 * Returns true if this layout is currently being updated
28984 * @return {Boolean}
28986 isUpdating : function(){
28987 return this.updating;
28991 * Suspend the LayoutManager from doing auto-layouts while
28992 * making multiple add or remove calls
28994 beginUpdate : function(){
28995 this.updating = true;
28999 * Restore auto-layouts and optionally disable the manager from performing a layout
29000 * @param {Boolean} noLayout true to disable a layout update
29002 endUpdate : function(noLayout){
29003 this.updating = false;
29009 layout: function(){
29013 onRegionResized : function(region, newSize){
29014 this.fireEvent("regionresized", region, newSize);
29018 onRegionCollapsed : function(region){
29019 this.fireEvent("regioncollapsed", region);
29022 onRegionExpanded : function(region){
29023 this.fireEvent("regionexpanded", region);
29027 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29028 * performs box-model adjustments.
29029 * @return {Object} The size as an object {width: (the width), height: (the height)}
29031 getViewSize : function(){
29033 if(this.el.dom != document.body){
29034 size = this.el.getSize();
29036 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29038 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29039 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29044 * Returns the Element this layout is bound to.
29045 * @return {Roo.Element}
29047 getEl : function(){
29052 * Returns the specified region.
29053 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29054 * @return {Roo.LayoutRegion}
29056 getRegion : function(target){
29057 return this.regions[target.toLowerCase()];
29060 onWindowResize : function(){
29061 if(this.monitorWindowResize){
29067 * Ext JS Library 1.1.1
29068 * Copyright(c) 2006-2007, Ext JS, LLC.
29070 * Originally Released Under LGPL - original licence link has changed is not relivant.
29073 * <script type="text/javascript">
29076 * @class Roo.BorderLayout
29077 * @extends Roo.LayoutManager
29078 * @children Roo.ContentPanel
29079 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29080 * please see: <br><br>
29081 * <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>
29082 * <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>
29085 var layout = new Roo.BorderLayout(document.body, {
29119 preferredTabWidth: 150
29124 var CP = Roo.ContentPanel;
29126 layout.beginUpdate();
29127 layout.add("north", new CP("north", "North"));
29128 layout.add("south", new CP("south", {title: "South", closable: true}));
29129 layout.add("west", new CP("west", {title: "West"}));
29130 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29131 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29132 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29133 layout.getRegion("center").showPanel("center1");
29134 layout.endUpdate();
29137 <b>The container the layout is rendered into can be either the body element or any other element.
29138 If it is not the body element, the container needs to either be an absolute positioned element,
29139 or you will need to add "position:relative" to the css of the container. You will also need to specify
29140 the container size if it is not the body element.</b>
29143 * Create a new BorderLayout
29144 * @param {String/HTMLElement/Element} container The container this layout is bound to
29145 * @param {Object} config Configuration options
29147 Roo.BorderLayout = function(container, config){
29148 config = config || {};
29149 Roo.BorderLayout.superclass.constructor.call(this, container, config);
29150 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29151 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29152 var target = this.factory.validRegions[i];
29153 if(config[target]){
29154 this.addRegion(target, config[target]);
29159 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29162 * @cfg {Roo.LayoutRegion} east
29165 * @cfg {Roo.LayoutRegion} west
29168 * @cfg {Roo.LayoutRegion} north
29171 * @cfg {Roo.LayoutRegion} south
29174 * @cfg {Roo.LayoutRegion} center
29177 * Creates and adds a new region if it doesn't already exist.
29178 * @param {String} target The target region key (north, south, east, west or center).
29179 * @param {Object} config The regions config object
29180 * @return {BorderLayoutRegion} The new region
29182 addRegion : function(target, config){
29183 if(!this.regions[target]){
29184 var r = this.factory.create(target, this, config);
29185 this.bindRegion(target, r);
29187 return this.regions[target];
29191 bindRegion : function(name, r){
29192 this.regions[name] = r;
29193 r.on("visibilitychange", this.layout, this);
29194 r.on("paneladded", this.layout, this);
29195 r.on("panelremoved", this.layout, this);
29196 r.on("invalidated", this.layout, this);
29197 r.on("resized", this.onRegionResized, this);
29198 r.on("collapsed", this.onRegionCollapsed, this);
29199 r.on("expanded", this.onRegionExpanded, this);
29203 * Performs a layout update.
29205 layout : function(){
29206 if(this.updating) {
29209 var size = this.getViewSize();
29210 var w = size.width;
29211 var h = size.height;
29216 //var x = 0, y = 0;
29218 var rs = this.regions;
29219 var north = rs["north"];
29220 var south = rs["south"];
29221 var west = rs["west"];
29222 var east = rs["east"];
29223 var center = rs["center"];
29224 //if(this.hideOnLayout){ // not supported anymore
29225 //c.el.setStyle("display", "none");
29227 if(north && north.isVisible()){
29228 var b = north.getBox();
29229 var m = north.getMargins();
29230 b.width = w - (m.left+m.right);
29233 centerY = b.height + b.y + m.bottom;
29234 centerH -= centerY;
29235 north.updateBox(this.safeBox(b));
29237 if(south && south.isVisible()){
29238 var b = south.getBox();
29239 var m = south.getMargins();
29240 b.width = w - (m.left+m.right);
29242 var totalHeight = (b.height + m.top + m.bottom);
29243 b.y = h - totalHeight + m.top;
29244 centerH -= totalHeight;
29245 south.updateBox(this.safeBox(b));
29247 if(west && west.isVisible()){
29248 var b = west.getBox();
29249 var m = west.getMargins();
29250 b.height = centerH - (m.top+m.bottom);
29252 b.y = centerY + m.top;
29253 var totalWidth = (b.width + m.left + m.right);
29254 centerX += totalWidth;
29255 centerW -= totalWidth;
29256 west.updateBox(this.safeBox(b));
29258 if(east && east.isVisible()){
29259 var b = east.getBox();
29260 var m = east.getMargins();
29261 b.height = centerH - (m.top+m.bottom);
29262 var totalWidth = (b.width + m.left + m.right);
29263 b.x = w - totalWidth + m.left;
29264 b.y = centerY + m.top;
29265 centerW -= totalWidth;
29266 east.updateBox(this.safeBox(b));
29269 var m = center.getMargins();
29271 x: centerX + m.left,
29272 y: centerY + m.top,
29273 width: centerW - (m.left+m.right),
29274 height: centerH - (m.top+m.bottom)
29276 //if(this.hideOnLayout){
29277 //center.el.setStyle("display", "block");
29279 center.updateBox(this.safeBox(centerBox));
29282 this.fireEvent("layout", this);
29286 safeBox : function(box){
29287 box.width = Math.max(0, box.width);
29288 box.height = Math.max(0, box.height);
29293 * Adds a ContentPanel (or subclass) to this layout.
29294 * @param {String} target The target region key (north, south, east, west or center).
29295 * @param {Roo.ContentPanel} panel The panel to add
29296 * @return {Roo.ContentPanel} The added panel
29298 add : function(target, panel){
29300 target = target.toLowerCase();
29301 return this.regions[target].add(panel);
29305 * Remove a ContentPanel (or subclass) to this layout.
29306 * @param {String} target The target region key (north, south, east, west or center).
29307 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29308 * @return {Roo.ContentPanel} The removed panel
29310 remove : function(target, panel){
29311 target = target.toLowerCase();
29312 return this.regions[target].remove(panel);
29316 * Searches all regions for a panel with the specified id
29317 * @param {String} panelId
29318 * @return {Roo.ContentPanel} The panel or null if it wasn't found
29320 findPanel : function(panelId){
29321 var rs = this.regions;
29322 for(var target in rs){
29323 if(typeof rs[target] != "function"){
29324 var p = rs[target].getPanel(panelId);
29334 * Searches all regions for a panel with the specified id and activates (shows) it.
29335 * @param {String/ContentPanel} panelId The panels id or the panel itself
29336 * @return {Roo.ContentPanel} The shown panel or null
29338 showPanel : function(panelId) {
29339 var rs = this.regions;
29340 for(var target in rs){
29341 var r = rs[target];
29342 if(typeof r != "function"){
29343 if(r.hasPanel(panelId)){
29344 return r.showPanel(panelId);
29352 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29353 * @param {Roo.state.Provider} provider (optional) An alternate state provider
29355 restoreState : function(provider){
29357 provider = Roo.state.Manager;
29359 var sm = new Roo.LayoutStateManager();
29360 sm.init(this, provider);
29364 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
29365 * object should contain properties for each region to add ContentPanels to, and each property's value should be
29366 * a valid ContentPanel config object. Example:
29368 // Create the main layout
29369 var layout = new Roo.BorderLayout('main-ct', {
29380 // Create and add multiple ContentPanels at once via configs
29383 id: 'source-files',
29385 title:'Ext Source Files',
29398 * @param {Object} regions An object containing ContentPanel configs by region name
29400 batchAdd : function(regions){
29401 this.beginUpdate();
29402 for(var rname in regions){
29403 var lr = this.regions[rname];
29405 this.addTypedPanels(lr, regions[rname]);
29412 addTypedPanels : function(lr, ps){
29413 if(typeof ps == 'string'){
29414 lr.add(new Roo.ContentPanel(ps));
29416 else if(ps instanceof Array){
29417 for(var i =0, len = ps.length; i < len; i++){
29418 this.addTypedPanels(lr, ps[i]);
29421 else if(!ps.events){ // raw config?
29423 delete ps.el; // prevent conflict
29424 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29426 else { // panel object assumed!
29431 * Adds a xtype elements to the layout.
29435 xtype : 'ContentPanel',
29442 xtype : 'NestedLayoutPanel',
29448 items : [ ... list of content panels or nested layout panels.. ]
29452 * @param {Object} cfg Xtype definition of item to add.
29454 addxtype : function(cfg)
29456 // basically accepts a pannel...
29457 // can accept a layout region..!?!?
29458 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29460 if (!cfg.xtype.match(/Panel$/)) {
29465 if (typeof(cfg.region) == 'undefined') {
29466 Roo.log("Failed to add Panel, region was not set");
29470 var region = cfg.region;
29476 xitems = cfg.items;
29483 case 'ContentPanel': // ContentPanel (el, cfg)
29484 case 'ScrollPanel': // ContentPanel (el, cfg)
29486 if(cfg.autoCreate) {
29487 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29489 var el = this.el.createChild();
29490 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29493 this.add(region, ret);
29497 case 'TreePanel': // our new panel!
29498 cfg.el = this.el.createChild();
29499 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29500 this.add(region, ret);
29503 case 'NestedLayoutPanel':
29504 // create a new Layout (which is a Border Layout...
29505 var el = this.el.createChild();
29506 var clayout = cfg.layout;
29508 clayout.items = clayout.items || [];
29509 // replace this exitems with the clayout ones..
29510 xitems = clayout.items;
29513 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29514 cfg.background = false;
29516 var layout = new Roo.BorderLayout(el, clayout);
29518 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29519 //console.log('adding nested layout panel ' + cfg.toSource());
29520 this.add(region, ret);
29521 nb = {}; /// find first...
29526 // needs grid and region
29528 //var el = this.getRegion(region).el.createChild();
29529 var el = this.el.createChild();
29530 // create the grid first...
29532 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29534 if (region == 'center' && this.active ) {
29535 cfg.background = false;
29537 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29539 this.add(region, ret);
29540 if (cfg.background) {
29541 ret.on('activate', function(gp) {
29542 if (!gp.grid.rendered) {
29557 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29559 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29560 this.add(region, ret);
29563 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29567 // GridPanel (grid, cfg)
29570 this.beginUpdate();
29574 Roo.each(xitems, function(i) {
29575 region = nb && i.region ? i.region : false;
29577 var add = ret.addxtype(i);
29580 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29581 if (!i.background) {
29582 abn[region] = nb[region] ;
29589 // make the last non-background panel active..
29590 //if (nb) { Roo.log(abn); }
29593 for(var r in abn) {
29594 region = this.getRegion(r);
29596 // tried using nb[r], but it does not work..
29598 region.showPanel(abn[r]);
29609 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29610 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
29611 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29612 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
29615 var CP = Roo.ContentPanel;
29617 var layout = Roo.BorderLayout.create({
29621 panels: [new CP("north", "North")]
29630 panels: [new CP("west", {title: "West"})]
29639 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29648 panels: [new CP("south", {title: "South", closable: true})]
29655 preferredTabWidth: 150,
29657 new CP("center1", {title: "Close Me", closable: true}),
29658 new CP("center2", {title: "Center Panel", closable: false})
29663 layout.getRegion("center").showPanel("center1");
29668 Roo.BorderLayout.create = function(config, targetEl){
29669 var layout = new Roo.BorderLayout(targetEl || document.body, config);
29670 layout.beginUpdate();
29671 var regions = Roo.BorderLayout.RegionFactory.validRegions;
29672 for(var j = 0, jlen = regions.length; j < jlen; j++){
29673 var lr = regions[j];
29674 if(layout.regions[lr] && config[lr].panels){
29675 var r = layout.regions[lr];
29676 var ps = config[lr].panels;
29677 layout.addTypedPanels(r, ps);
29680 layout.endUpdate();
29685 Roo.BorderLayout.RegionFactory = {
29687 validRegions : ["north","south","east","west","center"],
29690 create : function(target, mgr, config){
29691 target = target.toLowerCase();
29692 if(config.lightweight || config.basic){
29693 return new Roo.BasicLayoutRegion(mgr, config, target);
29697 return new Roo.NorthLayoutRegion(mgr, config);
29699 return new Roo.SouthLayoutRegion(mgr, config);
29701 return new Roo.EastLayoutRegion(mgr, config);
29703 return new Roo.WestLayoutRegion(mgr, config);
29705 return new Roo.CenterLayoutRegion(mgr, config);
29707 throw 'Layout region "'+target+'" not supported.';
29711 * Ext JS Library 1.1.1
29712 * Copyright(c) 2006-2007, Ext JS, LLC.
29714 * Originally Released Under LGPL - original licence link has changed is not relivant.
29717 * <script type="text/javascript">
29721 * @class Roo.BasicLayoutRegion
29722 * @extends Roo.util.Observable
29723 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29724 * and does not have a titlebar, tabs or any other features. All it does is size and position
29725 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29727 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29729 this.position = pos;
29732 * @scope Roo.BasicLayoutRegion
29736 * @event beforeremove
29737 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29738 * @param {Roo.LayoutRegion} this
29739 * @param {Roo.ContentPanel} panel The panel
29740 * @param {Object} e The cancel event object
29742 "beforeremove" : true,
29744 * @event invalidated
29745 * Fires when the layout for this region is changed.
29746 * @param {Roo.LayoutRegion} this
29748 "invalidated" : true,
29750 * @event visibilitychange
29751 * Fires when this region is shown or hidden
29752 * @param {Roo.LayoutRegion} this
29753 * @param {Boolean} visibility true or false
29755 "visibilitychange" : true,
29757 * @event paneladded
29758 * Fires when a panel is added.
29759 * @param {Roo.LayoutRegion} this
29760 * @param {Roo.ContentPanel} panel The panel
29762 "paneladded" : true,
29764 * @event panelremoved
29765 * Fires when a panel is removed.
29766 * @param {Roo.LayoutRegion} this
29767 * @param {Roo.ContentPanel} panel The panel
29769 "panelremoved" : true,
29771 * @event beforecollapse
29772 * Fires when this region before collapse.
29773 * @param {Roo.LayoutRegion} this
29775 "beforecollapse" : true,
29778 * Fires when this region is collapsed.
29779 * @param {Roo.LayoutRegion} this
29781 "collapsed" : true,
29784 * Fires when this region is expanded.
29785 * @param {Roo.LayoutRegion} this
29790 * Fires when this region is slid into view.
29791 * @param {Roo.LayoutRegion} this
29793 "slideshow" : true,
29796 * Fires when this region slides out of view.
29797 * @param {Roo.LayoutRegion} this
29799 "slidehide" : true,
29801 * @event panelactivated
29802 * Fires when a panel is activated.
29803 * @param {Roo.LayoutRegion} this
29804 * @param {Roo.ContentPanel} panel The activated panel
29806 "panelactivated" : true,
29809 * Fires when the user resizes this region.
29810 * @param {Roo.LayoutRegion} this
29811 * @param {Number} newSize The new size (width for east/west, height for north/south)
29815 /** A collection of panels in this region. @type Roo.util.MixedCollection */
29816 this.panels = new Roo.util.MixedCollection();
29817 this.panels.getKey = this.getPanelId.createDelegate(this);
29819 this.activePanel = null;
29820 // ensure listeners are added...
29822 if (config.listeners || config.events) {
29823 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29824 listeners : config.listeners || {},
29825 events : config.events || {}
29829 if(skipConfig !== true){
29830 this.applyConfig(config);
29834 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29835 getPanelId : function(p){
29839 applyConfig : function(config){
29840 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29841 this.config = config;
29846 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
29847 * the width, for horizontal (north, south) the height.
29848 * @param {Number} newSize The new width or height
29850 resizeTo : function(newSize){
29851 var el = this.el ? this.el :
29852 (this.activePanel ? this.activePanel.getEl() : null);
29854 switch(this.position){
29857 el.setWidth(newSize);
29858 this.fireEvent("resized", this, newSize);
29862 el.setHeight(newSize);
29863 this.fireEvent("resized", this, newSize);
29869 getBox : function(){
29870 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29873 getMargins : function(){
29874 return this.margins;
29877 updateBox : function(box){
29879 var el = this.activePanel.getEl();
29880 el.dom.style.left = box.x + "px";
29881 el.dom.style.top = box.y + "px";
29882 this.activePanel.setSize(box.width, box.height);
29886 * Returns the container element for this region.
29887 * @return {Roo.Element}
29889 getEl : function(){
29890 return this.activePanel;
29894 * Returns true if this region is currently visible.
29895 * @return {Boolean}
29897 isVisible : function(){
29898 return this.activePanel ? true : false;
29901 setActivePanel : function(panel){
29902 panel = this.getPanel(panel);
29903 if(this.activePanel && this.activePanel != panel){
29904 this.activePanel.setActiveState(false);
29905 this.activePanel.getEl().setLeftTop(-10000,-10000);
29907 this.activePanel = panel;
29908 panel.setActiveState(true);
29910 panel.setSize(this.box.width, this.box.height);
29912 this.fireEvent("panelactivated", this, panel);
29913 this.fireEvent("invalidated");
29917 * Show the specified panel.
29918 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29919 * @return {Roo.ContentPanel} The shown panel or null
29921 showPanel : function(panel){
29922 if(panel = this.getPanel(panel)){
29923 this.setActivePanel(panel);
29929 * Get the active panel for this region.
29930 * @return {Roo.ContentPanel} The active panel or null
29932 getActivePanel : function(){
29933 return this.activePanel;
29937 * Add the passed ContentPanel(s)
29938 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29939 * @return {Roo.ContentPanel} The panel added (if only one was added)
29941 add : function(panel){
29942 if(arguments.length > 1){
29943 for(var i = 0, len = arguments.length; i < len; i++) {
29944 this.add(arguments[i]);
29948 if(this.hasPanel(panel)){
29949 this.showPanel(panel);
29952 var el = panel.getEl();
29953 if(el.dom.parentNode != this.mgr.el.dom){
29954 this.mgr.el.dom.appendChild(el.dom);
29956 if(panel.setRegion){
29957 panel.setRegion(this);
29959 this.panels.add(panel);
29960 el.setStyle("position", "absolute");
29961 if(!panel.background){
29962 this.setActivePanel(panel);
29963 if(this.config.initialSize && this.panels.getCount()==1){
29964 this.resizeTo(this.config.initialSize);
29967 this.fireEvent("paneladded", this, panel);
29972 * Returns true if the panel is in this region.
29973 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29974 * @return {Boolean}
29976 hasPanel : function(panel){
29977 if(typeof panel == "object"){ // must be panel obj
29978 panel = panel.getId();
29980 return this.getPanel(panel) ? true : false;
29984 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29985 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29986 * @param {Boolean} preservePanel Overrides the config preservePanel option
29987 * @return {Roo.ContentPanel} The panel that was removed
29989 remove : function(panel, preservePanel){
29990 panel = this.getPanel(panel);
29995 this.fireEvent("beforeremove", this, panel, e);
29996 if(e.cancel === true){
29999 var panelId = panel.getId();
30000 this.panels.removeKey(panelId);
30005 * Returns the panel specified or null if it's not in this region.
30006 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30007 * @return {Roo.ContentPanel}
30009 getPanel : function(id){
30010 if(typeof id == "object"){ // must be panel obj
30013 return this.panels.get(id);
30017 * Returns this regions position (north/south/east/west/center).
30020 getPosition: function(){
30021 return this.position;
30025 * Ext JS Library 1.1.1
30026 * Copyright(c) 2006-2007, Ext JS, LLC.
30028 * Originally Released Under LGPL - original licence link has changed is not relivant.
30031 * <script type="text/javascript">
30035 * @class Roo.LayoutRegion
30036 * @extends Roo.BasicLayoutRegion
30037 * This class represents a region in a layout manager.
30038 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
30039 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
30040 * @cfg {Boolean} floatable False to disable floating (defaults to true)
30041 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30042 * @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})
30043 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
30044 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
30045 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
30046 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
30047 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
30048 * @cfg {String} title The title for the region (overrides panel titles)
30049 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
30050 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30051 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
30052 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30053 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
30054 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30055 * the space available, similar to FireFox 1.5 tabs (defaults to false)
30056 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
30057 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
30058 * @cfg {Boolean} showPin True to show a pin button
30059 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
30060 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
30061 * @cfg {Boolean} disableTabTips True to disable tab tooltips
30062 * @cfg {Number} width For East/West panels
30063 * @cfg {Number} height For North/South panels
30064 * @cfg {Boolean} split To show the splitter
30065 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
30067 Roo.LayoutRegion = function(mgr, config, pos){
30068 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30069 var dh = Roo.DomHelper;
30070 /** This region's container element
30071 * @type Roo.Element */
30072 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30073 /** This region's title element
30074 * @type Roo.Element */
30076 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30077 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
30078 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30080 this.titleEl.enableDisplayMode();
30081 /** This region's title text element
30082 * @type HTMLElement */
30083 this.titleTextEl = this.titleEl.dom.firstChild;
30084 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30085 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30086 this.closeBtn.enableDisplayMode();
30087 this.closeBtn.on("click", this.closeClicked, this);
30088 this.closeBtn.hide();
30090 this.createBody(config);
30091 this.visible = true;
30092 this.collapsed = false;
30094 if(config.hideWhenEmpty){
30096 this.on("paneladded", this.validateVisibility, this);
30097 this.on("panelremoved", this.validateVisibility, this);
30099 this.applyConfig(config);
30102 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30104 createBody : function(){
30105 /** This region's body element
30106 * @type Roo.Element */
30107 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30110 applyConfig : function(c){
30111 if(c.collapsible && this.position != "center" && !this.collapsedEl){
30112 var dh = Roo.DomHelper;
30113 if(c.titlebar !== false){
30114 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30115 this.collapseBtn.on("click", this.collapse, this);
30116 this.collapseBtn.enableDisplayMode();
30118 if(c.showPin === true || this.showPin){
30119 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30120 this.stickBtn.enableDisplayMode();
30121 this.stickBtn.on("click", this.expand, this);
30122 this.stickBtn.hide();
30125 /** This region's collapsed element
30126 * @type Roo.Element */
30127 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30128 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30130 if(c.floatable !== false){
30131 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30132 this.collapsedEl.on("click", this.collapseClick, this);
30135 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30136 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30137 id: "message", unselectable: "on", style:{"float":"left"}});
30138 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30140 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30141 this.expandBtn.on("click", this.expand, this);
30143 if(this.collapseBtn){
30144 this.collapseBtn.setVisible(c.collapsible == true);
30146 this.cmargins = c.cmargins || this.cmargins ||
30147 (this.position == "west" || this.position == "east" ?
30148 {top: 0, left: 2, right:2, bottom: 0} :
30149 {top: 2, left: 0, right:0, bottom: 2});
30150 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30151 this.bottomTabs = c.tabPosition != "top";
30152 this.autoScroll = c.autoScroll || false;
30153 if(this.autoScroll){
30154 this.bodyEl.setStyle("overflow", "auto");
30156 this.bodyEl.setStyle("overflow", "hidden");
30158 //if(c.titlebar !== false){
30159 if((!c.titlebar && !c.title) || c.titlebar === false){
30160 this.titleEl.hide();
30162 this.titleEl.show();
30164 this.titleTextEl.innerHTML = c.title;
30168 this.duration = c.duration || .30;
30169 this.slideDuration = c.slideDuration || .45;
30172 this.collapse(true);
30179 * Returns true if this region is currently visible.
30180 * @return {Boolean}
30182 isVisible : function(){
30183 return this.visible;
30187 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30188 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
30190 setCollapsedTitle : function(title){
30191 title = title || " ";
30192 if(this.collapsedTitleTextEl){
30193 this.collapsedTitleTextEl.innerHTML = title;
30197 getBox : function(){
30199 if(!this.collapsed){
30200 b = this.el.getBox(false, true);
30202 b = this.collapsedEl.getBox(false, true);
30207 getMargins : function(){
30208 return this.collapsed ? this.cmargins : this.margins;
30211 highlight : function(){
30212 this.el.addClass("x-layout-panel-dragover");
30215 unhighlight : function(){
30216 this.el.removeClass("x-layout-panel-dragover");
30219 updateBox : function(box){
30221 if(!this.collapsed){
30222 this.el.dom.style.left = box.x + "px";
30223 this.el.dom.style.top = box.y + "px";
30224 this.updateBody(box.width, box.height);
30226 this.collapsedEl.dom.style.left = box.x + "px";
30227 this.collapsedEl.dom.style.top = box.y + "px";
30228 this.collapsedEl.setSize(box.width, box.height);
30231 this.tabs.autoSizeTabs();
30235 updateBody : function(w, h){
30237 this.el.setWidth(w);
30238 w -= this.el.getBorderWidth("rl");
30239 if(this.config.adjustments){
30240 w += this.config.adjustments[0];
30244 this.el.setHeight(h);
30245 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30246 h -= this.el.getBorderWidth("tb");
30247 if(this.config.adjustments){
30248 h += this.config.adjustments[1];
30250 this.bodyEl.setHeight(h);
30252 h = this.tabs.syncHeight(h);
30255 if(this.panelSize){
30256 w = w !== null ? w : this.panelSize.width;
30257 h = h !== null ? h : this.panelSize.height;
30259 if(this.activePanel){
30260 var el = this.activePanel.getEl();
30261 w = w !== null ? w : el.getWidth();
30262 h = h !== null ? h : el.getHeight();
30263 this.panelSize = {width: w, height: h};
30264 this.activePanel.setSize(w, h);
30266 if(Roo.isIE && this.tabs){
30267 this.tabs.el.repaint();
30272 * Returns the container element for this region.
30273 * @return {Roo.Element}
30275 getEl : function(){
30280 * Hides this region.
30283 if(!this.collapsed){
30284 this.el.dom.style.left = "-2000px";
30287 this.collapsedEl.dom.style.left = "-2000px";
30288 this.collapsedEl.hide();
30290 this.visible = false;
30291 this.fireEvent("visibilitychange", this, false);
30295 * Shows this region if it was previously hidden.
30298 if(!this.collapsed){
30301 this.collapsedEl.show();
30303 this.visible = true;
30304 this.fireEvent("visibilitychange", this, true);
30307 closeClicked : function(){
30308 if(this.activePanel){
30309 this.remove(this.activePanel);
30313 collapseClick : function(e){
30315 e.stopPropagation();
30318 e.stopPropagation();
30324 * Collapses this region.
30325 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30327 collapse : function(skipAnim, skipCheck){
30328 if(this.collapsed) {
30332 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30334 this.collapsed = true;
30336 this.split.el.hide();
30338 if(this.config.animate && skipAnim !== true){
30339 this.fireEvent("invalidated", this);
30340 this.animateCollapse();
30342 this.el.setLocation(-20000,-20000);
30344 this.collapsedEl.show();
30345 this.fireEvent("collapsed", this);
30346 this.fireEvent("invalidated", this);
30352 animateCollapse : function(){
30357 * Expands this region if it was previously collapsed.
30358 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30359 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30361 expand : function(e, skipAnim){
30363 e.stopPropagation();
30365 if(!this.collapsed || this.el.hasActiveFx()) {
30369 this.afterSlideIn();
30372 this.collapsed = false;
30373 if(this.config.animate && skipAnim !== true){
30374 this.animateExpand();
30378 this.split.el.show();
30380 this.collapsedEl.setLocation(-2000,-2000);
30381 this.collapsedEl.hide();
30382 this.fireEvent("invalidated", this);
30383 this.fireEvent("expanded", this);
30387 animateExpand : function(){
30391 initTabs : function()
30393 this.bodyEl.setStyle("overflow", "hidden");
30394 var ts = new Roo.TabPanel(
30397 tabPosition: this.bottomTabs ? 'bottom' : 'top',
30398 disableTooltips: this.config.disableTabTips,
30399 toolbar : this.config.toolbar
30402 if(this.config.hideTabs){
30403 ts.stripWrap.setDisplayed(false);
30406 ts.resizeTabs = this.config.resizeTabs === true;
30407 ts.minTabWidth = this.config.minTabWidth || 40;
30408 ts.maxTabWidth = this.config.maxTabWidth || 250;
30409 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30410 ts.monitorResize = false;
30411 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30412 ts.bodyEl.addClass('x-layout-tabs-body');
30413 this.panels.each(this.initPanelAsTab, this);
30416 initPanelAsTab : function(panel){
30417 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30418 this.config.closeOnTab && panel.isClosable());
30419 if(panel.tabTip !== undefined){
30420 ti.setTooltip(panel.tabTip);
30422 ti.on("activate", function(){
30423 this.setActivePanel(panel);
30425 if(this.config.closeOnTab){
30426 ti.on("beforeclose", function(t, e){
30428 this.remove(panel);
30434 updatePanelTitle : function(panel, title){
30435 if(this.activePanel == panel){
30436 this.updateTitle(title);
30439 var ti = this.tabs.getTab(panel.getEl().id);
30441 if(panel.tabTip !== undefined){
30442 ti.setTooltip(panel.tabTip);
30447 updateTitle : function(title){
30448 if(this.titleTextEl && !this.config.title){
30449 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
30453 setActivePanel : function(panel){
30454 panel = this.getPanel(panel);
30455 if(this.activePanel && this.activePanel != panel){
30456 this.activePanel.setActiveState(false);
30458 this.activePanel = panel;
30459 panel.setActiveState(true);
30460 if(this.panelSize){
30461 panel.setSize(this.panelSize.width, this.panelSize.height);
30464 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30466 this.updateTitle(panel.getTitle());
30468 this.fireEvent("invalidated", this);
30470 this.fireEvent("panelactivated", this, panel);
30474 * Shows the specified panel.
30475 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30476 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30478 showPanel : function(panel)
30480 panel = this.getPanel(panel);
30483 var tab = this.tabs.getTab(panel.getEl().id);
30484 if(tab.isHidden()){
30485 this.tabs.unhideTab(tab.id);
30489 this.setActivePanel(panel);
30496 * Get the active panel for this region.
30497 * @return {Roo.ContentPanel} The active panel or null
30499 getActivePanel : function(){
30500 return this.activePanel;
30503 validateVisibility : function(){
30504 if(this.panels.getCount() < 1){
30505 this.updateTitle(" ");
30506 this.closeBtn.hide();
30509 if(!this.isVisible()){
30516 * Adds the passed ContentPanel(s) to this region.
30517 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30518 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30520 add : function(panel){
30521 if(arguments.length > 1){
30522 for(var i = 0, len = arguments.length; i < len; i++) {
30523 this.add(arguments[i]);
30527 if(this.hasPanel(panel)){
30528 this.showPanel(panel);
30531 panel.setRegion(this);
30532 this.panels.add(panel);
30533 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30534 this.bodyEl.dom.appendChild(panel.getEl().dom);
30535 if(panel.background !== true){
30536 this.setActivePanel(panel);
30538 this.fireEvent("paneladded", this, panel);
30544 this.initPanelAsTab(panel);
30546 if(panel.background !== true){
30547 this.tabs.activate(panel.getEl().id);
30549 this.fireEvent("paneladded", this, panel);
30554 * Hides the tab for the specified panel.
30555 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30557 hidePanel : function(panel){
30558 if(this.tabs && (panel = this.getPanel(panel))){
30559 this.tabs.hideTab(panel.getEl().id);
30564 * Unhides the tab for a previously hidden panel.
30565 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30567 unhidePanel : function(panel){
30568 if(this.tabs && (panel = this.getPanel(panel))){
30569 this.tabs.unhideTab(panel.getEl().id);
30573 clearPanels : function(){
30574 while(this.panels.getCount() > 0){
30575 this.remove(this.panels.first());
30580 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30581 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30582 * @param {Boolean} preservePanel Overrides the config preservePanel option
30583 * @return {Roo.ContentPanel} The panel that was removed
30585 remove : function(panel, preservePanel){
30586 panel = this.getPanel(panel);
30591 this.fireEvent("beforeremove", this, panel, e);
30592 if(e.cancel === true){
30595 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30596 var panelId = panel.getId();
30597 this.panels.removeKey(panelId);
30599 document.body.appendChild(panel.getEl().dom);
30602 this.tabs.removeTab(panel.getEl().id);
30603 }else if (!preservePanel){
30604 this.bodyEl.dom.removeChild(panel.getEl().dom);
30606 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30607 var p = this.panels.first();
30608 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30609 tempEl.appendChild(p.getEl().dom);
30610 this.bodyEl.update("");
30611 this.bodyEl.dom.appendChild(p.getEl().dom);
30613 this.updateTitle(p.getTitle());
30615 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30616 this.setActivePanel(p);
30618 panel.setRegion(null);
30619 if(this.activePanel == panel){
30620 this.activePanel = null;
30622 if(this.config.autoDestroy !== false && preservePanel !== true){
30623 try{panel.destroy();}catch(e){}
30625 this.fireEvent("panelremoved", this, panel);
30630 * Returns the TabPanel component used by this region
30631 * @return {Roo.TabPanel}
30633 getTabs : function(){
30637 createTool : function(parentEl, className){
30638 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30639 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
30640 btn.addClassOnOver("x-layout-tools-button-over");
30645 * Ext JS Library 1.1.1
30646 * Copyright(c) 2006-2007, Ext JS, LLC.
30648 * Originally Released Under LGPL - original licence link has changed is not relivant.
30651 * <script type="text/javascript">
30657 * @class Roo.SplitLayoutRegion
30658 * @extends Roo.LayoutRegion
30659 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30661 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30662 this.cursor = cursor;
30663 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30666 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30667 splitTip : "Drag to resize.",
30668 collapsibleSplitTip : "Drag to resize. Double click to hide.",
30669 useSplitTips : false,
30671 applyConfig : function(config){
30672 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30675 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
30676 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
30677 /** The SplitBar for this region
30678 * @type Roo.SplitBar */
30679 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30680 this.split.on("moved", this.onSplitMove, this);
30681 this.split.useShim = config.useShim === true;
30682 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30683 if(this.useSplitTips){
30684 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30686 if(config.collapsible){
30687 this.split.el.on("dblclick", this.collapse, this);
30690 if(typeof config.minSize != "undefined"){
30691 this.split.minSize = config.minSize;
30693 if(typeof config.maxSize != "undefined"){
30694 this.split.maxSize = config.maxSize;
30696 if(config.hideWhenEmpty || config.hidden || config.collapsed){
30697 this.hideSplitter();
30702 getHMaxSize : function(){
30703 var cmax = this.config.maxSize || 10000;
30704 var center = this.mgr.getRegion("center");
30705 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30708 getVMaxSize : function(){
30709 var cmax = this.config.maxSize || 10000;
30710 var center = this.mgr.getRegion("center");
30711 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30714 onSplitMove : function(split, newSize){
30715 this.fireEvent("resized", this, newSize);
30719 * Returns the {@link Roo.SplitBar} for this region.
30720 * @return {Roo.SplitBar}
30722 getSplitBar : function(){
30727 this.hideSplitter();
30728 Roo.SplitLayoutRegion.superclass.hide.call(this);
30731 hideSplitter : function(){
30733 this.split.el.setLocation(-2000,-2000);
30734 this.split.el.hide();
30740 this.split.el.show();
30742 Roo.SplitLayoutRegion.superclass.show.call(this);
30745 beforeSlide: function(){
30746 if(Roo.isGecko){// firefox overflow auto bug workaround
30747 this.bodyEl.clip();
30749 this.tabs.bodyEl.clip();
30751 if(this.activePanel){
30752 this.activePanel.getEl().clip();
30754 if(this.activePanel.beforeSlide){
30755 this.activePanel.beforeSlide();
30761 afterSlide : function(){
30762 if(Roo.isGecko){// firefox overflow auto bug workaround
30763 this.bodyEl.unclip();
30765 this.tabs.bodyEl.unclip();
30767 if(this.activePanel){
30768 this.activePanel.getEl().unclip();
30769 if(this.activePanel.afterSlide){
30770 this.activePanel.afterSlide();
30776 initAutoHide : function(){
30777 if(this.autoHide !== false){
30778 if(!this.autoHideHd){
30779 var st = new Roo.util.DelayedTask(this.slideIn, this);
30780 this.autoHideHd = {
30781 "mouseout": function(e){
30782 if(!e.within(this.el, true)){
30786 "mouseover" : function(e){
30792 this.el.on(this.autoHideHd);
30796 clearAutoHide : function(){
30797 if(this.autoHide !== false){
30798 this.el.un("mouseout", this.autoHideHd.mouseout);
30799 this.el.un("mouseover", this.autoHideHd.mouseover);
30803 clearMonitor : function(){
30804 Roo.get(document).un("click", this.slideInIf, this);
30807 // these names are backwards but not changed for compat
30808 slideOut : function(){
30809 if(this.isSlid || this.el.hasActiveFx()){
30812 this.isSlid = true;
30813 if(this.collapseBtn){
30814 this.collapseBtn.hide();
30816 this.closeBtnState = this.closeBtn.getStyle('display');
30817 this.closeBtn.hide();
30819 this.stickBtn.show();
30822 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30823 this.beforeSlide();
30824 this.el.setStyle("z-index", 10001);
30825 this.el.slideIn(this.getSlideAnchor(), {
30826 callback: function(){
30828 this.initAutoHide();
30829 Roo.get(document).on("click", this.slideInIf, this);
30830 this.fireEvent("slideshow", this);
30837 afterSlideIn : function(){
30838 this.clearAutoHide();
30839 this.isSlid = false;
30840 this.clearMonitor();
30841 this.el.setStyle("z-index", "");
30842 if(this.collapseBtn){
30843 this.collapseBtn.show();
30845 this.closeBtn.setStyle('display', this.closeBtnState);
30847 this.stickBtn.hide();
30849 this.fireEvent("slidehide", this);
30852 slideIn : function(cb){
30853 if(!this.isSlid || this.el.hasActiveFx()){
30857 this.isSlid = false;
30858 this.beforeSlide();
30859 this.el.slideOut(this.getSlideAnchor(), {
30860 callback: function(){
30861 this.el.setLeftTop(-10000, -10000);
30863 this.afterSlideIn();
30871 slideInIf : function(e){
30872 if(!e.within(this.el)){
30877 animateCollapse : function(){
30878 this.beforeSlide();
30879 this.el.setStyle("z-index", 20000);
30880 var anchor = this.getSlideAnchor();
30881 this.el.slideOut(anchor, {
30882 callback : function(){
30883 this.el.setStyle("z-index", "");
30884 this.collapsedEl.slideIn(anchor, {duration:.3});
30886 this.el.setLocation(-10000,-10000);
30888 this.fireEvent("collapsed", this);
30895 animateExpand : function(){
30896 this.beforeSlide();
30897 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30898 this.el.setStyle("z-index", 20000);
30899 this.collapsedEl.hide({
30902 this.el.slideIn(this.getSlideAnchor(), {
30903 callback : function(){
30904 this.el.setStyle("z-index", "");
30907 this.split.el.show();
30909 this.fireEvent("invalidated", this);
30910 this.fireEvent("expanded", this);
30938 getAnchor : function(){
30939 return this.anchors[this.position];
30942 getCollapseAnchor : function(){
30943 return this.canchors[this.position];
30946 getSlideAnchor : function(){
30947 return this.sanchors[this.position];
30950 getAlignAdj : function(){
30951 var cm = this.cmargins;
30952 switch(this.position){
30968 getExpandAdj : function(){
30969 var c = this.collapsedEl, cm = this.cmargins;
30970 switch(this.position){
30972 return [-(cm.right+c.getWidth()+cm.left), 0];
30975 return [cm.right+c.getWidth()+cm.left, 0];
30978 return [0, -(cm.top+cm.bottom+c.getHeight())];
30981 return [0, cm.top+cm.bottom+c.getHeight()];
30987 * Ext JS Library 1.1.1
30988 * Copyright(c) 2006-2007, Ext JS, LLC.
30990 * Originally Released Under LGPL - original licence link has changed is not relivant.
30993 * <script type="text/javascript">
30996 * These classes are private internal classes
30998 Roo.CenterLayoutRegion = function(mgr, config){
30999 Roo.LayoutRegion.call(this, mgr, config, "center");
31000 this.visible = true;
31001 this.minWidth = config.minWidth || 20;
31002 this.minHeight = config.minHeight || 20;
31005 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31007 // center panel can't be hidden
31011 // center panel can't be hidden
31014 getMinWidth: function(){
31015 return this.minWidth;
31018 getMinHeight: function(){
31019 return this.minHeight;
31024 Roo.NorthLayoutRegion = function(mgr, config){
31025 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31027 this.split.placement = Roo.SplitBar.TOP;
31028 this.split.orientation = Roo.SplitBar.VERTICAL;
31029 this.split.el.addClass("x-layout-split-v");
31031 var size = config.initialSize || config.height;
31032 if(typeof size != "undefined"){
31033 this.el.setHeight(size);
31036 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31037 orientation: Roo.SplitBar.VERTICAL,
31038 getBox : function(){
31039 if(this.collapsed){
31040 return this.collapsedEl.getBox();
31042 var box = this.el.getBox();
31044 box.height += this.split.el.getHeight();
31049 updateBox : function(box){
31050 if(this.split && !this.collapsed){
31051 box.height -= this.split.el.getHeight();
31052 this.split.el.setLeft(box.x);
31053 this.split.el.setTop(box.y+box.height);
31054 this.split.el.setWidth(box.width);
31056 if(this.collapsed){
31057 this.updateBody(box.width, null);
31059 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31063 Roo.SouthLayoutRegion = function(mgr, config){
31064 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31066 this.split.placement = Roo.SplitBar.BOTTOM;
31067 this.split.orientation = Roo.SplitBar.VERTICAL;
31068 this.split.el.addClass("x-layout-split-v");
31070 var size = config.initialSize || config.height;
31071 if(typeof size != "undefined"){
31072 this.el.setHeight(size);
31075 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31076 orientation: Roo.SplitBar.VERTICAL,
31077 getBox : function(){
31078 if(this.collapsed){
31079 return this.collapsedEl.getBox();
31081 var box = this.el.getBox();
31083 var sh = this.split.el.getHeight();
31090 updateBox : function(box){
31091 if(this.split && !this.collapsed){
31092 var sh = this.split.el.getHeight();
31095 this.split.el.setLeft(box.x);
31096 this.split.el.setTop(box.y-sh);
31097 this.split.el.setWidth(box.width);
31099 if(this.collapsed){
31100 this.updateBody(box.width, null);
31102 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31106 Roo.EastLayoutRegion = function(mgr, config){
31107 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31109 this.split.placement = Roo.SplitBar.RIGHT;
31110 this.split.orientation = Roo.SplitBar.HORIZONTAL;
31111 this.split.el.addClass("x-layout-split-h");
31113 var size = config.initialSize || config.width;
31114 if(typeof size != "undefined"){
31115 this.el.setWidth(size);
31118 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31119 orientation: Roo.SplitBar.HORIZONTAL,
31120 getBox : function(){
31121 if(this.collapsed){
31122 return this.collapsedEl.getBox();
31124 var box = this.el.getBox();
31126 var sw = this.split.el.getWidth();
31133 updateBox : function(box){
31134 if(this.split && !this.collapsed){
31135 var sw = this.split.el.getWidth();
31137 this.split.el.setLeft(box.x);
31138 this.split.el.setTop(box.y);
31139 this.split.el.setHeight(box.height);
31142 if(this.collapsed){
31143 this.updateBody(null, box.height);
31145 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31149 Roo.WestLayoutRegion = function(mgr, config){
31150 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31152 this.split.placement = Roo.SplitBar.LEFT;
31153 this.split.orientation = Roo.SplitBar.HORIZONTAL;
31154 this.split.el.addClass("x-layout-split-h");
31156 var size = config.initialSize || config.width;
31157 if(typeof size != "undefined"){
31158 this.el.setWidth(size);
31161 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31162 orientation: Roo.SplitBar.HORIZONTAL,
31163 getBox : function(){
31164 if(this.collapsed){
31165 return this.collapsedEl.getBox();
31167 var box = this.el.getBox();
31169 box.width += this.split.el.getWidth();
31174 updateBox : function(box){
31175 if(this.split && !this.collapsed){
31176 var sw = this.split.el.getWidth();
31178 this.split.el.setLeft(box.x+box.width);
31179 this.split.el.setTop(box.y);
31180 this.split.el.setHeight(box.height);
31182 if(this.collapsed){
31183 this.updateBody(null, box.height);
31185 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31190 * Ext JS Library 1.1.1
31191 * Copyright(c) 2006-2007, Ext JS, LLC.
31193 * Originally Released Under LGPL - original licence link has changed is not relivant.
31196 * <script type="text/javascript">
31201 * Private internal class for reading and applying state
31203 Roo.LayoutStateManager = function(layout){
31204 // default empty state
31213 Roo.LayoutStateManager.prototype = {
31214 init : function(layout, provider){
31215 this.provider = provider;
31216 var state = provider.get(layout.id+"-layout-state");
31218 var wasUpdating = layout.isUpdating();
31220 layout.beginUpdate();
31222 for(var key in state){
31223 if(typeof state[key] != "function"){
31224 var rstate = state[key];
31225 var r = layout.getRegion(key);
31228 r.resizeTo(rstate.size);
31230 if(rstate.collapsed == true){
31233 r.expand(null, true);
31239 layout.endUpdate();
31241 this.state = state;
31243 this.layout = layout;
31244 layout.on("regionresized", this.onRegionResized, this);
31245 layout.on("regioncollapsed", this.onRegionCollapsed, this);
31246 layout.on("regionexpanded", this.onRegionExpanded, this);
31249 storeState : function(){
31250 this.provider.set(this.layout.id+"-layout-state", this.state);
31253 onRegionResized : function(region, newSize){
31254 this.state[region.getPosition()].size = newSize;
31258 onRegionCollapsed : function(region){
31259 this.state[region.getPosition()].collapsed = true;
31263 onRegionExpanded : function(region){
31264 this.state[region.getPosition()].collapsed = false;
31269 * Ext JS Library 1.1.1
31270 * Copyright(c) 2006-2007, Ext JS, LLC.
31272 * Originally Released Under LGPL - original licence link has changed is not relivant.
31275 * <script type="text/javascript">
31278 * @class Roo.ContentPanel
31279 * @extends Roo.util.Observable
31280 * @children Roo.form.Form Roo.JsonView Roo.View
31281 * @parent Roo.BorderLayout Roo.LayoutDialog builder-top
31282 * A basic ContentPanel element.
31283 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
31284 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
31285 * @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
31286 * @cfg {Boolean} closable True if the panel can be closed/removed
31287 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
31288 * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31289 * @cfg {Roo.Toolbar} toolbar A toolbar for this panel
31290 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
31291 * @cfg {String} title The title for this panel
31292 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31293 * @cfg {String} url Calls {@link #setUrl} with this value
31294 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31295 * @cfg {String|Object} params When used with {@link #url}, calls {@link #setUrl} with this value
31296 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
31297 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
31298 * @cfg {String} style Extra style to add to the content panel
31299 * @cfg {Roo.menu.Menu} menu popup menu
31302 * Create a new ContentPanel.
31303 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31304 * @param {String/Object} config A string to set only the title or a config object
31305 * @param {String} content (optional) Set the HTML content for this panel
31306 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31308 Roo.ContentPanel = function(el, config, content){
31312 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31316 if (config && config.parentLayout) {
31317 el = config.parentLayout.el.createChild();
31320 if(el.autoCreate){ // xtype is available if this is called from factory
31324 this.el = Roo.get(el);
31325 if(!this.el && config && config.autoCreate){
31326 if(typeof config.autoCreate == "object"){
31327 if(!config.autoCreate.id){
31328 config.autoCreate.id = config.id||el;
31330 this.el = Roo.DomHelper.append(document.body,
31331 config.autoCreate, true);
31333 this.el = Roo.DomHelper.append(document.body,
31334 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31339 this.closable = false;
31340 this.loaded = false;
31341 this.active = false;
31342 if(typeof config == "string"){
31343 this.title = config;
31345 Roo.apply(this, config);
31348 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31349 this.wrapEl = this.el.wrap();
31350 this.toolbar.container = this.el.insertSibling(false, 'before');
31351 this.toolbar = new Roo.Toolbar(this.toolbar);
31354 // xtype created footer. - not sure if will work as we normally have to render first..
31355 if (this.footer && !this.footer.el && this.footer.xtype) {
31356 if (!this.wrapEl) {
31357 this.wrapEl = this.el.wrap();
31360 this.footer.container = this.wrapEl.createChild();
31362 this.footer = Roo.factory(this.footer, Roo);
31367 this.resizeEl = Roo.get(this.resizeEl, true);
31369 this.resizeEl = this.el;
31371 // handle view.xtype
31379 * Fires when this panel is activated.
31380 * @param {Roo.ContentPanel} this
31384 * @event deactivate
31385 * Fires when this panel is activated.
31386 * @param {Roo.ContentPanel} this
31388 "deactivate" : true,
31392 * Fires when this panel is resized if fitToFrame is true.
31393 * @param {Roo.ContentPanel} this
31394 * @param {Number} width The width after any component adjustments
31395 * @param {Number} height The height after any component adjustments
31401 * Fires when this tab is created
31402 * @param {Roo.ContentPanel} this
31412 if(this.autoScroll){
31413 this.resizeEl.setStyle("overflow", "auto");
31415 // fix randome scrolling
31416 this.el.on('scroll', function() {
31417 Roo.log('fix random scolling');
31418 this.scrollTo('top',0);
31421 content = content || this.content;
31423 this.setContent(content);
31425 if(config && config.url){
31426 this.setUrl(this.url, this.params, this.loadOnce);
31431 Roo.ContentPanel.superclass.constructor.call(this);
31433 if (this.view && typeof(this.view.xtype) != 'undefined') {
31434 this.view.el = this.el.appendChild(document.createElement("div"));
31435 this.view = Roo.factory(this.view);
31436 this.view.render && this.view.render(false, '');
31440 this.fireEvent('render', this);
31443 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31445 setRegion : function(region){
31446 this.region = region;
31448 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31450 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31455 * Returns the toolbar for this Panel if one was configured.
31456 * @return {Roo.Toolbar}
31458 getToolbar : function(){
31459 return this.toolbar;
31462 setActiveState : function(active){
31463 this.active = active;
31465 this.fireEvent("deactivate", this);
31467 this.fireEvent("activate", this);
31471 * Updates this panel's element
31472 * @param {String} content The new content
31473 * @param {Boolean} loadScripts (optional) true to look for and process scripts
31475 setContent : function(content, loadScripts){
31476 this.el.update(content, loadScripts);
31479 ignoreResize : function(w, h){
31480 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31483 this.lastSize = {width: w, height: h};
31488 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31489 * @return {Roo.UpdateManager} The UpdateManager
31491 getUpdateManager : function(){
31492 return this.el.getUpdateManager();
31495 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31496 * @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:
31499 url: "your-url.php",
31500 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31501 callback: yourFunction,
31502 scope: yourObject, //(optional scope)
31505 text: "Loading...",
31510 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31511 * 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.
31512 * @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}
31513 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31514 * @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.
31515 * @return {Roo.ContentPanel} this
31518 var um = this.el.getUpdateManager();
31519 um.update.apply(um, arguments);
31525 * 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.
31526 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31527 * @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)
31528 * @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)
31529 * @return {Roo.UpdateManager} The UpdateManager
31531 setUrl : function(url, params, loadOnce){
31532 if(this.refreshDelegate){
31533 this.removeListener("activate", this.refreshDelegate);
31535 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31536 this.on("activate", this.refreshDelegate);
31537 return this.el.getUpdateManager();
31540 _handleRefresh : function(url, params, loadOnce){
31541 if(!loadOnce || !this.loaded){
31542 var updater = this.el.getUpdateManager();
31543 updater.update(url, params, this._setLoaded.createDelegate(this));
31547 _setLoaded : function(){
31548 this.loaded = true;
31552 * Returns this panel's id
31555 getId : function(){
31560 * Returns this panel's element - used by regiosn to add.
31561 * @return {Roo.Element}
31563 getEl : function(){
31564 return this.wrapEl || this.el;
31567 adjustForComponents : function(width, height)
31569 //Roo.log('adjustForComponents ');
31570 if(this.resizeEl != this.el){
31571 width -= this.el.getFrameWidth('lr');
31572 height -= this.el.getFrameWidth('tb');
31575 var te = this.toolbar.getEl();
31576 height -= te.getHeight();
31577 te.setWidth(width);
31580 var te = this.footer.getEl();
31581 //Roo.log("footer:" + te.getHeight());
31583 height -= te.getHeight();
31584 te.setWidth(width);
31588 if(this.adjustments){
31589 width += this.adjustments[0];
31590 height += this.adjustments[1];
31592 return {"width": width, "height": height};
31595 setSize : function(width, height){
31596 if(this.fitToFrame && !this.ignoreResize(width, height)){
31597 if(this.fitContainer && this.resizeEl != this.el){
31598 this.el.setSize(width, height);
31600 var size = this.adjustForComponents(width, height);
31601 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31602 this.fireEvent('resize', this, size.width, size.height);
31607 * Returns this panel's title
31610 getTitle : function(){
31615 * Set this panel's title
31616 * @param {String} title
31618 setTitle : function(title){
31619 this.title = title;
31621 this.region.updatePanelTitle(this, title);
31626 * Returns true is this panel was configured to be closable
31627 * @return {Boolean}
31629 isClosable : function(){
31630 return this.closable;
31633 beforeSlide : function(){
31635 this.resizeEl.clip();
31638 afterSlide : function(){
31640 this.resizeEl.unclip();
31644 * Force a content refresh from the URL specified in the {@link #setUrl} method.
31645 * Will fail silently if the {@link #setUrl} method has not been called.
31646 * This does not activate the panel, just updates its content.
31648 refresh : function(){
31649 if(this.refreshDelegate){
31650 this.loaded = false;
31651 this.refreshDelegate();
31656 * Destroys this panel
31658 destroy : function(){
31659 this.el.removeAllListeners();
31660 var tempEl = document.createElement("span");
31661 tempEl.appendChild(this.el.dom);
31662 tempEl.innerHTML = "";
31668 * form - if the content panel contains a form - this is a reference to it.
31669 * @type {Roo.form.Form}
31673 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31674 * This contains a reference to it.
31680 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31690 * @param {Object} cfg Xtype definition of item to add.
31693 addxtype : function(cfg) {
31695 if (cfg.xtype.match(/^Form$/)) {
31698 //if (this.footer) {
31699 // el = this.footer.container.insertSibling(false, 'before');
31701 el = this.el.createChild();
31704 this.form = new Roo.form.Form(cfg);
31707 if ( this.form.allItems.length) {
31708 this.form.render(el.dom);
31712 // should only have one of theses..
31713 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31714 // views.. should not be just added - used named prop 'view''
31716 cfg.el = this.el.appendChild(document.createElement("div"));
31719 var ret = new Roo.factory(cfg);
31721 ret.render && ret.render(false, ''); // render blank..
31730 * @class Roo.GridPanel
31731 * @extends Roo.ContentPanel
31733 * Create a new GridPanel.
31734 * @param {Roo.grid.Grid} grid The grid for this panel
31735 * @param {String/Object} config A string to set only the panel's title, or a config object
31737 Roo.GridPanel = function(grid, config){
31740 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31741 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31743 this.wrapper.dom.appendChild(grid.getGridEl().dom);
31745 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31748 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31750 // xtype created footer. - not sure if will work as we normally have to render first..
31751 if (this.footer && !this.footer.el && this.footer.xtype) {
31753 this.footer.container = this.grid.getView().getFooterPanel(true);
31754 this.footer.dataSource = this.grid.dataSource;
31755 this.footer = Roo.factory(this.footer, Roo);
31759 grid.monitorWindowResize = false; // turn off autosizing
31760 grid.autoHeight = false;
31761 grid.autoWidth = false;
31763 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31766 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31767 getId : function(){
31768 return this.grid.id;
31772 * Returns the grid for this panel
31773 * @return {Roo.grid.Grid}
31775 getGrid : function(){
31779 setSize : function(width, height){
31780 if(!this.ignoreResize(width, height)){
31781 var grid = this.grid;
31782 var size = this.adjustForComponents(width, height);
31783 grid.getGridEl().setSize(size.width, size.height);
31788 beforeSlide : function(){
31789 this.grid.getView().scroller.clip();
31792 afterSlide : function(){
31793 this.grid.getView().scroller.unclip();
31796 destroy : function(){
31797 this.grid.destroy();
31799 Roo.GridPanel.superclass.destroy.call(this);
31805 * @class Roo.NestedLayoutPanel
31806 * @extends Roo.ContentPanel
31808 * Create a new NestedLayoutPanel.
31811 * @param {Roo.BorderLayout} layout [required] The layout for this panel
31812 * @param {String/Object} config A string to set only the title or a config object
31814 Roo.NestedLayoutPanel = function(layout, config)
31816 // construct with only one argument..
31817 /* FIXME - implement nicer consturctors
31818 if (layout.layout) {
31820 layout = config.layout;
31821 delete config.layout;
31823 if (layout.xtype && !layout.getEl) {
31824 // then layout needs constructing..
31825 layout = Roo.factory(layout, Roo);
31830 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31832 layout.monitorWindowResize = false; // turn off autosizing
31833 this.layout = layout;
31834 this.layout.getEl().addClass("x-layout-nested-layout");
31841 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31843 setSize : function(width, height){
31844 if(!this.ignoreResize(width, height)){
31845 var size = this.adjustForComponents(width, height);
31846 var el = this.layout.getEl();
31847 el.setSize(size.width, size.height);
31848 var touch = el.dom.offsetWidth;
31849 this.layout.layout();
31850 // ie requires a double layout on the first pass
31851 if(Roo.isIE && !this.initialized){
31852 this.initialized = true;
31853 this.layout.layout();
31858 // activate all subpanels if not currently active..
31860 setActiveState : function(active){
31861 this.active = active;
31863 this.fireEvent("deactivate", this);
31867 this.fireEvent("activate", this);
31868 // not sure if this should happen before or after..
31869 if (!this.layout) {
31870 return; // should not happen..
31873 for (var r in this.layout.regions) {
31874 reg = this.layout.getRegion(r);
31875 if (reg.getActivePanel()) {
31876 //reg.showPanel(reg.getActivePanel()); // force it to activate..
31877 reg.setActivePanel(reg.getActivePanel());
31880 if (!reg.panels.length) {
31883 reg.showPanel(reg.getPanel(0));
31892 * Returns the nested BorderLayout for this panel
31893 * @return {Roo.BorderLayout}
31895 getLayout : function(){
31896 return this.layout;
31900 * Adds a xtype elements to the layout of the nested panel
31904 xtype : 'ContentPanel',
31911 xtype : 'NestedLayoutPanel',
31917 items : [ ... list of content panels or nested layout panels.. ]
31921 * @param {Object} cfg Xtype definition of item to add.
31923 addxtype : function(cfg) {
31924 return this.layout.addxtype(cfg);
31929 Roo.ScrollPanel = function(el, config, content){
31930 config = config || {};
31931 config.fitToFrame = true;
31932 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31934 this.el.dom.style.overflow = "hidden";
31935 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31936 this.el.removeClass("x-layout-inactive-content");
31937 this.el.on("mousewheel", this.onWheel, this);
31939 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
31940 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
31941 up.unselectable(); down.unselectable();
31942 up.on("click", this.scrollUp, this);
31943 down.on("click", this.scrollDown, this);
31944 up.addClassOnOver("x-scroller-btn-over");
31945 down.addClassOnOver("x-scroller-btn-over");
31946 up.addClassOnClick("x-scroller-btn-click");
31947 down.addClassOnClick("x-scroller-btn-click");
31948 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31950 this.resizeEl = this.el;
31951 this.el = wrap; this.up = up; this.down = down;
31954 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31956 wheelIncrement : 5,
31957 scrollUp : function(){
31958 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31961 scrollDown : function(){
31962 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31965 afterScroll : function(){
31966 var el = this.resizeEl;
31967 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31968 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31969 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31972 setSize : function(){
31973 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31974 this.afterScroll();
31977 onWheel : function(e){
31978 var d = e.getWheelDelta();
31979 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31980 this.afterScroll();
31984 setContent : function(content, loadScripts){
31985 this.resizeEl.update(content, loadScripts);
31993 * @class Roo.TreePanel
31994 * @extends Roo.ContentPanel
31995 * Treepanel component
31998 * Create a new TreePanel. - defaults to fit/scoll contents.
31999 * @param {String/Object} config A string to set only the panel's title, or a config object
32001 Roo.TreePanel = function(config){
32002 var el = config.el;
32003 var tree = config.tree;
32004 delete config.tree;
32005 delete config.el; // hopefull!
32007 // wrapper for IE7 strict & safari scroll issue
32009 var treeEl = el.createChild();
32010 config.resizeEl = treeEl;
32014 Roo.TreePanel.superclass.constructor.call(this, el, config);
32017 this.tree = new Roo.tree.TreePanel(treeEl , tree);
32018 //console.log(tree);
32019 this.on('activate', function()
32021 if (this.tree.rendered) {
32024 //console.log('render tree');
32025 this.tree.render();
32027 // this should not be needed.. - it's actually the 'el' that resizes?
32028 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32030 //this.on('resize', function (cp, w, h) {
32031 // this.tree.innerCt.setWidth(w);
32032 // this.tree.innerCt.setHeight(h);
32033 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
32040 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
32044 * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
32062 * Ext JS Library 1.1.1
32063 * Copyright(c) 2006-2007, Ext JS, LLC.
32065 * Originally Released Under LGPL - original licence link has changed is not relivant.
32068 * <script type="text/javascript">
32073 * @class Roo.ReaderLayout
32074 * @extends Roo.BorderLayout
32075 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
32076 * center region containing two nested regions (a top one for a list view and one for item preview below),
32077 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32078 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32079 * expedites the setup of the overall layout and regions for this common application style.
32082 var reader = new Roo.ReaderLayout();
32083 var CP = Roo.ContentPanel; // shortcut for adding
32085 reader.beginUpdate();
32086 reader.add("north", new CP("north", "North"));
32087 reader.add("west", new CP("west", {title: "West"}));
32088 reader.add("east", new CP("east", {title: "East"}));
32090 reader.regions.listView.add(new CP("listView", "List"));
32091 reader.regions.preview.add(new CP("preview", "Preview"));
32092 reader.endUpdate();
32095 * Create a new ReaderLayout
32096 * @param {Object} config Configuration options
32097 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32098 * document.body if omitted)
32100 Roo.ReaderLayout = function(config, renderTo){
32101 var c = config || {size:{}};
32102 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32103 north: c.north !== false ? Roo.apply({
32107 }, c.north) : false,
32108 west: c.west !== false ? Roo.apply({
32116 margins:{left:5,right:0,bottom:5,top:5},
32117 cmargins:{left:5,right:5,bottom:5,top:5}
32118 }, c.west) : false,
32119 east: c.east !== false ? Roo.apply({
32127 margins:{left:0,right:5,bottom:5,top:5},
32128 cmargins:{left:5,right:5,bottom:5,top:5}
32129 }, c.east) : false,
32130 center: Roo.apply({
32131 tabPosition: 'top',
32135 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32139 this.el.addClass('x-reader');
32141 this.beginUpdate();
32143 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32144 south: c.preview !== false ? Roo.apply({
32151 cmargins:{top:5,left:0, right:0, bottom:0}
32152 }, c.preview) : false,
32153 center: Roo.apply({
32159 this.add('center', new Roo.NestedLayoutPanel(inner,
32160 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32164 this.regions.preview = inner.getRegion('south');
32165 this.regions.listView = inner.getRegion('center');
32168 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32170 * Ext JS Library 1.1.1
32171 * Copyright(c) 2006-2007, Ext JS, LLC.
32173 * Originally Released Under LGPL - original licence link has changed is not relivant.
32176 * <script type="text/javascript">
32180 * @class Roo.grid.Grid
32181 * @extends Roo.util.Observable
32182 * This class represents the primary interface of a component based grid control.
32183 * <br><br>Usage:<pre><code>
32184 var grid = new Roo.grid.Grid("my-container-id", {
32187 selModel: mySelectionModel,
32188 autoSizeColumns: true,
32189 monitorWindowResize: false,
32190 trackMouseOver: true
32195 * <b>Common Problems:</b><br/>
32196 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32197 * element will correct this<br/>
32198 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32199 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32200 * are unpredictable.<br/>
32201 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32202 * grid to calculate dimensions/offsets.<br/>
32204 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32205 * The container MUST have some type of size defined for the grid to fill. The container will be
32206 * automatically set to position relative if it isn't already.
32207 * @param {Object} config A config object that sets properties on this grid.
32209 Roo.grid.Grid = function(container, config){
32210 // initialize the container
32211 this.container = Roo.get(container);
32212 this.container.update("");
32213 this.container.setStyle("overflow", "hidden");
32214 this.container.addClass('x-grid-container');
32216 this.id = this.container.id;
32218 Roo.apply(this, config);
32219 // check and correct shorthanded configs
32221 this.dataSource = this.ds;
32225 this.colModel = this.cm;
32229 this.selModel = this.sm;
32233 if (this.selModel) {
32234 this.selModel = Roo.factory(this.selModel, Roo.grid);
32235 this.sm = this.selModel;
32236 this.sm.xmodule = this.xmodule || false;
32238 if (typeof(this.colModel.config) == 'undefined') {
32239 this.colModel = new Roo.grid.ColumnModel(this.colModel);
32240 this.cm = this.colModel;
32241 this.cm.xmodule = this.xmodule || false;
32243 if (this.dataSource) {
32244 this.dataSource= Roo.factory(this.dataSource, Roo.data);
32245 this.ds = this.dataSource;
32246 this.ds.xmodule = this.xmodule || false;
32253 this.container.setWidth(this.width);
32257 this.container.setHeight(this.height);
32264 * The raw click event for the entire grid.
32265 * @param {Roo.EventObject} e
32270 * The raw dblclick event for the entire grid.
32271 * @param {Roo.EventObject} e
32275 * @event contextmenu
32276 * The raw contextmenu event for the entire grid.
32277 * @param {Roo.EventObject} e
32279 "contextmenu" : true,
32282 * The raw mousedown event for the entire grid.
32283 * @param {Roo.EventObject} e
32285 "mousedown" : true,
32288 * The raw mouseup event for the entire grid.
32289 * @param {Roo.EventObject} e
32294 * The raw mouseover event for the entire grid.
32295 * @param {Roo.EventObject} e
32297 "mouseover" : true,
32300 * The raw mouseout event for the entire grid.
32301 * @param {Roo.EventObject} e
32306 * The raw keypress event for the entire grid.
32307 * @param {Roo.EventObject} e
32312 * The raw keydown event for the entire grid.
32313 * @param {Roo.EventObject} e
32321 * Fires when a cell is clicked
32322 * @param {Grid} this
32323 * @param {Number} rowIndex
32324 * @param {Number} columnIndex
32325 * @param {Roo.EventObject} e
32327 "cellclick" : true,
32329 * @event celldblclick
32330 * Fires when a cell is double clicked
32331 * @param {Grid} this
32332 * @param {Number} rowIndex
32333 * @param {Number} columnIndex
32334 * @param {Roo.EventObject} e
32336 "celldblclick" : true,
32339 * Fires when a row is clicked
32340 * @param {Grid} this
32341 * @param {Number} rowIndex
32342 * @param {Roo.EventObject} e
32346 * @event rowdblclick
32347 * Fires when a row is double clicked
32348 * @param {Grid} this
32349 * @param {Number} rowIndex
32350 * @param {Roo.EventObject} e
32352 "rowdblclick" : true,
32354 * @event headerclick
32355 * Fires when a header is clicked
32356 * @param {Grid} this
32357 * @param {Number} columnIndex
32358 * @param {Roo.EventObject} e
32360 "headerclick" : true,
32362 * @event headerdblclick
32363 * Fires when a header cell is double clicked
32364 * @param {Grid} this
32365 * @param {Number} columnIndex
32366 * @param {Roo.EventObject} e
32368 "headerdblclick" : true,
32370 * @event rowcontextmenu
32371 * Fires when a row is right clicked
32372 * @param {Grid} this
32373 * @param {Number} rowIndex
32374 * @param {Roo.EventObject} e
32376 "rowcontextmenu" : true,
32378 * @event cellcontextmenu
32379 * Fires when a cell is right clicked
32380 * @param {Grid} this
32381 * @param {Number} rowIndex
32382 * @param {Number} cellIndex
32383 * @param {Roo.EventObject} e
32385 "cellcontextmenu" : true,
32387 * @event headercontextmenu
32388 * Fires when a header is right clicked
32389 * @param {Grid} this
32390 * @param {Number} columnIndex
32391 * @param {Roo.EventObject} e
32393 "headercontextmenu" : true,
32395 * @event bodyscroll
32396 * Fires when the body element is scrolled
32397 * @param {Number} scrollLeft
32398 * @param {Number} scrollTop
32400 "bodyscroll" : true,
32402 * @event columnresize
32403 * Fires when the user resizes a column
32404 * @param {Number} columnIndex
32405 * @param {Number} newSize
32407 "columnresize" : true,
32409 * @event columnmove
32410 * Fires when the user moves a column
32411 * @param {Number} oldIndex
32412 * @param {Number} newIndex
32414 "columnmove" : true,
32417 * Fires when row(s) start being dragged
32418 * @param {Grid} this
32419 * @param {Roo.GridDD} dd The drag drop object
32420 * @param {event} e The raw browser event
32422 "startdrag" : true,
32425 * Fires when a drag operation is complete
32426 * @param {Grid} this
32427 * @param {Roo.GridDD} dd The drag drop object
32428 * @param {event} e The raw browser event
32433 * Fires when dragged row(s) are dropped on a valid DD target
32434 * @param {Grid} this
32435 * @param {Roo.GridDD} dd The drag drop object
32436 * @param {String} targetId The target drag drop object
32437 * @param {event} e The raw browser event
32442 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32443 * @param {Grid} this
32444 * @param {Roo.GridDD} dd The drag drop object
32445 * @param {String} targetId The target drag drop object
32446 * @param {event} e The raw browser event
32451 * Fires when the dragged row(s) first cross another DD target while being dragged
32452 * @param {Grid} this
32453 * @param {Roo.GridDD} dd The drag drop object
32454 * @param {String} targetId The target drag drop object
32455 * @param {event} e The raw browser event
32457 "dragenter" : true,
32460 * Fires when the dragged row(s) leave another DD target while being dragged
32461 * @param {Grid} this
32462 * @param {Roo.GridDD} dd The drag drop object
32463 * @param {String} targetId The target drag drop object
32464 * @param {event} e The raw browser event
32469 * Fires when a row is rendered, so you can change add a style to it.
32470 * @param {GridView} gridview The grid view
32471 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
32477 * Fires when the grid is rendered
32478 * @param {Grid} grid
32483 Roo.grid.Grid.superclass.constructor.call(this);
32485 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32488 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
32491 * @cfg {Roo.grid.GridView} view The view that renders the grid (default = Roo.grid.GridView)
32494 * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
32497 * @cfg {Roo.grid.Store} ds The data store for the grid
32500 * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
32503 * @cfg {String} ddGroup - drag drop group.
32506 * @cfg {String} dragGroup - drag group (?? not sure if needed.)
32510 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32512 minColumnWidth : 25,
32515 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32516 * <b>on initial render.</b> It is more efficient to explicitly size the columns
32517 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
32519 autoSizeColumns : false,
32522 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32524 autoSizeHeaders : true,
32527 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32529 monitorWindowResize : true,
32532 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32533 * rows measured to get a columns size. Default is 0 (all rows).
32535 maxRowsToMeasure : 0,
32538 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32540 trackMouseOver : true,
32543 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
32546 * @cfg {Boolean} enableDrop True to enable drop of elements. Default is false. (double check if this is needed?)
32550 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32552 enableDragDrop : false,
32555 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32557 enableColumnMove : true,
32560 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32562 enableColumnHide : true,
32565 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32567 enableRowHeightSync : false,
32570 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
32575 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32577 autoHeight : false,
32580 * @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.
32582 autoExpandColumn : false,
32585 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32588 autoExpandMin : 50,
32591 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32593 autoExpandMax : 1000,
32596 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32601 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32605 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32615 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32616 * of a fixed width. Default is false.
32619 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32624 * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32625 * %0 is replaced with the number of selected rows.
32627 ddText : "{0} selected row{1}",
32631 * Called once after all setup has been completed and the grid is ready to be rendered.
32632 * @return {Roo.grid.Grid} this
32634 render : function()
32636 var c = this.container;
32637 // try to detect autoHeight/width mode
32638 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32639 this.autoHeight = true;
32641 var view = this.getView();
32644 c.on("click", this.onClick, this);
32645 c.on("dblclick", this.onDblClick, this);
32646 c.on("contextmenu", this.onContextMenu, this);
32647 c.on("keydown", this.onKeyDown, this);
32649 c.on("touchstart", this.onTouchStart, this);
32652 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32654 this.getSelectionModel().init(this);
32659 this.loadMask = new Roo.LoadMask(this.container,
32660 Roo.apply({store:this.dataSource}, this.loadMask));
32664 if (this.toolbar && this.toolbar.xtype) {
32665 this.toolbar.container = this.getView().getHeaderPanel(true);
32666 this.toolbar = new Roo.Toolbar(this.toolbar);
32668 if (this.footer && this.footer.xtype) {
32669 this.footer.dataSource = this.getDataSource();
32670 this.footer.container = this.getView().getFooterPanel(true);
32671 this.footer = Roo.factory(this.footer, Roo);
32673 if (this.dropTarget && this.dropTarget.xtype) {
32674 delete this.dropTarget.xtype;
32675 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32679 this.rendered = true;
32680 this.fireEvent('render', this);
32685 * Reconfigures the grid to use a different Store and Column Model.
32686 * The View will be bound to the new objects and refreshed.
32687 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32688 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32690 reconfigure : function(dataSource, colModel){
32692 this.loadMask.destroy();
32693 this.loadMask = new Roo.LoadMask(this.container,
32694 Roo.apply({store:dataSource}, this.loadMask));
32696 this.view.bind(dataSource, colModel);
32697 this.dataSource = dataSource;
32698 this.colModel = colModel;
32699 this.view.refresh(true);
32703 * Add's a column, default at the end..
32705 * @param {int} position to add (default end)
32706 * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel}
32708 addColumns : function(pos, ar)
32711 for (var i =0;i< ar.length;i++) {
32713 cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
32714 this.cm.lookup[cfg.id] = cfg;
32718 if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
32719 pos = this.cm.config.length; //this.cm.config.push(cfg);
32721 pos = Math.max(0,pos);
32724 this.cm.config.splice.apply(this.cm.config, ar);
32728 this.view.generateRules(this.cm);
32729 this.view.refresh(true);
32737 onKeyDown : function(e){
32738 this.fireEvent("keydown", e);
32742 * Destroy this grid.
32743 * @param {Boolean} removeEl True to remove the element
32745 destroy : function(removeEl, keepListeners){
32747 this.loadMask.destroy();
32749 var c = this.container;
32750 c.removeAllListeners();
32751 this.view.destroy();
32752 this.colModel.purgeListeners();
32753 if(!keepListeners){
32754 this.purgeListeners();
32757 if(removeEl === true){
32763 processEvent : function(name, e){
32764 // does this fire select???
32765 //Roo.log('grid:processEvent ' + name);
32767 if (name != 'touchstart' ) {
32768 this.fireEvent(name, e);
32771 var t = e.getTarget();
32773 var header = v.findHeaderIndex(t);
32774 if(header !== false){
32775 var ename = name == 'touchstart' ? 'click' : name;
32777 this.fireEvent("header" + ename, this, header, e);
32779 var row = v.findRowIndex(t);
32780 var cell = v.findCellIndex(t);
32781 if (name == 'touchstart') {
32782 // first touch is always a click.
32783 // hopefull this happens after selection is updated.?
32786 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32787 var cs = this.selModel.getSelectedCell();
32788 if (row == cs[0] && cell == cs[1]){
32792 if (typeof(this.selModel.getSelections) != 'undefined') {
32793 var cs = this.selModel.getSelections();
32794 var ds = this.dataSource;
32795 if (cs.length == 1 && ds.getAt(row) == cs[0]){
32806 this.fireEvent("row" + name, this, row, e);
32807 if(cell !== false){
32808 this.fireEvent("cell" + name, this, row, cell, e);
32815 onClick : function(e){
32816 this.processEvent("click", e);
32819 onTouchStart : function(e){
32820 this.processEvent("touchstart", e);
32824 onContextMenu : function(e, t){
32825 this.processEvent("contextmenu", e);
32829 onDblClick : function(e){
32830 this.processEvent("dblclick", e);
32834 walkCells : function(row, col, step, fn, scope){
32835 var cm = this.colModel, clen = cm.getColumnCount();
32836 var ds = this.dataSource, rlen = ds.getCount(), first = true;
32848 if(fn.call(scope || this, row, col, cm) === true){
32866 if(fn.call(scope || this, row, col, cm) === true){
32878 getSelections : function(){
32879 return this.selModel.getSelections();
32883 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32884 * but if manual update is required this method will initiate it.
32886 autoSize : function(){
32888 this.view.layout();
32889 if(this.view.adjustForScroll){
32890 this.view.adjustForScroll();
32896 * Returns the grid's underlying element.
32897 * @return {Element} The element
32899 getGridEl : function(){
32900 return this.container;
32903 // private for compatibility, overridden by editor grid
32904 stopEditing : function(){},
32907 * Returns the grid's SelectionModel.
32908 * @return {SelectionModel}
32910 getSelectionModel : function(){
32911 if(!this.selModel){
32912 this.selModel = new Roo.grid.RowSelectionModel();
32914 return this.selModel;
32918 * Returns the grid's DataSource.
32919 * @return {DataSource}
32921 getDataSource : function(){
32922 return this.dataSource;
32926 * Returns the grid's ColumnModel.
32927 * @return {ColumnModel}
32929 getColumnModel : function(){
32930 return this.colModel;
32934 * Returns the grid's GridView object.
32935 * @return {GridView}
32937 getView : function(){
32939 this.view = new Roo.grid.GridView(this.viewConfig);
32940 this.relayEvents(this.view, [
32941 "beforerowremoved", "beforerowsinserted",
32942 "beforerefresh", "rowremoved",
32943 "rowsinserted", "rowupdated" ,"refresh"
32949 * Called to get grid's drag proxy text, by default returns this.ddText.
32950 * Override this to put something different in the dragged text.
32953 getDragDropText : function(){
32954 var count = this.selModel.getCount();
32955 return String.format(this.ddText, count, count == 1 ? '' : 's');
32960 * Ext JS Library 1.1.1
32961 * Copyright(c) 2006-2007, Ext JS, LLC.
32963 * Originally Released Under LGPL - original licence link has changed is not relivant.
32966 * <script type="text/javascript">
32969 * @class Roo.grid.AbstractGridView
32970 * @extends Roo.util.Observable
32972 * Abstract base class for grid Views
32975 Roo.grid.AbstractGridView = function(){
32979 "beforerowremoved" : true,
32980 "beforerowsinserted" : true,
32981 "beforerefresh" : true,
32982 "rowremoved" : true,
32983 "rowsinserted" : true,
32984 "rowupdated" : true,
32987 Roo.grid.AbstractGridView.superclass.constructor.call(this);
32990 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32991 rowClass : "x-grid-row",
32992 cellClass : "x-grid-cell",
32993 tdClass : "x-grid-td",
32994 hdClass : "x-grid-hd",
32995 splitClass : "x-grid-hd-split",
32997 init: function(grid){
32999 var cid = this.grid.getGridEl().id;
33000 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33001 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33002 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33003 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33006 getColumnRenderers : function(){
33007 var renderers = [];
33008 var cm = this.grid.colModel;
33009 var colCount = cm.getColumnCount();
33010 for(var i = 0; i < colCount; i++){
33011 renderers[i] = cm.getRenderer(i);
33016 getColumnIds : function(){
33018 var cm = this.grid.colModel;
33019 var colCount = cm.getColumnCount();
33020 for(var i = 0; i < colCount; i++){
33021 ids[i] = cm.getColumnId(i);
33026 getDataIndexes : function(){
33027 if(!this.indexMap){
33028 this.indexMap = this.buildIndexMap();
33030 return this.indexMap.colToData;
33033 getColumnIndexByDataIndex : function(dataIndex){
33034 if(!this.indexMap){
33035 this.indexMap = this.buildIndexMap();
33037 return this.indexMap.dataToCol[dataIndex];
33041 * Set a css style for a column dynamically.
33042 * @param {Number} colIndex The index of the column
33043 * @param {String} name The css property name
33044 * @param {String} value The css value
33046 setCSSStyle : function(colIndex, name, value){
33047 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33048 Roo.util.CSS.updateRule(selector, name, value);
33051 generateRules : function(cm){
33052 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33053 Roo.util.CSS.removeStyleSheet(rulesId);
33054 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33055 var cid = cm.getColumnId(i);
33056 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33057 this.tdSelector, cid, " {\n}\n",
33058 this.hdSelector, cid, " {\n}\n",
33059 this.splitSelector, cid, " {\n}\n");
33061 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33065 * Ext JS Library 1.1.1
33066 * Copyright(c) 2006-2007, Ext JS, LLC.
33068 * Originally Released Under LGPL - original licence link has changed is not relivant.
33071 * <script type="text/javascript">
33075 // This is a support class used internally by the Grid components
33076 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33078 this.view = grid.getView();
33079 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33080 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33082 this.setHandleElId(Roo.id(hd));
33083 this.setOuterHandleElId(Roo.id(hd2));
33085 this.scroll = false;
33087 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33089 getDragData : function(e){
33090 var t = Roo.lib.Event.getTarget(e);
33091 var h = this.view.findHeaderCell(t);
33093 return {ddel: h.firstChild, header:h};
33098 onInitDrag : function(e){
33099 this.view.headersDisabled = true;
33100 var clone = this.dragData.ddel.cloneNode(true);
33101 clone.id = Roo.id();
33102 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33103 this.proxy.update(clone);
33107 afterValidDrop : function(){
33109 setTimeout(function(){
33110 v.headersDisabled = false;
33114 afterInvalidDrop : function(){
33116 setTimeout(function(){
33117 v.headersDisabled = false;
33123 * Ext JS Library 1.1.1
33124 * Copyright(c) 2006-2007, Ext JS, LLC.
33126 * Originally Released Under LGPL - original licence link has changed is not relivant.
33129 * <script type="text/javascript">
33132 // This is a support class used internally by the Grid components
33133 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33135 this.view = grid.getView();
33136 // split the proxies so they don't interfere with mouse events
33137 this.proxyTop = Roo.DomHelper.append(document.body, {
33138 cls:"col-move-top", html:" "
33140 this.proxyBottom = Roo.DomHelper.append(document.body, {
33141 cls:"col-move-bottom", html:" "
33143 this.proxyTop.hide = this.proxyBottom.hide = function(){
33144 this.setLeftTop(-100,-100);
33145 this.setStyle("visibility", "hidden");
33147 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33148 // temporarily disabled
33149 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33150 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33152 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33153 proxyOffsets : [-4, -9],
33154 fly: Roo.Element.fly,
33156 getTargetFromEvent : function(e){
33157 var t = Roo.lib.Event.getTarget(e);
33158 var cindex = this.view.findCellIndex(t);
33159 if(cindex !== false){
33160 return this.view.getHeaderCell(cindex);
33165 nextVisible : function(h){
33166 var v = this.view, cm = this.grid.colModel;
33169 if(!cm.isHidden(v.getCellIndex(h))){
33177 prevVisible : function(h){
33178 var v = this.view, cm = this.grid.colModel;
33181 if(!cm.isHidden(v.getCellIndex(h))){
33189 positionIndicator : function(h, n, e){
33190 var x = Roo.lib.Event.getPageX(e);
33191 var r = Roo.lib.Dom.getRegion(n.firstChild);
33192 var px, pt, py = r.top + this.proxyOffsets[1];
33193 if((r.right - x) <= (r.right-r.left)/2){
33194 px = r.right+this.view.borderWidth;
33200 var oldIndex = this.view.getCellIndex(h);
33201 var newIndex = this.view.getCellIndex(n);
33203 if(this.grid.colModel.isFixed(newIndex)){
33207 var locked = this.grid.colModel.isLocked(newIndex);
33212 if(oldIndex < newIndex){
33215 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33218 px += this.proxyOffsets[0];
33219 this.proxyTop.setLeftTop(px, py);
33220 this.proxyTop.show();
33221 if(!this.bottomOffset){
33222 this.bottomOffset = this.view.mainHd.getHeight();
33224 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33225 this.proxyBottom.show();
33229 onNodeEnter : function(n, dd, e, data){
33230 if(data.header != n){
33231 this.positionIndicator(data.header, n, e);
33235 onNodeOver : function(n, dd, e, data){
33236 var result = false;
33237 if(data.header != n){
33238 result = this.positionIndicator(data.header, n, e);
33241 this.proxyTop.hide();
33242 this.proxyBottom.hide();
33244 return result ? this.dropAllowed : this.dropNotAllowed;
33247 onNodeOut : function(n, dd, e, data){
33248 this.proxyTop.hide();
33249 this.proxyBottom.hide();
33252 onNodeDrop : function(n, dd, e, data){
33253 var h = data.header;
33255 var cm = this.grid.colModel;
33256 var x = Roo.lib.Event.getPageX(e);
33257 var r = Roo.lib.Dom.getRegion(n.firstChild);
33258 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33259 var oldIndex = this.view.getCellIndex(h);
33260 var newIndex = this.view.getCellIndex(n);
33261 var locked = cm.isLocked(newIndex);
33265 if(oldIndex < newIndex){
33268 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33271 cm.setLocked(oldIndex, locked, true);
33272 cm.moveColumn(oldIndex, newIndex);
33273 this.grid.fireEvent("columnmove", oldIndex, newIndex);
33281 * Ext JS Library 1.1.1
33282 * Copyright(c) 2006-2007, Ext JS, LLC.
33284 * Originally Released Under LGPL - original licence link has changed is not relivant.
33287 * <script type="text/javascript">
33291 * @class Roo.grid.GridView
33292 * @extends Roo.util.Observable
33295 * @param {Object} config
33297 Roo.grid.GridView = function(config){
33298 Roo.grid.GridView.superclass.constructor.call(this);
33301 Roo.apply(this, config);
33304 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33306 unselectable : 'unselectable="on"',
33307 unselectableCls : 'x-unselectable',
33310 rowClass : "x-grid-row",
33312 cellClass : "x-grid-col",
33314 tdClass : "x-grid-td",
33316 hdClass : "x-grid-hd",
33318 splitClass : "x-grid-split",
33320 sortClasses : ["sort-asc", "sort-desc"],
33322 enableMoveAnim : false,
33326 dh : Roo.DomHelper,
33328 fly : Roo.Element.fly,
33330 css : Roo.util.CSS,
33336 scrollIncrement : 22,
33338 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33340 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33342 bind : function(ds, cm){
33344 this.ds.un("load", this.onLoad, this);
33345 this.ds.un("datachanged", this.onDataChange, this);
33346 this.ds.un("add", this.onAdd, this);
33347 this.ds.un("remove", this.onRemove, this);
33348 this.ds.un("update", this.onUpdate, this);
33349 this.ds.un("clear", this.onClear, this);
33352 ds.on("load", this.onLoad, this);
33353 ds.on("datachanged", this.onDataChange, this);
33354 ds.on("add", this.onAdd, this);
33355 ds.on("remove", this.onRemove, this);
33356 ds.on("update", this.onUpdate, this);
33357 ds.on("clear", this.onClear, this);
33362 this.cm.un("widthchange", this.onColWidthChange, this);
33363 this.cm.un("headerchange", this.onHeaderChange, this);
33364 this.cm.un("hiddenchange", this.onHiddenChange, this);
33365 this.cm.un("columnmoved", this.onColumnMove, this);
33366 this.cm.un("columnlockchange", this.onColumnLock, this);
33369 this.generateRules(cm);
33370 cm.on("widthchange", this.onColWidthChange, this);
33371 cm.on("headerchange", this.onHeaderChange, this);
33372 cm.on("hiddenchange", this.onHiddenChange, this);
33373 cm.on("columnmoved", this.onColumnMove, this);
33374 cm.on("columnlockchange", this.onColumnLock, this);
33379 init: function(grid){
33380 Roo.grid.GridView.superclass.init.call(this, grid);
33382 this.bind(grid.dataSource, grid.colModel);
33384 grid.on("headerclick", this.handleHeaderClick, this);
33386 if(grid.trackMouseOver){
33387 grid.on("mouseover", this.onRowOver, this);
33388 grid.on("mouseout", this.onRowOut, this);
33390 grid.cancelTextSelection = function(){};
33391 this.gridId = grid.id;
33393 var tpls = this.templates || {};
33396 tpls.master = new Roo.Template(
33397 '<div class="x-grid" hidefocus="true">',
33398 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33399 '<div class="x-grid-topbar"></div>',
33400 '<div class="x-grid-scroller"><div></div></div>',
33401 '<div class="x-grid-locked">',
33402 '<div class="x-grid-header">{lockedHeader}</div>',
33403 '<div class="x-grid-body">{lockedBody}</div>',
33405 '<div class="x-grid-viewport">',
33406 '<div class="x-grid-header">{header}</div>',
33407 '<div class="x-grid-body">{body}</div>',
33409 '<div class="x-grid-bottombar"></div>',
33411 '<div class="x-grid-resize-proxy"> </div>',
33414 tpls.master.disableformats = true;
33418 tpls.header = new Roo.Template(
33419 '<table border="0" cellspacing="0" cellpadding="0">',
33420 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33423 tpls.header.disableformats = true;
33425 tpls.header.compile();
33428 tpls.hcell = new Roo.Template(
33429 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33430 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33433 tpls.hcell.disableFormats = true;
33435 tpls.hcell.compile();
33438 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33439 this.unselectableCls + '" ' + this.unselectable +'> </div>');
33440 tpls.hsplit.disableFormats = true;
33442 tpls.hsplit.compile();
33445 tpls.body = new Roo.Template(
33446 '<table border="0" cellspacing="0" cellpadding="0">',
33447 "<tbody>{rows}</tbody>",
33450 tpls.body.disableFormats = true;
33452 tpls.body.compile();
33455 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33456 tpls.row.disableFormats = true;
33458 tpls.row.compile();
33461 tpls.cell = new Roo.Template(
33462 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33463 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33464 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33467 tpls.cell.disableFormats = true;
33469 tpls.cell.compile();
33471 this.templates = tpls;
33474 // remap these for backwards compat
33475 onColWidthChange : function(){
33476 this.updateColumns.apply(this, arguments);
33478 onHeaderChange : function(){
33479 this.updateHeaders.apply(this, arguments);
33481 onHiddenChange : function(){
33482 this.handleHiddenChange.apply(this, arguments);
33484 onColumnMove : function(){
33485 this.handleColumnMove.apply(this, arguments);
33487 onColumnLock : function(){
33488 this.handleLockChange.apply(this, arguments);
33491 onDataChange : function(){
33493 this.updateHeaderSortState();
33496 onClear : function(){
33500 onUpdate : function(ds, record){
33501 this.refreshRow(record);
33504 refreshRow : function(record){
33505 var ds = this.ds, index;
33506 if(typeof record == 'number'){
33508 record = ds.getAt(index);
33510 index = ds.indexOf(record);
33512 this.insertRows(ds, index, index, true);
33513 this.onRemove(ds, record, index+1, true);
33514 this.syncRowHeights(index, index);
33516 this.fireEvent("rowupdated", this, index, record);
33519 onAdd : function(ds, records, index){
33520 this.insertRows(ds, index, index + (records.length-1));
33523 onRemove : function(ds, record, index, isUpdate){
33524 if(isUpdate !== true){
33525 this.fireEvent("beforerowremoved", this, index, record);
33527 var bt = this.getBodyTable(), lt = this.getLockedTable();
33528 if(bt.rows[index]){
33529 bt.firstChild.removeChild(bt.rows[index]);
33531 if(lt.rows[index]){
33532 lt.firstChild.removeChild(lt.rows[index]);
33534 if(isUpdate !== true){
33535 this.stripeRows(index);
33536 this.syncRowHeights(index, index);
33538 this.fireEvent("rowremoved", this, index, record);
33542 onLoad : function(){
33543 this.scrollToTop();
33547 * Scrolls the grid to the top
33549 scrollToTop : function(){
33551 this.scroller.dom.scrollTop = 0;
33557 * Gets a panel in the header of the grid that can be used for toolbars etc.
33558 * After modifying the contents of this panel a call to grid.autoSize() may be
33559 * required to register any changes in size.
33560 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33561 * @return Roo.Element
33563 getHeaderPanel : function(doShow){
33565 this.headerPanel.show();
33567 return this.headerPanel;
33571 * Gets a panel in the footer of the grid that can be used for toolbars etc.
33572 * After modifying the contents of this panel a call to grid.autoSize() may be
33573 * required to register any changes in size.
33574 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33575 * @return Roo.Element
33577 getFooterPanel : function(doShow){
33579 this.footerPanel.show();
33581 return this.footerPanel;
33584 initElements : function(){
33585 var E = Roo.Element;
33586 var el = this.grid.getGridEl().dom.firstChild;
33587 var cs = el.childNodes;
33589 this.el = new E(el);
33591 this.focusEl = new E(el.firstChild);
33592 this.focusEl.swallowEvent("click", true);
33594 this.headerPanel = new E(cs[1]);
33595 this.headerPanel.enableDisplayMode("block");
33597 this.scroller = new E(cs[2]);
33598 this.scrollSizer = new E(this.scroller.dom.firstChild);
33600 this.lockedWrap = new E(cs[3]);
33601 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33602 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33604 this.mainWrap = new E(cs[4]);
33605 this.mainHd = new E(this.mainWrap.dom.firstChild);
33606 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33608 this.footerPanel = new E(cs[5]);
33609 this.footerPanel.enableDisplayMode("block");
33611 this.resizeProxy = new E(cs[6]);
33613 this.headerSelector = String.format(
33614 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33615 this.lockedHd.id, this.mainHd.id
33618 this.splitterSelector = String.format(
33619 '#{0} div.x-grid-split, #{1} div.x-grid-split',
33620 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33623 idToCssName : function(s)
33625 return s.replace(/[^a-z0-9]+/ig, '-');
33628 getHeaderCell : function(index){
33629 return Roo.DomQuery.select(this.headerSelector)[index];
33632 getHeaderCellMeasure : function(index){
33633 return this.getHeaderCell(index).firstChild;
33636 getHeaderCellText : function(index){
33637 return this.getHeaderCell(index).firstChild.firstChild;
33640 getLockedTable : function(){
33641 return this.lockedBody.dom.firstChild;
33644 getBodyTable : function(){
33645 return this.mainBody.dom.firstChild;
33648 getLockedRow : function(index){
33649 return this.getLockedTable().rows[index];
33652 getRow : function(index){
33653 return this.getBodyTable().rows[index];
33656 getRowComposite : function(index){
33658 this.rowEl = new Roo.CompositeElementLite();
33660 var els = [], lrow, mrow;
33661 if(lrow = this.getLockedRow(index)){
33664 if(mrow = this.getRow(index)){
33667 this.rowEl.elements = els;
33671 * Gets the 'td' of the cell
33673 * @param {Integer} rowIndex row to select
33674 * @param {Integer} colIndex column to select
33678 getCell : function(rowIndex, colIndex){
33679 var locked = this.cm.getLockedCount();
33681 if(colIndex < locked){
33682 source = this.lockedBody.dom.firstChild;
33684 source = this.mainBody.dom.firstChild;
33685 colIndex -= locked;
33687 return source.rows[rowIndex].childNodes[colIndex];
33690 getCellText : function(rowIndex, colIndex){
33691 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33694 getCellBox : function(cell){
33695 var b = this.fly(cell).getBox();
33696 if(Roo.isOpera){ // opera fails to report the Y
33697 b.y = cell.offsetTop + this.mainBody.getY();
33702 getCellIndex : function(cell){
33703 var id = String(cell.className).match(this.cellRE);
33705 return parseInt(id[1], 10);
33710 findHeaderIndex : function(n){
33711 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33712 return r ? this.getCellIndex(r) : false;
33715 findHeaderCell : function(n){
33716 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33717 return r ? r : false;
33720 findRowIndex : function(n){
33724 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33725 return r ? r.rowIndex : false;
33728 findCellIndex : function(node){
33729 var stop = this.el.dom;
33730 while(node && node != stop){
33731 if(this.findRE.test(node.className)){
33732 return this.getCellIndex(node);
33734 node = node.parentNode;
33739 getColumnId : function(index){
33740 return this.cm.getColumnId(index);
33743 getSplitters : function()
33745 if(this.splitterSelector){
33746 return Roo.DomQuery.select(this.splitterSelector);
33752 getSplitter : function(index){
33753 return this.getSplitters()[index];
33756 onRowOver : function(e, t){
33758 if((row = this.findRowIndex(t)) !== false){
33759 this.getRowComposite(row).addClass("x-grid-row-over");
33763 onRowOut : function(e, t){
33765 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33766 this.getRowComposite(row).removeClass("x-grid-row-over");
33770 renderHeaders : function(){
33772 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33773 var cb = [], lb = [], sb = [], lsb = [], p = {};
33774 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33775 p.cellId = "x-grid-hd-0-" + i;
33776 p.splitId = "x-grid-csplit-0-" + i;
33777 p.id = cm.getColumnId(i);
33778 p.value = cm.getColumnHeader(i) || "";
33779 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
33780 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33781 if(!cm.isLocked(i)){
33782 cb[cb.length] = ct.apply(p);
33783 sb[sb.length] = st.apply(p);
33785 lb[lb.length] = ct.apply(p);
33786 lsb[lsb.length] = st.apply(p);
33789 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33790 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33793 updateHeaders : function(){
33794 var html = this.renderHeaders();
33795 this.lockedHd.update(html[0]);
33796 this.mainHd.update(html[1]);
33800 * Focuses the specified row.
33801 * @param {Number} row The row index
33803 focusRow : function(row)
33805 //Roo.log('GridView.focusRow');
33806 var x = this.scroller.dom.scrollLeft;
33807 this.focusCell(row, 0, false);
33808 this.scroller.dom.scrollLeft = x;
33812 * Focuses the specified cell.
33813 * @param {Number} row The row index
33814 * @param {Number} col The column index
33815 * @param {Boolean} hscroll false to disable horizontal scrolling
33817 focusCell : function(row, col, hscroll)
33819 //Roo.log('GridView.focusCell');
33820 var el = this.ensureVisible(row, col, hscroll);
33821 this.focusEl.alignTo(el, "tl-tl");
33823 this.focusEl.focus();
33825 this.focusEl.focus.defer(1, this.focusEl);
33830 * Scrolls the specified cell into view
33831 * @param {Number} row The row index
33832 * @param {Number} col The column index
33833 * @param {Boolean} hscroll false to disable horizontal scrolling
33835 ensureVisible : function(row, col, hscroll)
33837 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33838 //return null; //disable for testing.
33839 if(typeof row != "number"){
33840 row = row.rowIndex;
33842 if(row < 0 && row >= this.ds.getCount()){
33845 col = (col !== undefined ? col : 0);
33846 var cm = this.grid.colModel;
33847 while(cm.isHidden(col)){
33851 var el = this.getCell(row, col);
33855 var c = this.scroller.dom;
33857 var ctop = parseInt(el.offsetTop, 10);
33858 var cleft = parseInt(el.offsetLeft, 10);
33859 var cbot = ctop + el.offsetHeight;
33860 var cright = cleft + el.offsetWidth;
33862 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33863 var stop = parseInt(c.scrollTop, 10);
33864 var sleft = parseInt(c.scrollLeft, 10);
33865 var sbot = stop + ch;
33866 var sright = sleft + c.clientWidth;
33868 Roo.log('GridView.ensureVisible:' +
33870 ' c.clientHeight:' + c.clientHeight +
33871 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33879 c.scrollTop = ctop;
33880 //Roo.log("set scrolltop to ctop DISABLE?");
33881 }else if(cbot > sbot){
33882 //Roo.log("set scrolltop to cbot-ch");
33883 c.scrollTop = cbot-ch;
33886 if(hscroll !== false){
33888 c.scrollLeft = cleft;
33889 }else if(cright > sright){
33890 c.scrollLeft = cright-c.clientWidth;
33897 updateColumns : function(){
33898 this.grid.stopEditing();
33899 var cm = this.grid.colModel, colIds = this.getColumnIds();
33900 //var totalWidth = cm.getTotalWidth();
33902 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33903 //if(cm.isHidden(i)) continue;
33904 var w = cm.getColumnWidth(i);
33905 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33906 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33908 this.updateSplitters();
33911 generateRules : function(cm){
33912 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33913 Roo.util.CSS.removeStyleSheet(rulesId);
33914 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33915 var cid = cm.getColumnId(i);
33917 if(cm.config[i].align){
33918 align = 'text-align:'+cm.config[i].align+';';
33921 if(cm.isHidden(i)){
33922 hidden = 'display:none;';
33924 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33926 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33927 this.hdSelector, cid, " {\n", align, width, "}\n",
33928 this.tdSelector, cid, " {\n",hidden,"\n}\n",
33929 this.splitSelector, cid, " {\n", hidden , "\n}\n");
33931 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33934 updateSplitters : function(){
33935 var cm = this.cm, s = this.getSplitters();
33936 if(s){ // splitters not created yet
33937 var pos = 0, locked = true;
33938 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33939 if(cm.isHidden(i)) {
33942 var w = cm.getColumnWidth(i); // make sure it's a number
33943 if(!cm.isLocked(i) && locked){
33948 s[i].style.left = (pos-this.splitOffset) + "px";
33953 handleHiddenChange : function(colModel, colIndex, hidden){
33955 this.hideColumn(colIndex);
33957 this.unhideColumn(colIndex);
33961 hideColumn : function(colIndex){
33962 var cid = this.getColumnId(colIndex);
33963 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33964 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33966 this.updateHeaders();
33968 this.updateSplitters();
33972 unhideColumn : function(colIndex){
33973 var cid = this.getColumnId(colIndex);
33974 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33975 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33978 this.updateHeaders();
33980 this.updateSplitters();
33984 insertRows : function(dm, firstRow, lastRow, isUpdate){
33985 if(firstRow == 0 && lastRow == dm.getCount()-1){
33989 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33991 var s = this.getScrollState();
33992 var markup = this.renderRows(firstRow, lastRow);
33993 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33994 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33995 this.restoreScroll(s);
33997 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33998 this.syncRowHeights(firstRow, lastRow);
33999 this.stripeRows(firstRow);
34005 bufferRows : function(markup, target, index){
34006 var before = null, trows = target.rows, tbody = target.tBodies[0];
34007 if(index < trows.length){
34008 before = trows[index];
34010 var b = document.createElement("div");
34011 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34012 var rows = b.firstChild.rows;
34013 for(var i = 0, len = rows.length; i < len; i++){
34015 tbody.insertBefore(rows[0], before);
34017 tbody.appendChild(rows[0]);
34024 deleteRows : function(dm, firstRow, lastRow){
34025 if(dm.getRowCount()<1){
34026 this.fireEvent("beforerefresh", this);
34027 this.mainBody.update("");
34028 this.lockedBody.update("");
34029 this.fireEvent("refresh", this);
34031 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34032 var bt = this.getBodyTable();
34033 var tbody = bt.firstChild;
34034 var rows = bt.rows;
34035 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34036 tbody.removeChild(rows[firstRow]);
34038 this.stripeRows(firstRow);
34039 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34043 updateRows : function(dataSource, firstRow, lastRow){
34044 var s = this.getScrollState();
34046 this.restoreScroll(s);
34049 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34053 this.updateHeaderSortState();
34056 getScrollState : function(){
34058 var sb = this.scroller.dom;
34059 return {left: sb.scrollLeft, top: sb.scrollTop};
34062 stripeRows : function(startRow){
34063 if(!this.grid.stripeRows || this.ds.getCount() < 1){
34066 startRow = startRow || 0;
34067 var rows = this.getBodyTable().rows;
34068 var lrows = this.getLockedTable().rows;
34069 var cls = ' x-grid-row-alt ';
34070 for(var i = startRow, len = rows.length; i < len; i++){
34071 var row = rows[i], lrow = lrows[i];
34072 var isAlt = ((i+1) % 2 == 0);
34073 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34074 if(isAlt == hasAlt){
34078 row.className += " x-grid-row-alt";
34080 row.className = row.className.replace("x-grid-row-alt", "");
34083 lrow.className = row.className;
34088 restoreScroll : function(state){
34089 //Roo.log('GridView.restoreScroll');
34090 var sb = this.scroller.dom;
34091 sb.scrollLeft = state.left;
34092 sb.scrollTop = state.top;
34096 syncScroll : function(){
34097 //Roo.log('GridView.syncScroll');
34098 var sb = this.scroller.dom;
34099 var sh = this.mainHd.dom;
34100 var bs = this.mainBody.dom;
34101 var lv = this.lockedBody.dom;
34102 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34103 lv.scrollTop = bs.scrollTop = sb.scrollTop;
34106 handleScroll : function(e){
34108 var sb = this.scroller.dom;
34109 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34113 handleWheel : function(e){
34114 var d = e.getWheelDelta();
34115 this.scroller.dom.scrollTop -= d*22;
34116 // set this here to prevent jumpy scrolling on large tables
34117 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34121 renderRows : function(startRow, endRow){
34122 // pull in all the crap needed to render rows
34123 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34124 var colCount = cm.getColumnCount();
34126 if(ds.getCount() < 1){
34130 // build a map for all the columns
34132 for(var i = 0; i < colCount; i++){
34133 var name = cm.getDataIndex(i);
34135 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34136 renderer : cm.getRenderer(i),
34137 id : cm.getColumnId(i),
34138 locked : cm.isLocked(i),
34139 has_editor : cm.isCellEditable(i)
34143 startRow = startRow || 0;
34144 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34146 // records to render
34147 var rs = ds.getRange(startRow, endRow);
34149 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34152 // As much as I hate to duplicate code, this was branched because FireFox really hates
34153 // [].join("") on strings. The performance difference was substantial enough to
34154 // branch this function
34155 doRender : Roo.isGecko ?
34156 function(cs, rs, ds, startRow, colCount, stripe){
34157 var ts = this.templates, ct = ts.cell, rt = ts.row;
34159 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34161 var hasListener = this.grid.hasListener('rowclass');
34163 for(var j = 0, len = rs.length; j < len; j++){
34164 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34165 for(var i = 0; i < colCount; i++){
34167 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34169 p.css = p.attr = "";
34170 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34171 if(p.value == undefined || p.value === "") {
34172 p.value = " ";
34175 p.css += ' x-grid-editable-cell';
34177 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34178 p.css += ' x-grid-dirty-cell';
34180 var markup = ct.apply(p);
34188 if(stripe && ((rowIndex+1) % 2 == 0)){
34189 alt.push("x-grid-row-alt")
34192 alt.push( " x-grid-dirty-row");
34195 if(this.getRowClass){
34196 alt.push(this.getRowClass(r, rowIndex));
34202 rowIndex : rowIndex,
34205 this.grid.fireEvent('rowclass', this, rowcfg);
34206 alt.push(rowcfg.rowClass);
34208 rp.alt = alt.join(" ");
34209 lbuf+= rt.apply(rp);
34211 buf+= rt.apply(rp);
34213 return [lbuf, buf];
34215 function(cs, rs, ds, startRow, colCount, stripe){
34216 var ts = this.templates, ct = ts.cell, rt = ts.row;
34218 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34219 var hasListener = this.grid.hasListener('rowclass');
34222 for(var j = 0, len = rs.length; j < len; j++){
34223 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34224 for(var i = 0; i < colCount; i++){
34226 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34228 p.css = p.attr = "";
34229 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34230 if(p.value == undefined || p.value === "") {
34231 p.value = " ";
34235 p.css += ' x-grid-editable-cell';
34237 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34238 p.css += ' x-grid-dirty-cell'
34241 var markup = ct.apply(p);
34243 cb[cb.length] = markup;
34245 lcb[lcb.length] = markup;
34249 if(stripe && ((rowIndex+1) % 2 == 0)){
34250 alt.push( "x-grid-row-alt");
34253 alt.push(" x-grid-dirty-row");
34256 if(this.getRowClass){
34257 alt.push( this.getRowClass(r, rowIndex));
34263 rowIndex : rowIndex,
34266 this.grid.fireEvent('rowclass', this, rowcfg);
34267 alt.push(rowcfg.rowClass);
34270 rp.alt = alt.join(" ");
34271 rp.cells = lcb.join("");
34272 lbuf[lbuf.length] = rt.apply(rp);
34273 rp.cells = cb.join("");
34274 buf[buf.length] = rt.apply(rp);
34276 return [lbuf.join(""), buf.join("")];
34279 renderBody : function(){
34280 var markup = this.renderRows();
34281 var bt = this.templates.body;
34282 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34286 * Refreshes the grid
34287 * @param {Boolean} headersToo
34289 refresh : function(headersToo){
34290 this.fireEvent("beforerefresh", this);
34291 this.grid.stopEditing();
34292 var result = this.renderBody();
34293 this.lockedBody.update(result[0]);
34294 this.mainBody.update(result[1]);
34295 if(headersToo === true){
34296 this.updateHeaders();
34297 this.updateColumns();
34298 this.updateSplitters();
34299 this.updateHeaderSortState();
34301 this.syncRowHeights();
34303 this.fireEvent("refresh", this);
34306 handleColumnMove : function(cm, oldIndex, newIndex){
34307 this.indexMap = null;
34308 var s = this.getScrollState();
34309 this.refresh(true);
34310 this.restoreScroll(s);
34311 this.afterMove(newIndex);
34314 afterMove : function(colIndex){
34315 if(this.enableMoveAnim && Roo.enableFx){
34316 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34318 // if multisort - fix sortOrder, and reload..
34319 if (this.grid.dataSource.multiSort) {
34320 // the we can call sort again..
34321 var dm = this.grid.dataSource;
34322 var cm = this.grid.colModel;
34324 for(var i = 0; i < cm.config.length; i++ ) {
34326 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34327 continue; // dont' bother, it's not in sort list or being set.
34330 so.push(cm.config[i].dataIndex);
34333 dm.load(dm.lastOptions);
34340 updateCell : function(dm, rowIndex, dataIndex){
34341 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34342 if(typeof colIndex == "undefined"){ // not present in grid
34345 var cm = this.grid.colModel;
34346 var cell = this.getCell(rowIndex, colIndex);
34347 var cellText = this.getCellText(rowIndex, colIndex);
34350 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34351 id : cm.getColumnId(colIndex),
34352 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34354 var renderer = cm.getRenderer(colIndex);
34355 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34356 if(typeof val == "undefined" || val === "") {
34359 cellText.innerHTML = val;
34360 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34361 this.syncRowHeights(rowIndex, rowIndex);
34364 calcColumnWidth : function(colIndex, maxRowsToMeasure){
34366 if(this.grid.autoSizeHeaders){
34367 var h = this.getHeaderCellMeasure(colIndex);
34368 maxWidth = Math.max(maxWidth, h.scrollWidth);
34371 if(this.cm.isLocked(colIndex)){
34372 tb = this.getLockedTable();
34375 tb = this.getBodyTable();
34376 index = colIndex - this.cm.getLockedCount();
34379 var rows = tb.rows;
34380 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34381 for(var i = 0; i < stopIndex; i++){
34382 var cell = rows[i].childNodes[index].firstChild;
34383 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34386 return maxWidth + /*margin for error in IE*/ 5;
34389 * Autofit a column to its content.
34390 * @param {Number} colIndex
34391 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34393 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34394 if(this.cm.isHidden(colIndex)){
34395 return; // can't calc a hidden column
34398 var cid = this.cm.getColumnId(colIndex);
34399 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34400 if(this.grid.autoSizeHeaders){
34401 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34404 var newWidth = this.calcColumnWidth(colIndex);
34405 this.cm.setColumnWidth(colIndex,
34406 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34407 if(!suppressEvent){
34408 this.grid.fireEvent("columnresize", colIndex, newWidth);
34413 * Autofits all columns to their content and then expands to fit any extra space in the grid
34415 autoSizeColumns : function(){
34416 var cm = this.grid.colModel;
34417 var colCount = cm.getColumnCount();
34418 for(var i = 0; i < colCount; i++){
34419 this.autoSizeColumn(i, true, true);
34421 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34424 this.updateColumns();
34430 * Autofits all columns to the grid's width proportionate with their current size
34431 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34433 fitColumns : function(reserveScrollSpace){
34434 var cm = this.grid.colModel;
34435 var colCount = cm.getColumnCount();
34439 for (i = 0; i < colCount; i++){
34440 if(!cm.isHidden(i) && !cm.isFixed(i)){
34441 w = cm.getColumnWidth(i);
34447 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34448 if(reserveScrollSpace){
34451 var frac = (avail - cm.getTotalWidth())/width;
34452 while (cols.length){
34455 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34457 this.updateColumns();
34461 onRowSelect : function(rowIndex){
34462 var row = this.getRowComposite(rowIndex);
34463 row.addClass("x-grid-row-selected");
34466 onRowDeselect : function(rowIndex){
34467 var row = this.getRowComposite(rowIndex);
34468 row.removeClass("x-grid-row-selected");
34471 onCellSelect : function(row, col){
34472 var cell = this.getCell(row, col);
34474 Roo.fly(cell).addClass("x-grid-cell-selected");
34478 onCellDeselect : function(row, col){
34479 var cell = this.getCell(row, col);
34481 Roo.fly(cell).removeClass("x-grid-cell-selected");
34485 updateHeaderSortState : function(){
34487 // sort state can be single { field: xxx, direction : yyy}
34488 // or { xxx=>ASC , yyy : DESC ..... }
34491 if (!this.ds.multiSort) {
34492 var state = this.ds.getSortState();
34496 mstate[state.field] = state.direction;
34497 // FIXME... - this is not used here.. but might be elsewhere..
34498 this.sortState = state;
34501 mstate = this.ds.sortToggle;
34503 //remove existing sort classes..
34505 var sc = this.sortClasses;
34506 var hds = this.el.select(this.headerSelector).removeClass(sc);
34508 for(var f in mstate) {
34510 var sortColumn = this.cm.findColumnIndex(f);
34512 if(sortColumn != -1){
34513 var sortDir = mstate[f];
34514 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34523 handleHeaderClick : function(g, index,e){
34525 Roo.log("header click");
34528 // touch events on header are handled by context
34529 this.handleHdCtx(g,index,e);
34534 if(this.headersDisabled){
34537 var dm = g.dataSource, cm = g.colModel;
34538 if(!cm.isSortable(index)){
34543 if (dm.multiSort) {
34544 // update the sortOrder
34546 for(var i = 0; i < cm.config.length; i++ ) {
34548 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34549 continue; // dont' bother, it's not in sort list or being set.
34552 so.push(cm.config[i].dataIndex);
34558 dm.sort(cm.getDataIndex(index));
34562 destroy : function(){
34564 this.colMenu.removeAll();
34565 Roo.menu.MenuMgr.unregister(this.colMenu);
34566 this.colMenu.getEl().remove();
34567 delete this.colMenu;
34570 this.hmenu.removeAll();
34571 Roo.menu.MenuMgr.unregister(this.hmenu);
34572 this.hmenu.getEl().remove();
34575 if(this.grid.enableColumnMove){
34576 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34578 for(var dd in dds){
34579 if(!dds[dd].config.isTarget && dds[dd].dragElId){
34580 var elid = dds[dd].dragElId;
34582 Roo.get(elid).remove();
34583 } else if(dds[dd].config.isTarget){
34584 dds[dd].proxyTop.remove();
34585 dds[dd].proxyBottom.remove();
34588 if(Roo.dd.DDM.locationCache[dd]){
34589 delete Roo.dd.DDM.locationCache[dd];
34592 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34595 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34596 this.bind(null, null);
34597 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34600 handleLockChange : function(){
34601 this.refresh(true);
34604 onDenyColumnLock : function(){
34608 onDenyColumnHide : function(){
34612 handleHdMenuClick : function(item){
34613 var index = this.hdCtxIndex;
34614 var cm = this.cm, ds = this.ds;
34617 ds.sort(cm.getDataIndex(index), "ASC");
34620 ds.sort(cm.getDataIndex(index), "DESC");
34623 var lc = cm.getLockedCount();
34624 if(cm.getColumnCount(true) <= lc+1){
34625 this.onDenyColumnLock();
34629 cm.setLocked(index, true, true);
34630 cm.moveColumn(index, lc);
34631 this.grid.fireEvent("columnmove", index, lc);
34633 cm.setLocked(index, true);
34637 var lc = cm.getLockedCount();
34638 if((lc-1) != index){
34639 cm.setLocked(index, false, true);
34640 cm.moveColumn(index, lc-1);
34641 this.grid.fireEvent("columnmove", index, lc-1);
34643 cm.setLocked(index, false);
34646 case 'wider': // used to expand cols on touch..
34648 var cw = cm.getColumnWidth(index);
34649 cw += (item.id == 'wider' ? 1 : -1) * 50;
34650 cw = Math.max(0, cw);
34651 cw = Math.min(cw,4000);
34652 cm.setColumnWidth(index, cw);
34656 index = cm.getIndexById(item.id.substr(4));
34658 if(item.checked && cm.getColumnCount(true) <= 1){
34659 this.onDenyColumnHide();
34662 cm.setHidden(index, item.checked);
34668 beforeColMenuShow : function(){
34669 var cm = this.cm, colCount = cm.getColumnCount();
34670 this.colMenu.removeAll();
34671 for(var i = 0; i < colCount; i++){
34672 this.colMenu.add(new Roo.menu.CheckItem({
34673 id: "col-"+cm.getColumnId(i),
34674 text: cm.getColumnHeader(i),
34675 checked: !cm.isHidden(i),
34681 handleHdCtx : function(g, index, e){
34683 var hd = this.getHeaderCell(index);
34684 this.hdCtxIndex = index;
34685 var ms = this.hmenu.items, cm = this.cm;
34686 ms.get("asc").setDisabled(!cm.isSortable(index));
34687 ms.get("desc").setDisabled(!cm.isSortable(index));
34688 if(this.grid.enableColLock !== false){
34689 ms.get("lock").setDisabled(cm.isLocked(index));
34690 ms.get("unlock").setDisabled(!cm.isLocked(index));
34692 this.hmenu.show(hd, "tl-bl");
34695 handleHdOver : function(e){
34696 var hd = this.findHeaderCell(e.getTarget());
34697 if(hd && !this.headersDisabled){
34698 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34699 this.fly(hd).addClass("x-grid-hd-over");
34704 handleHdOut : function(e){
34705 var hd = this.findHeaderCell(e.getTarget());
34707 this.fly(hd).removeClass("x-grid-hd-over");
34711 handleSplitDblClick : function(e, t){
34712 var i = this.getCellIndex(t);
34713 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34714 this.autoSizeColumn(i, true);
34719 render : function(){
34722 var colCount = cm.getColumnCount();
34724 if(this.grid.monitorWindowResize === true){
34725 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34727 var header = this.renderHeaders();
34728 var body = this.templates.body.apply({rows:""});
34729 var html = this.templates.master.apply({
34732 lockedHeader: header[0],
34736 //this.updateColumns();
34738 this.grid.getGridEl().dom.innerHTML = html;
34740 this.initElements();
34742 // a kludge to fix the random scolling effect in webkit
34743 this.el.on("scroll", function() {
34744 this.el.dom.scrollTop=0; // hopefully not recursive..
34747 this.scroller.on("scroll", this.handleScroll, this);
34748 this.lockedBody.on("mousewheel", this.handleWheel, this);
34749 this.mainBody.on("mousewheel", this.handleWheel, this);
34751 this.mainHd.on("mouseover", this.handleHdOver, this);
34752 this.mainHd.on("mouseout", this.handleHdOut, this);
34753 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34754 {delegate: "."+this.splitClass});
34756 this.lockedHd.on("mouseover", this.handleHdOver, this);
34757 this.lockedHd.on("mouseout", this.handleHdOut, this);
34758 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34759 {delegate: "."+this.splitClass});
34761 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34762 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34765 this.updateSplitters();
34767 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34768 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34769 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34772 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34773 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34775 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34776 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34778 if(this.grid.enableColLock !== false){
34779 this.hmenu.add('-',
34780 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34781 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34785 this.hmenu.add('-',
34786 {id:"wider", text: this.columnsWiderText},
34787 {id:"narrow", text: this.columnsNarrowText }
34793 if(this.grid.enableColumnHide !== false){
34795 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34796 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34797 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34799 this.hmenu.add('-',
34800 {id:"columns", text: this.columnsText, menu: this.colMenu}
34803 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34805 this.grid.on("headercontextmenu", this.handleHdCtx, this);
34808 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34809 this.dd = new Roo.grid.GridDragZone(this.grid, {
34810 ddGroup : this.grid.ddGroup || 'GridDD'
34816 for(var i = 0; i < colCount; i++){
34817 if(cm.isHidden(i)){
34818 this.hideColumn(i);
34820 if(cm.config[i].align){
34821 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34822 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34826 this.updateHeaderSortState();
34828 this.beforeInitialResize();
34831 // two part rendering gives faster view to the user
34832 this.renderPhase2.defer(1, this);
34835 renderPhase2 : function(){
34836 // render the rows now
34838 if(this.grid.autoSizeColumns){
34839 this.autoSizeColumns();
34843 beforeInitialResize : function(){
34847 onColumnSplitterMoved : function(i, w){
34848 this.userResized = true;
34849 var cm = this.grid.colModel;
34850 cm.setColumnWidth(i, w, true);
34851 var cid = cm.getColumnId(i);
34852 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34853 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34854 this.updateSplitters();
34856 this.grid.fireEvent("columnresize", i, w);
34859 syncRowHeights : function(startIndex, endIndex){
34860 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34861 startIndex = startIndex || 0;
34862 var mrows = this.getBodyTable().rows;
34863 var lrows = this.getLockedTable().rows;
34864 var len = mrows.length-1;
34865 endIndex = Math.min(endIndex || len, len);
34866 for(var i = startIndex; i <= endIndex; i++){
34867 var m = mrows[i], l = lrows[i];
34868 var h = Math.max(m.offsetHeight, l.offsetHeight);
34869 m.style.height = l.style.height = h + "px";
34874 layout : function(initialRender, is2ndPass)
34877 var auto = g.autoHeight;
34878 var scrollOffset = 16;
34879 var c = g.getGridEl(), cm = this.cm,
34880 expandCol = g.autoExpandColumn,
34882 //c.beginMeasure();
34884 if(!c.dom.offsetWidth){ // display:none?
34886 this.lockedWrap.show();
34887 this.mainWrap.show();
34892 var hasLock = this.cm.isLocked(0);
34894 var tbh = this.headerPanel.getHeight();
34895 var bbh = this.footerPanel.getHeight();
34898 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34899 var newHeight = ch + c.getBorderWidth("tb");
34901 newHeight = Math.min(g.maxHeight, newHeight);
34903 c.setHeight(newHeight);
34907 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34910 var s = this.scroller;
34912 var csize = c.getSize(true);
34914 this.el.setSize(csize.width, csize.height);
34916 this.headerPanel.setWidth(csize.width);
34917 this.footerPanel.setWidth(csize.width);
34919 var hdHeight = this.mainHd.getHeight();
34920 var vw = csize.width;
34921 var vh = csize.height - (tbh + bbh);
34925 var bt = this.getBodyTable();
34927 if(cm.getLockedCount() == cm.config.length){
34928 bt = this.getLockedTable();
34931 var ltWidth = hasLock ?
34932 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34934 var scrollHeight = bt.offsetHeight;
34935 var scrollWidth = ltWidth + bt.offsetWidth;
34936 var vscroll = false, hscroll = false;
34938 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34940 var lw = this.lockedWrap, mw = this.mainWrap;
34941 var lb = this.lockedBody, mb = this.mainBody;
34943 setTimeout(function(){
34944 var t = s.dom.offsetTop;
34945 var w = s.dom.clientWidth,
34946 h = s.dom.clientHeight;
34949 lw.setSize(ltWidth, h);
34951 mw.setLeftTop(ltWidth, t);
34952 mw.setSize(w-ltWidth, h);
34954 lb.setHeight(h-hdHeight);
34955 mb.setHeight(h-hdHeight);
34957 if(is2ndPass !== true && !gv.userResized && expandCol){
34958 // high speed resize without full column calculation
34960 var ci = cm.getIndexById(expandCol);
34962 ci = cm.findColumnIndex(expandCol);
34964 ci = Math.max(0, ci); // make sure it's got at least the first col.
34965 var expandId = cm.getColumnId(ci);
34966 var tw = cm.getTotalWidth(false);
34967 var currentWidth = cm.getColumnWidth(ci);
34968 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34969 if(currentWidth != cw){
34970 cm.setColumnWidth(ci, cw, true);
34971 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34972 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34973 gv.updateSplitters();
34974 gv.layout(false, true);
34986 onWindowResize : function(){
34987 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34993 appendFooter : function(parentEl){
34997 sortAscText : "Sort Ascending",
34998 sortDescText : "Sort Descending",
34999 lockText : "Lock Column",
35000 unlockText : "Unlock Column",
35001 columnsText : "Columns",
35003 columnsWiderText : "Wider",
35004 columnsNarrowText : "Thinner"
35008 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35009 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35010 this.proxy.el.addClass('x-grid3-col-dd');
35013 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35014 handleMouseDown : function(e){
35018 callHandleMouseDown : function(e){
35019 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35024 * Ext JS Library 1.1.1
35025 * Copyright(c) 2006-2007, Ext JS, LLC.
35027 * Originally Released Under LGPL - original licence link has changed is not relivant.
35030 * <script type="text/javascript">
35033 * @extends Roo.dd.DDProxy
35034 * @class Roo.grid.SplitDragZone
35035 * Support for Column Header resizing
35037 * @param {Object} config
35040 // This is a support class used internally by the Grid components
35041 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35043 this.view = grid.getView();
35044 this.proxy = this.view.resizeProxy;
35045 Roo.grid.SplitDragZone.superclass.constructor.call(
35048 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
35050 dragElId : Roo.id(this.proxy.dom),
35055 this.setHandleElId(Roo.id(hd));
35056 if (hd2 !== false) {
35057 this.setOuterHandleElId(Roo.id(hd2));
35060 this.scroll = false;
35062 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35063 fly: Roo.Element.fly,
35065 b4StartDrag : function(x, y){
35066 this.view.headersDisabled = true;
35067 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
35068 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
35070 this.proxy.setHeight(h);
35072 // for old system colWidth really stored the actual width?
35073 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
35074 // which in reality did not work.. - it worked only for fixed sizes
35075 // for resizable we need to use actual sizes.
35076 var w = this.cm.getColumnWidth(this.cellIndex);
35077 if (!this.view.mainWrap) {
35079 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
35084 // this was w-this.grid.minColumnWidth;
35085 // doesnt really make sense? - w = thie curren width or the rendered one?
35086 var minw = Math.max(w-this.grid.minColumnWidth, 0);
35087 this.resetConstraints();
35088 this.setXConstraint(minw, 1000);
35089 this.setYConstraint(0, 0);
35090 this.minX = x - minw;
35091 this.maxX = x + 1000;
35093 if (!this.view.mainWrap) { // this is Bootstrap code..
35094 this.getDragEl().style.display='block';
35097 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35101 handleMouseDown : function(e){
35102 ev = Roo.EventObject.setEvent(e);
35103 var t = this.fly(ev.getTarget());
35104 if(t.hasClass("x-grid-split")){
35105 this.cellIndex = this.view.getCellIndex(t.dom);
35106 this.split = t.dom;
35107 this.cm = this.grid.colModel;
35108 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35109 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35114 endDrag : function(e){
35115 this.view.headersDisabled = false;
35116 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35117 var diff = endX - this.startPos;
35119 var w = this.cm.getColumnWidth(this.cellIndex);
35120 if (!this.view.mainWrap) {
35123 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
35126 autoOffset : function(){
35127 this.setDelta(0,0);
35131 * Ext JS Library 1.1.1
35132 * Copyright(c) 2006-2007, Ext JS, LLC.
35134 * Originally Released Under LGPL - original licence link has changed is not relivant.
35137 * <script type="text/javascript">
35141 // This is a support class used internally by the Grid components
35142 Roo.grid.GridDragZone = function(grid, config){
35143 this.view = grid.getView();
35144 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35145 if(this.view.lockedBody){
35146 this.setHandleElId(Roo.id(this.view.mainBody.dom));
35147 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35149 this.scroll = false;
35151 this.ddel = document.createElement('div');
35152 this.ddel.className = 'x-grid-dd-wrap';
35155 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35156 ddGroup : "GridDD",
35158 getDragData : function(e){
35159 var t = Roo.lib.Event.getTarget(e);
35160 var rowIndex = this.view.findRowIndex(t);
35161 var sm = this.grid.selModel;
35163 //Roo.log(rowIndex);
35165 if (sm.getSelectedCell) {
35166 // cell selection..
35167 if (!sm.getSelectedCell()) {
35170 if (rowIndex != sm.getSelectedCell()[0]) {
35175 if (sm.getSelections && sm.getSelections().length < 1) {
35180 // before it used to all dragging of unseleted... - now we dont do that.
35181 if(rowIndex !== false){
35186 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35188 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35191 if (e.hasModifier()){
35192 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35195 Roo.log("getDragData");
35200 rowIndex: rowIndex,
35201 selections: sm.getSelections ? sm.getSelections() : (
35202 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
35209 onInitDrag : function(e){
35210 var data = this.dragData;
35211 this.ddel.innerHTML = this.grid.getDragDropText();
35212 this.proxy.update(this.ddel);
35213 // fire start drag?
35216 afterRepair : function(){
35217 this.dragging = false;
35220 getRepairXY : function(e, data){
35224 onEndDrag : function(data, e){
35228 onValidDrop : function(dd, e, id){
35233 beforeInvalidDrop : function(e, id){
35238 * Ext JS Library 1.1.1
35239 * Copyright(c) 2006-2007, Ext JS, LLC.
35241 * Originally Released Under LGPL - original licence link has changed is not relivant.
35244 * <script type="text/javascript">
35249 * @class Roo.grid.ColumnModel
35250 * @extends Roo.util.Observable
35251 * This is the default implementation of a ColumnModel used by the Grid. It defines
35252 * the columns in the grid.
35255 var colModel = new Roo.grid.ColumnModel([
35256 {header: "Ticker", width: 60, sortable: true, locked: true},
35257 {header: "Company Name", width: 150, sortable: true},
35258 {header: "Market Cap.", width: 100, sortable: true},
35259 {header: "$ Sales", width: 100, sortable: true, renderer: money},
35260 {header: "Employees", width: 100, sortable: true, resizable: false}
35265 * The config options listed for this class are options which may appear in each
35266 * individual column definition.
35267 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35269 * @param {Object} config An Array of column config objects. See this class's
35270 * config objects for details.
35272 Roo.grid.ColumnModel = function(config){
35274 * The config passed into the constructor
35276 this.config = []; //config;
35279 // if no id, create one
35280 // if the column does not have a dataIndex mapping,
35281 // map it to the order it is in the config
35282 for(var i = 0, len = config.length; i < len; i++){
35283 this.addColumn(config[i]);
35288 * The width of columns which have no width specified (defaults to 100)
35291 this.defaultWidth = 100;
35294 * Default sortable of columns which have no sortable specified (defaults to false)
35297 this.defaultSortable = false;
35301 * @event widthchange
35302 * Fires when the width of a column changes.
35303 * @param {ColumnModel} this
35304 * @param {Number} columnIndex The column index
35305 * @param {Number} newWidth The new width
35307 "widthchange": true,
35309 * @event headerchange
35310 * Fires when the text of a header changes.
35311 * @param {ColumnModel} this
35312 * @param {Number} columnIndex The column index
35313 * @param {Number} newText The new header text
35315 "headerchange": true,
35317 * @event hiddenchange
35318 * Fires when a column is hidden or "unhidden".
35319 * @param {ColumnModel} this
35320 * @param {Number} columnIndex The column index
35321 * @param {Boolean} hidden true if hidden, false otherwise
35323 "hiddenchange": true,
35325 * @event columnmoved
35326 * Fires when a column is moved.
35327 * @param {ColumnModel} this
35328 * @param {Number} oldIndex
35329 * @param {Number} newIndex
35331 "columnmoved" : true,
35333 * @event columlockchange
35334 * Fires when a column's locked state is changed
35335 * @param {ColumnModel} this
35336 * @param {Number} colIndex
35337 * @param {Boolean} locked true if locked
35339 "columnlockchange" : true
35341 Roo.grid.ColumnModel.superclass.constructor.call(this);
35343 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35345 * @cfg {String} header The header text to display in the Grid view.
35348 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
35351 * @cfg {String} smHeader Header at Bootsrap Small width
35354 * @cfg {String} mdHeader Header at Bootsrap Medium width
35357 * @cfg {String} lgHeader Header at Bootsrap Large width
35360 * @cfg {String} xlHeader Header at Bootsrap extra Large width
35363 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35364 * {@link Roo.data.Record} definition from which to draw the column's value. If not
35365 * specified, the column's index is used as an index into the Record's data Array.
35368 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35369 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35372 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35373 * Defaults to the value of the {@link #defaultSortable} property.
35374 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35377 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
35380 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
35383 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35386 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35389 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35390 * given the cell's data value. See {@link #setRenderer}. If not specified, the
35391 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35392 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35395 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
35398 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
35401 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
35404 * @cfg {String} cursor (Optional)
35407 * @cfg {String} tooltip (Optional)
35410 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
35413 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
35416 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
35419 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
35422 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
35425 * Returns the id of the column at the specified index.
35426 * @param {Number} index The column index
35427 * @return {String} the id
35429 getColumnId : function(index){
35430 return this.config[index].id;
35434 * Returns the column for a specified id.
35435 * @param {String} id The column id
35436 * @return {Object} the column
35438 getColumnById : function(id){
35439 return this.lookup[id];
35444 * Returns the column Object for a specified dataIndex.
35445 * @param {String} dataIndex The column dataIndex
35446 * @return {Object|Boolean} the column or false if not found
35448 getColumnByDataIndex: function(dataIndex){
35449 var index = this.findColumnIndex(dataIndex);
35450 return index > -1 ? this.config[index] : false;
35454 * Returns the index for a specified column id.
35455 * @param {String} id The column id
35456 * @return {Number} the index, or -1 if not found
35458 getIndexById : function(id){
35459 for(var i = 0, len = this.config.length; i < len; i++){
35460 if(this.config[i].id == id){
35468 * Returns the index for a specified column dataIndex.
35469 * @param {String} dataIndex The column dataIndex
35470 * @return {Number} the index, or -1 if not found
35473 findColumnIndex : function(dataIndex){
35474 for(var i = 0, len = this.config.length; i < len; i++){
35475 if(this.config[i].dataIndex == dataIndex){
35483 moveColumn : function(oldIndex, newIndex){
35484 var c = this.config[oldIndex];
35485 this.config.splice(oldIndex, 1);
35486 this.config.splice(newIndex, 0, c);
35487 this.dataMap = null;
35488 this.fireEvent("columnmoved", this, oldIndex, newIndex);
35491 isLocked : function(colIndex){
35492 return this.config[colIndex].locked === true;
35495 setLocked : function(colIndex, value, suppressEvent){
35496 if(this.isLocked(colIndex) == value){
35499 this.config[colIndex].locked = value;
35500 if(!suppressEvent){
35501 this.fireEvent("columnlockchange", this, colIndex, value);
35505 getTotalLockedWidth : function(){
35506 var totalWidth = 0;
35507 for(var i = 0; i < this.config.length; i++){
35508 if(this.isLocked(i) && !this.isHidden(i)){
35509 this.totalWidth += this.getColumnWidth(i);
35515 getLockedCount : function(){
35516 for(var i = 0, len = this.config.length; i < len; i++){
35517 if(!this.isLocked(i)){
35522 return this.config.length;
35526 * Returns the number of columns.
35529 getColumnCount : function(visibleOnly){
35530 if(visibleOnly === true){
35532 for(var i = 0, len = this.config.length; i < len; i++){
35533 if(!this.isHidden(i)){
35539 return this.config.length;
35543 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35544 * @param {Function} fn
35545 * @param {Object} scope (optional)
35546 * @return {Array} result
35548 getColumnsBy : function(fn, scope){
35550 for(var i = 0, len = this.config.length; i < len; i++){
35551 var c = this.config[i];
35552 if(fn.call(scope||this, c, i) === true){
35560 * Returns true if the specified column is sortable.
35561 * @param {Number} col The column index
35562 * @return {Boolean}
35564 isSortable : function(col){
35565 if(typeof this.config[col].sortable == "undefined"){
35566 return this.defaultSortable;
35568 return this.config[col].sortable;
35572 * Returns the rendering (formatting) function defined for the column.
35573 * @param {Number} col The column index.
35574 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35576 getRenderer : function(col){
35577 if(!this.config[col].renderer){
35578 return Roo.grid.ColumnModel.defaultRenderer;
35580 return this.config[col].renderer;
35584 * Sets the rendering (formatting) function for a column.
35585 * @param {Number} col The column index
35586 * @param {Function} fn The function to use to process the cell's raw data
35587 * to return HTML markup for the grid view. The render function is called with
35588 * the following parameters:<ul>
35589 * <li>Data value.</li>
35590 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35591 * <li>css A CSS style string to apply to the table cell.</li>
35592 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35593 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35594 * <li>Row index</li>
35595 * <li>Column index</li>
35596 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35598 setRenderer : function(col, fn){
35599 this.config[col].renderer = fn;
35603 * Returns the width for the specified column.
35604 * @param {Number} col The column index
35605 * @param (optional) {String} gridSize bootstrap width size.
35608 getColumnWidth : function(col, gridSize)
35610 var cfg = this.config[col];
35612 if (typeof(gridSize) == 'undefined') {
35613 return cfg.width * 1 || this.defaultWidth;
35615 if (gridSize === false) { // if we set it..
35616 return cfg.width || false;
35618 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
35620 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
35621 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
35624 return cfg[ sizes[i] ];
35631 * Sets the width for a column.
35632 * @param {Number} col The column index
35633 * @param {Number} width The new width
35635 setColumnWidth : function(col, width, suppressEvent){
35636 this.config[col].width = width;
35637 this.totalWidth = null;
35638 if(!suppressEvent){
35639 this.fireEvent("widthchange", this, col, width);
35644 * Returns the total width of all columns.
35645 * @param {Boolean} includeHidden True to include hidden column widths
35648 getTotalWidth : function(includeHidden){
35649 if(!this.totalWidth){
35650 this.totalWidth = 0;
35651 for(var i = 0, len = this.config.length; i < len; i++){
35652 if(includeHidden || !this.isHidden(i)){
35653 this.totalWidth += this.getColumnWidth(i);
35657 return this.totalWidth;
35661 * Returns the header for the specified column.
35662 * @param {Number} col The column index
35665 getColumnHeader : function(col){
35666 return this.config[col].header;
35670 * Sets the header for a column.
35671 * @param {Number} col The column index
35672 * @param {String} header The new header
35674 setColumnHeader : function(col, header){
35675 this.config[col].header = header;
35676 this.fireEvent("headerchange", this, col, header);
35680 * Returns the tooltip for the specified column.
35681 * @param {Number} col The column index
35684 getColumnTooltip : function(col){
35685 return this.config[col].tooltip;
35688 * Sets the tooltip for a column.
35689 * @param {Number} col The column index
35690 * @param {String} tooltip The new tooltip
35692 setColumnTooltip : function(col, tooltip){
35693 this.config[col].tooltip = tooltip;
35697 * Returns the dataIndex for the specified column.
35698 * @param {Number} col The column index
35701 getDataIndex : function(col){
35702 return this.config[col].dataIndex;
35706 * Sets the dataIndex for a column.
35707 * @param {Number} col The column index
35708 * @param {Number} dataIndex The new dataIndex
35710 setDataIndex : function(col, dataIndex){
35711 this.config[col].dataIndex = dataIndex;
35717 * Returns true if the cell is editable.
35718 * @param {Number} colIndex The column index
35719 * @param {Number} rowIndex The row index - this is nto actually used..?
35720 * @return {Boolean}
35722 isCellEditable : function(colIndex, rowIndex){
35723 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35727 * Returns the editor defined for the cell/column.
35728 * return false or null to disable editing.
35729 * @param {Number} colIndex The column index
35730 * @param {Number} rowIndex The row index
35733 getCellEditor : function(colIndex, rowIndex){
35734 return this.config[colIndex].editor;
35738 * Sets if a column is editable.
35739 * @param {Number} col The column index
35740 * @param {Boolean} editable True if the column is editable
35742 setEditable : function(col, editable){
35743 this.config[col].editable = editable;
35748 * Returns true if the column is hidden.
35749 * @param {Number} colIndex The column index
35750 * @return {Boolean}
35752 isHidden : function(colIndex){
35753 return this.config[colIndex].hidden;
35758 * Returns true if the column width cannot be changed
35760 isFixed : function(colIndex){
35761 return this.config[colIndex].fixed;
35765 * Returns true if the column can be resized
35766 * @return {Boolean}
35768 isResizable : function(colIndex){
35769 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35772 * Sets if a column is hidden.
35773 * @param {Number} colIndex The column index
35774 * @param {Boolean} hidden True if the column is hidden
35776 setHidden : function(colIndex, hidden){
35777 this.config[colIndex].hidden = hidden;
35778 this.totalWidth = null;
35779 this.fireEvent("hiddenchange", this, colIndex, hidden);
35783 * Sets the editor for a column.
35784 * @param {Number} col The column index
35785 * @param {Object} editor The editor object
35787 setEditor : function(col, editor){
35788 this.config[col].editor = editor;
35791 * Add a column (experimental...) - defaults to adding to the end..
35792 * @param {Object} config
35794 addColumn : function(c)
35797 var i = this.config.length;
35798 this.config[i] = c;
35800 if(typeof c.dataIndex == "undefined"){
35803 if(typeof c.renderer == "string"){
35804 c.renderer = Roo.util.Format[c.renderer];
35806 if(typeof c.id == "undefined"){
35809 if(c.editor && c.editor.xtype){
35810 c.editor = Roo.factory(c.editor, Roo.grid);
35812 if(c.editor && c.editor.isFormField){
35813 c.editor = new Roo.grid.GridEditor(c.editor);
35815 this.lookup[c.id] = c;
35820 Roo.grid.ColumnModel.defaultRenderer = function(value)
35822 if(typeof value == "object") {
35825 if(typeof value == "string" && value.length < 1){
35829 return String.format("{0}", value);
35832 // Alias for backwards compatibility
35833 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35836 * Ext JS Library 1.1.1
35837 * Copyright(c) 2006-2007, Ext JS, LLC.
35839 * Originally Released Under LGPL - original licence link has changed is not relivant.
35842 * <script type="text/javascript">
35846 * @class Roo.grid.AbstractSelectionModel
35847 * @extends Roo.util.Observable
35849 * Abstract base class for grid SelectionModels. It provides the interface that should be
35850 * implemented by descendant classes. This class should not be directly instantiated.
35853 Roo.grid.AbstractSelectionModel = function(){
35854 this.locked = false;
35855 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35858 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
35859 /** @ignore Called by the grid automatically. Do not call directly. */
35860 init : function(grid){
35866 * Locks the selections.
35869 this.locked = true;
35873 * Unlocks the selections.
35875 unlock : function(){
35876 this.locked = false;
35880 * Returns true if the selections are locked.
35881 * @return {Boolean}
35883 isLocked : function(){
35884 return this.locked;
35888 * Ext JS Library 1.1.1
35889 * Copyright(c) 2006-2007, Ext JS, LLC.
35891 * Originally Released Under LGPL - original licence link has changed is not relivant.
35894 * <script type="text/javascript">
35897 * @extends Roo.grid.AbstractSelectionModel
35898 * @class Roo.grid.RowSelectionModel
35899 * The default SelectionModel used by {@link Roo.grid.Grid}.
35900 * It supports multiple selections and keyboard selection/navigation.
35902 * @param {Object} config
35904 Roo.grid.RowSelectionModel = function(config){
35905 Roo.apply(this, config);
35906 this.selections = new Roo.util.MixedCollection(false, function(o){
35911 this.lastActive = false;
35915 * @event selectionchange
35916 * Fires when the selection changes
35917 * @param {SelectionModel} this
35919 "selectionchange" : true,
35921 * @event afterselectionchange
35922 * Fires after the selection changes (eg. by key press or clicking)
35923 * @param {SelectionModel} this
35925 "afterselectionchange" : true,
35927 * @event beforerowselect
35928 * Fires when a row is selected being selected, return false to cancel.
35929 * @param {SelectionModel} this
35930 * @param {Number} rowIndex The selected index
35931 * @param {Boolean} keepExisting False if other selections will be cleared
35933 "beforerowselect" : true,
35936 * Fires when a row is selected.
35937 * @param {SelectionModel} this
35938 * @param {Number} rowIndex The selected index
35939 * @param {Roo.data.Record} r The record
35941 "rowselect" : true,
35943 * @event rowdeselect
35944 * Fires when a row is deselected.
35945 * @param {SelectionModel} this
35946 * @param {Number} rowIndex The selected index
35948 "rowdeselect" : true
35950 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35951 this.locked = false;
35954 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
35956 * @cfg {Boolean} singleSelect
35957 * True to allow selection of only one row at a time (defaults to false)
35959 singleSelect : false,
35962 initEvents : function(){
35964 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35965 this.grid.on("mousedown", this.handleMouseDown, this);
35966 }else{ // allow click to work like normal
35967 this.grid.on("rowclick", this.handleDragableRowClick, this);
35969 // bootstrap does not have a view..
35970 var view = this.grid.view ? this.grid.view : this.grid;
35971 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35972 "up" : function(e){
35974 this.selectPrevious(e.shiftKey);
35975 }else if(this.last !== false && this.lastActive !== false){
35976 var last = this.last;
35977 this.selectRange(this.last, this.lastActive-1);
35978 view.focusRow(this.lastActive);
35979 if(last !== false){
35983 this.selectFirstRow();
35985 this.fireEvent("afterselectionchange", this);
35987 "down" : function(e){
35989 this.selectNext(e.shiftKey);
35990 }else if(this.last !== false && this.lastActive !== false){
35991 var last = this.last;
35992 this.selectRange(this.last, this.lastActive+1);
35993 view.focusRow(this.lastActive);
35994 if(last !== false){
35998 this.selectFirstRow();
36000 this.fireEvent("afterselectionchange", this);
36006 view.on("refresh", this.onRefresh, this);
36007 view.on("rowupdated", this.onRowUpdated, this);
36008 view.on("rowremoved", this.onRemove, this);
36012 onRefresh : function(){
36013 var ds = this.grid.ds, i, v = this.grid.view;
36014 var s = this.selections;
36015 s.each(function(r){
36016 if((i = ds.indexOfId(r.id)) != -1){
36018 s.add(ds.getAt(i)); // updating the selection relate data
36026 onRemove : function(v, index, r){
36027 this.selections.remove(r);
36031 onRowUpdated : function(v, index, r){
36032 if(this.isSelected(r)){
36033 v.onRowSelect(index);
36039 * @param {Array} records The records to select
36040 * @param {Boolean} keepExisting (optional) True to keep existing selections
36042 selectRecords : function(records, keepExisting){
36044 this.clearSelections();
36046 var ds = this.grid.ds;
36047 for(var i = 0, len = records.length; i < len; i++){
36048 this.selectRow(ds.indexOf(records[i]), true);
36053 * Gets the number of selected rows.
36056 getCount : function(){
36057 return this.selections.length;
36061 * Selects the first row in the grid.
36063 selectFirstRow : function(){
36068 * Select the last row.
36069 * @param {Boolean} keepExisting (optional) True to keep existing selections
36071 selectLastRow : function(keepExisting){
36072 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
36076 * Selects the row immediately following the last selected row.
36077 * @param {Boolean} keepExisting (optional) True to keep existing selections
36079 selectNext : function(keepExisting){
36080 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
36081 this.selectRow(this.last+1, keepExisting);
36082 var view = this.grid.view ? this.grid.view : this.grid;
36083 view.focusRow(this.last);
36088 * Selects the row that precedes the last selected row.
36089 * @param {Boolean} keepExisting (optional) True to keep existing selections
36091 selectPrevious : function(keepExisting){
36093 this.selectRow(this.last-1, keepExisting);
36094 var view = this.grid.view ? this.grid.view : this.grid;
36095 view.focusRow(this.last);
36100 * Returns the selected records
36101 * @return {Array} Array of selected records
36103 getSelections : function(){
36104 return [].concat(this.selections.items);
36108 * Returns the first selected record.
36111 getSelected : function(){
36112 return this.selections.itemAt(0);
36117 * Clears all selections.
36119 clearSelections : function(fast){
36124 var ds = this.grid.ds;
36125 var s = this.selections;
36126 s.each(function(r){
36127 this.deselectRow(ds.indexOfId(r.id));
36131 this.selections.clear();
36138 * Selects all rows.
36140 selectAll : function(){
36144 this.selections.clear();
36145 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
36146 this.selectRow(i, true);
36151 * Returns True if there is a selection.
36152 * @return {Boolean}
36154 hasSelection : function(){
36155 return this.selections.length > 0;
36159 * Returns True if the specified row is selected.
36160 * @param {Number/Record} record The record or index of the record to check
36161 * @return {Boolean}
36163 isSelected : function(index){
36164 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
36165 return (r && this.selections.key(r.id) ? true : false);
36169 * Returns True if the specified record id is selected.
36170 * @param {String} id The id of record to check
36171 * @return {Boolean}
36173 isIdSelected : function(id){
36174 return (this.selections.key(id) ? true : false);
36178 handleMouseDown : function(e, t)
36180 var view = this.grid.view ? this.grid.view : this.grid;
36182 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36185 if(e.shiftKey && this.last !== false){
36186 var last = this.last;
36187 this.selectRange(last, rowIndex, e.ctrlKey);
36188 this.last = last; // reset the last
36189 view.focusRow(rowIndex);
36191 var isSelected = this.isSelected(rowIndex);
36192 if(e.button !== 0 && isSelected){
36193 view.focusRow(rowIndex);
36194 }else if(e.ctrlKey && isSelected){
36195 this.deselectRow(rowIndex);
36196 }else if(!isSelected){
36197 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36198 view.focusRow(rowIndex);
36201 this.fireEvent("afterselectionchange", this);
36204 handleDragableRowClick : function(grid, rowIndex, e)
36206 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36207 this.selectRow(rowIndex, false);
36208 var view = this.grid.view ? this.grid.view : this.grid;
36209 view.focusRow(rowIndex);
36210 this.fireEvent("afterselectionchange", this);
36215 * Selects multiple rows.
36216 * @param {Array} rows Array of the indexes of the row to select
36217 * @param {Boolean} keepExisting (optional) True to keep existing selections
36219 selectRows : function(rows, keepExisting){
36221 this.clearSelections();
36223 for(var i = 0, len = rows.length; i < len; i++){
36224 this.selectRow(rows[i], true);
36229 * Selects a range of rows. All rows in between startRow and endRow are also selected.
36230 * @param {Number} startRow The index of the first row in the range
36231 * @param {Number} endRow The index of the last row in the range
36232 * @param {Boolean} keepExisting (optional) True to retain existing selections
36234 selectRange : function(startRow, endRow, keepExisting){
36239 this.clearSelections();
36241 if(startRow <= endRow){
36242 for(var i = startRow; i <= endRow; i++){
36243 this.selectRow(i, true);
36246 for(var i = startRow; i >= endRow; i--){
36247 this.selectRow(i, true);
36253 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36254 * @param {Number} startRow The index of the first row in the range
36255 * @param {Number} endRow The index of the last row in the range
36257 deselectRange : function(startRow, endRow, preventViewNotify){
36261 for(var i = startRow; i <= endRow; i++){
36262 this.deselectRow(i, preventViewNotify);
36268 * @param {Number} row The index of the row to select
36269 * @param {Boolean} keepExisting (optional) True to keep existing selections
36271 selectRow : function(index, keepExisting, preventViewNotify){
36272 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
36275 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36276 if(!keepExisting || this.singleSelect){
36277 this.clearSelections();
36279 var r = this.grid.ds.getAt(index);
36280 this.selections.add(r);
36281 this.last = this.lastActive = index;
36282 if(!preventViewNotify){
36283 var view = this.grid.view ? this.grid.view : this.grid;
36284 view.onRowSelect(index);
36286 this.fireEvent("rowselect", this, index, r);
36287 this.fireEvent("selectionchange", this);
36293 * @param {Number} row The index of the row to deselect
36295 deselectRow : function(index, preventViewNotify){
36299 if(this.last == index){
36302 if(this.lastActive == index){
36303 this.lastActive = false;
36305 var r = this.grid.ds.getAt(index);
36306 this.selections.remove(r);
36307 if(!preventViewNotify){
36308 var view = this.grid.view ? this.grid.view : this.grid;
36309 view.onRowDeselect(index);
36311 this.fireEvent("rowdeselect", this, index);
36312 this.fireEvent("selectionchange", this);
36316 restoreLast : function(){
36318 this.last = this._last;
36323 acceptsNav : function(row, col, cm){
36324 return !cm.isHidden(col) && cm.isCellEditable(col, row);
36328 onEditorKey : function(field, e){
36329 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36334 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36336 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36338 }else if(k == e.ENTER && !e.ctrlKey){
36342 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36344 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36346 }else if(k == e.ESC){
36350 g.startEditing(newCell[0], newCell[1]);
36355 * Ext JS Library 1.1.1
36356 * Copyright(c) 2006-2007, Ext JS, LLC.
36358 * Originally Released Under LGPL - original licence link has changed is not relivant.
36361 * <script type="text/javascript">
36364 * @class Roo.grid.CellSelectionModel
36365 * @extends Roo.grid.AbstractSelectionModel
36366 * This class provides the basic implementation for cell selection in a grid.
36368 * @param {Object} config The object containing the configuration of this model.
36369 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36371 Roo.grid.CellSelectionModel = function(config){
36372 Roo.apply(this, config);
36374 this.selection = null;
36378 * @event beforerowselect
36379 * Fires before a cell is selected.
36380 * @param {SelectionModel} this
36381 * @param {Number} rowIndex The selected row index
36382 * @param {Number} colIndex The selected cell index
36384 "beforecellselect" : true,
36386 * @event cellselect
36387 * Fires when a cell is selected.
36388 * @param {SelectionModel} this
36389 * @param {Number} rowIndex The selected row index
36390 * @param {Number} colIndex The selected cell index
36392 "cellselect" : true,
36394 * @event selectionchange
36395 * Fires when the active selection changes.
36396 * @param {SelectionModel} this
36397 * @param {Object} selection null for no selection or an object (o) with two properties
36399 <li>o.record: the record object for the row the selection is in</li>
36400 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36403 "selectionchange" : true,
36406 * Fires when the tab (or enter) was pressed on the last editable cell
36407 * You can use this to trigger add new row.
36408 * @param {SelectionModel} this
36412 * @event beforeeditnext
36413 * Fires before the next editable sell is made active
36414 * You can use this to skip to another cell or fire the tabend
36415 * if you set cell to false
36416 * @param {Object} eventdata object : { cell : [ row, col ] }
36418 "beforeeditnext" : true
36420 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36423 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
36425 enter_is_tab: false,
36428 initEvents : function(){
36429 this.grid.on("mousedown", this.handleMouseDown, this);
36430 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36431 var view = this.grid.view;
36432 view.on("refresh", this.onViewChange, this);
36433 view.on("rowupdated", this.onRowUpdated, this);
36434 view.on("beforerowremoved", this.clearSelections, this);
36435 view.on("beforerowsinserted", this.clearSelections, this);
36436 if(this.grid.isEditor){
36437 this.grid.on("beforeedit", this.beforeEdit, this);
36442 beforeEdit : function(e){
36443 this.select(e.row, e.column, false, true, e.record);
36447 onRowUpdated : function(v, index, r){
36448 if(this.selection && this.selection.record == r){
36449 v.onCellSelect(index, this.selection.cell[1]);
36454 onViewChange : function(){
36455 this.clearSelections(true);
36459 * Returns the currently selected cell,.
36460 * @return {Array} The selected cell (row, column) or null if none selected.
36462 getSelectedCell : function(){
36463 return this.selection ? this.selection.cell : null;
36467 * Clears all selections.
36468 * @param {Boolean} true to prevent the gridview from being notified about the change.
36470 clearSelections : function(preventNotify){
36471 var s = this.selection;
36473 if(preventNotify !== true){
36474 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36476 this.selection = null;
36477 this.fireEvent("selectionchange", this, null);
36482 * Returns true if there is a selection.
36483 * @return {Boolean}
36485 hasSelection : function(){
36486 return this.selection ? true : false;
36490 handleMouseDown : function(e, t){
36491 var v = this.grid.getView();
36492 if(this.isLocked()){
36495 var row = v.findRowIndex(t);
36496 var cell = v.findCellIndex(t);
36497 if(row !== false && cell !== false){
36498 this.select(row, cell);
36504 * @param {Number} rowIndex
36505 * @param {Number} collIndex
36507 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36508 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36509 this.clearSelections();
36510 r = r || this.grid.dataSource.getAt(rowIndex);
36513 cell : [rowIndex, colIndex]
36515 if(!preventViewNotify){
36516 var v = this.grid.getView();
36517 v.onCellSelect(rowIndex, colIndex);
36518 if(preventFocus !== true){
36519 v.focusCell(rowIndex, colIndex);
36522 this.fireEvent("cellselect", this, rowIndex, colIndex);
36523 this.fireEvent("selectionchange", this, this.selection);
36528 isSelectable : function(rowIndex, colIndex, cm){
36529 return !cm.isHidden(colIndex);
36533 handleKeyDown : function(e){
36534 //Roo.log('Cell Sel Model handleKeyDown');
36535 if(!e.isNavKeyPress()){
36538 var g = this.grid, s = this.selection;
36541 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
36543 this.select(cell[0], cell[1]);
36548 var walk = function(row, col, step){
36549 return g.walkCells(row, col, step, sm.isSelectable, sm);
36551 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36558 // handled by onEditorKey
36559 if (g.isEditor && g.editing) {
36563 newCell = walk(r, c-1, -1);
36565 newCell = walk(r, c+1, 1);
36570 newCell = walk(r+1, c, 1);
36574 newCell = walk(r-1, c, -1);
36578 newCell = walk(r, c+1, 1);
36582 newCell = walk(r, c-1, -1);
36587 if(g.isEditor && !g.editing){
36588 g.startEditing(r, c);
36597 this.select(newCell[0], newCell[1]);
36603 acceptsNav : function(row, col, cm){
36604 return !cm.isHidden(col) && cm.isCellEditable(col, row);
36608 * @param {Number} field (not used) - as it's normally used as a listener
36609 * @param {Number} e - event - fake it by using
36611 * var e = Roo.EventObjectImpl.prototype;
36612 * e.keyCode = e.TAB
36616 onEditorKey : function(field, e){
36618 var k = e.getKey(),
36621 ed = g.activeEditor,
36623 ///Roo.log('onEditorKey' + k);
36626 if (this.enter_is_tab && k == e.ENTER) {
36632 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36634 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36640 } else if(k == e.ENTER && !e.ctrlKey){
36643 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36645 } else if(k == e.ESC){
36650 var ecall = { cell : newCell, forward : forward };
36651 this.fireEvent('beforeeditnext', ecall );
36652 newCell = ecall.cell;
36653 forward = ecall.forward;
36657 //Roo.log('next cell after edit');
36658 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36659 } else if (forward) {
36660 // tabbed past last
36661 this.fireEvent.defer(100, this, ['tabend',this]);
36666 * Ext JS Library 1.1.1
36667 * Copyright(c) 2006-2007, Ext JS, LLC.
36669 * Originally Released Under LGPL - original licence link has changed is not relivant.
36672 * <script type="text/javascript">
36676 * @class Roo.grid.EditorGrid
36677 * @extends Roo.grid.Grid
36678 * Class for creating and editable grid.
36679 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36680 * The container MUST have some type of size defined for the grid to fill. The container will be
36681 * automatically set to position relative if it isn't already.
36682 * @param {Object} dataSource The data model to bind to
36683 * @param {Object} colModel The column model with info about this grid's columns
36685 Roo.grid.EditorGrid = function(container, config){
36686 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36687 this.getGridEl().addClass("xedit-grid");
36689 if(!this.selModel){
36690 this.selModel = new Roo.grid.CellSelectionModel();
36693 this.activeEditor = null;
36697 * @event beforeedit
36698 * Fires before cell editing is triggered. The edit event object has the following properties <br />
36699 * <ul style="padding:5px;padding-left:16px;">
36700 * <li>grid - This grid</li>
36701 * <li>record - The record being edited</li>
36702 * <li>field - The field name being edited</li>
36703 * <li>value - The value for the field being edited.</li>
36704 * <li>row - The grid row index</li>
36705 * <li>column - The grid column index</li>
36706 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36708 * @param {Object} e An edit event (see above for description)
36710 "beforeedit" : true,
36713 * Fires after a cell is edited. <br />
36714 * <ul style="padding:5px;padding-left:16px;">
36715 * <li>grid - This grid</li>
36716 * <li>record - The record being edited</li>
36717 * <li>field - The field name being edited</li>
36718 * <li>value - The value being set</li>
36719 * <li>originalValue - The original value for the field, before the edit.</li>
36720 * <li>row - The grid row index</li>
36721 * <li>column - The grid column index</li>
36723 * @param {Object} e An edit event (see above for description)
36725 "afteredit" : true,
36727 * @event validateedit
36728 * Fires after a cell is edited, but before the value is set in the record.
36729 * You can use this to modify the value being set in the field, Return false
36730 * to cancel the change. The edit event object has the following properties <br />
36731 * <ul style="padding:5px;padding-left:16px;">
36732 * <li>editor - This editor</li>
36733 * <li>grid - This grid</li>
36734 * <li>record - The record being edited</li>
36735 * <li>field - The field name being edited</li>
36736 * <li>value - The value being set</li>
36737 * <li>originalValue - The original value for the field, before the edit.</li>
36738 * <li>row - The grid row index</li>
36739 * <li>column - The grid column index</li>
36740 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36742 * @param {Object} e An edit event (see above for description)
36744 "validateedit" : true
36746 this.on("bodyscroll", this.stopEditing, this);
36747 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
36750 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36752 * @cfg {Number} clicksToEdit
36753 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36760 trackMouseOver: false, // causes very odd FF errors
36762 onCellDblClick : function(g, row, col){
36763 this.startEditing(row, col);
36766 onEditComplete : function(ed, value, startValue){
36767 this.editing = false;
36768 this.activeEditor = null;
36769 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36771 var field = this.colModel.getDataIndex(ed.col);
36776 originalValue: startValue,
36783 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36786 if(String(value) !== String(startValue)){
36788 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36789 r.set(field, e.value);
36790 // if we are dealing with a combo box..
36791 // then we also set the 'name' colum to be the displayField
36792 if (ed.field.displayField && ed.field.name) {
36793 r.set(ed.field.name, ed.field.el.dom.value);
36796 delete e.cancel; //?? why!!!
36797 this.fireEvent("afteredit", e);
36800 this.fireEvent("afteredit", e); // always fire it!
36802 this.view.focusCell(ed.row, ed.col);
36806 * Starts editing the specified for the specified row/column
36807 * @param {Number} rowIndex
36808 * @param {Number} colIndex
36810 startEditing : function(row, col){
36811 this.stopEditing();
36812 if(this.colModel.isCellEditable(col, row)){
36813 this.view.ensureVisible(row, col, true);
36815 var r = this.dataSource.getAt(row);
36816 var field = this.colModel.getDataIndex(col);
36817 var cell = Roo.get(this.view.getCell(row,col));
36822 value: r.data[field],
36827 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36828 this.editing = true;
36829 var ed = this.colModel.getCellEditor(col, row);
36835 ed.render(ed.parentEl || document.body);
36841 (function(){ // complex but required for focus issues in safari, ie and opera
36845 ed.on("complete", this.onEditComplete, this, {single: true});
36846 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
36847 this.activeEditor = ed;
36848 var v = r.data[field];
36849 ed.startEdit(this.view.getCell(row, col), v);
36850 // combo's with 'displayField and name set
36851 if (ed.field.displayField && ed.field.name) {
36852 ed.field.el.dom.value = r.data[ed.field.name];
36856 }).defer(50, this);
36862 * Stops any active editing
36864 stopEditing : function(){
36865 if(this.activeEditor){
36866 this.activeEditor.completeEdit();
36868 this.activeEditor = null;
36872 * Called to get grid's drag proxy text, by default returns this.ddText.
36875 getDragDropText : function(){
36876 var count = this.selModel.getSelectedCell() ? 1 : 0;
36877 return String.format(this.ddText, count, count == 1 ? '' : 's');
36882 * Ext JS Library 1.1.1
36883 * Copyright(c) 2006-2007, Ext JS, LLC.
36885 * Originally Released Under LGPL - original licence link has changed is not relivant.
36888 * <script type="text/javascript">
36891 // private - not really -- you end up using it !
36892 // This is a support class used internally by the Grid components
36895 * @class Roo.grid.GridEditor
36896 * @extends Roo.Editor
36897 * Class for creating and editable grid elements.
36898 * @param {Object} config any settings (must include field)
36900 Roo.grid.GridEditor = function(field, config){
36901 if (!config && field.field) {
36903 field = Roo.factory(config.field, Roo.form);
36905 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36906 field.monitorTab = false;
36909 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36912 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36915 alignment: "tl-tl",
36918 cls: "x-small-editor x-grid-editor",
36923 * Ext JS Library 1.1.1
36924 * Copyright(c) 2006-2007, Ext JS, LLC.
36926 * Originally Released Under LGPL - original licence link has changed is not relivant.
36929 * <script type="text/javascript">
36934 Roo.grid.PropertyRecord = Roo.data.Record.create([
36935 {name:'name',type:'string'}, 'value'
36939 Roo.grid.PropertyStore = function(grid, source){
36941 this.store = new Roo.data.Store({
36942 recordType : Roo.grid.PropertyRecord
36944 this.store.on('update', this.onUpdate, this);
36946 this.setSource(source);
36948 Roo.grid.PropertyStore.superclass.constructor.call(this);
36953 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36954 setSource : function(o){
36956 this.store.removeAll();
36959 if(this.isEditableValue(o[k])){
36960 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36963 this.store.loadRecords({records: data}, {}, true);
36966 onUpdate : function(ds, record, type){
36967 if(type == Roo.data.Record.EDIT){
36968 var v = record.data['value'];
36969 var oldValue = record.modified['value'];
36970 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36971 this.source[record.id] = v;
36973 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36980 getProperty : function(row){
36981 return this.store.getAt(row);
36984 isEditableValue: function(val){
36985 if(val && val instanceof Date){
36987 }else if(typeof val == 'object' || typeof val == 'function'){
36993 setValue : function(prop, value){
36994 this.source[prop] = value;
36995 this.store.getById(prop).set('value', value);
36998 getSource : function(){
36999 return this.source;
37003 Roo.grid.PropertyColumnModel = function(grid, store){
37006 g.PropertyColumnModel.superclass.constructor.call(this, [
37007 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37008 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37010 this.store = store;
37011 this.bselect = Roo.DomHelper.append(document.body, {
37012 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37013 {tag: 'option', value: 'true', html: 'true'},
37014 {tag: 'option', value: 'false', html: 'false'}
37017 Roo.id(this.bselect);
37020 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37021 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37022 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37023 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37024 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37026 this.renderCellDelegate = this.renderCell.createDelegate(this);
37027 this.renderPropDelegate = this.renderProp.createDelegate(this);
37030 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37034 valueText : 'Value',
37036 dateFormat : 'm/j/Y',
37039 renderDate : function(dateVal){
37040 return dateVal.dateFormat(this.dateFormat);
37043 renderBool : function(bVal){
37044 return bVal ? 'true' : 'false';
37047 isCellEditable : function(colIndex, rowIndex){
37048 return colIndex == 1;
37051 getRenderer : function(col){
37053 this.renderCellDelegate : this.renderPropDelegate;
37056 renderProp : function(v){
37057 return this.getPropertyName(v);
37060 renderCell : function(val){
37062 if(val instanceof Date){
37063 rv = this.renderDate(val);
37064 }else if(typeof val == 'boolean'){
37065 rv = this.renderBool(val);
37067 return Roo.util.Format.htmlEncode(rv);
37070 getPropertyName : function(name){
37071 var pn = this.grid.propertyNames;
37072 return pn && pn[name] ? pn[name] : name;
37075 getCellEditor : function(colIndex, rowIndex){
37076 var p = this.store.getProperty(rowIndex);
37077 var n = p.data['name'], val = p.data['value'];
37079 if(typeof(this.grid.customEditors[n]) == 'string'){
37080 return this.editors[this.grid.customEditors[n]];
37082 if(typeof(this.grid.customEditors[n]) != 'undefined'){
37083 return this.grid.customEditors[n];
37085 if(val instanceof Date){
37086 return this.editors['date'];
37087 }else if(typeof val == 'number'){
37088 return this.editors['number'];
37089 }else if(typeof val == 'boolean'){
37090 return this.editors['boolean'];
37092 return this.editors['string'];
37098 * @class Roo.grid.PropertyGrid
37099 * @extends Roo.grid.EditorGrid
37100 * This class represents the interface of a component based property grid control.
37101 * <br><br>Usage:<pre><code>
37102 var grid = new Roo.grid.PropertyGrid("my-container-id", {
37110 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37111 * The container MUST have some type of size defined for the grid to fill. The container will be
37112 * automatically set to position relative if it isn't already.
37113 * @param {Object} config A config object that sets properties on this grid.
37115 Roo.grid.PropertyGrid = function(container, config){
37116 config = config || {};
37117 var store = new Roo.grid.PropertyStore(this);
37118 this.store = store;
37119 var cm = new Roo.grid.PropertyColumnModel(this, store);
37120 store.store.sort('name', 'ASC');
37121 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37124 enableColLock:false,
37125 enableColumnMove:false,
37127 trackMouseOver: false,
37130 this.getGridEl().addClass('x-props-grid');
37131 this.lastEditRow = null;
37132 this.on('columnresize', this.onColumnResize, this);
37135 * @event beforepropertychange
37136 * Fires before a property changes (return false to stop?)
37137 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37138 * @param {String} id Record Id
37139 * @param {String} newval New Value
37140 * @param {String} oldval Old Value
37142 "beforepropertychange": true,
37144 * @event propertychange
37145 * Fires after a property changes
37146 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37147 * @param {String} id Record Id
37148 * @param {String} newval New Value
37149 * @param {String} oldval Old Value
37151 "propertychange": true
37153 this.customEditors = this.customEditors || {};
37155 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37158 * @cfg {Object} customEditors map of colnames=> custom editors.
37159 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37160 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37161 * false disables editing of the field.
37165 * @cfg {Object} propertyNames map of property Names to their displayed value
37168 render : function(){
37169 Roo.grid.PropertyGrid.superclass.render.call(this);
37170 this.autoSize.defer(100, this);
37173 autoSize : function(){
37174 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37176 this.view.fitColumns();
37180 onColumnResize : function(){
37181 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37185 * Sets the data for the Grid
37186 * accepts a Key => Value object of all the elements avaiable.
37187 * @param {Object} data to appear in grid.
37189 setSource : function(source){
37190 this.store.setSource(source);
37194 * Gets all the data from the grid.
37195 * @return {Object} data data stored in grid
37197 getSource : function(){
37198 return this.store.getSource();
37207 * @class Roo.grid.Calendar
37208 * @extends Roo.grid.Grid
37209 * This class extends the Grid to provide a calendar widget
37210 * <br><br>Usage:<pre><code>
37211 var grid = new Roo.grid.Calendar("my-container-id", {
37214 selModel: mySelectionModel,
37215 autoSizeColumns: true,
37216 monitorWindowResize: false,
37217 trackMouseOver: true
37218 eventstore : real data store..
37224 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37225 * The container MUST have some type of size defined for the grid to fill. The container will be
37226 * automatically set to position relative if it isn't already.
37227 * @param {Object} config A config object that sets properties on this grid.
37229 Roo.grid.Calendar = function(container, config){
37230 // initialize the container
37231 this.container = Roo.get(container);
37232 this.container.update("");
37233 this.container.setStyle("overflow", "hidden");
37234 this.container.addClass('x-grid-container');
37236 this.id = this.container.id;
37238 Roo.apply(this, config);
37239 // check and correct shorthanded configs
37243 for (var r = 0;r < 6;r++) {
37246 for (var c =0;c < 7;c++) {
37250 if (this.eventStore) {
37251 this.eventStore= Roo.factory(this.eventStore, Roo.data);
37252 this.eventStore.on('load',this.onLoad, this);
37253 this.eventStore.on('beforeload',this.clearEvents, this);
37257 this.dataSource = new Roo.data.Store({
37258 proxy: new Roo.data.MemoryProxy(rows),
37259 reader: new Roo.data.ArrayReader({}, [
37260 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37263 this.dataSource.load();
37264 this.ds = this.dataSource;
37265 this.ds.xmodule = this.xmodule || false;
37268 var cellRender = function(v,x,r)
37270 return String.format(
37271 '<div class="fc-day fc-widget-content"><div>' +
37272 '<div class="fc-event-container"></div>' +
37273 '<div class="fc-day-number">{0}</div>'+
37275 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37276 '</div></div>', v);
37281 this.colModel = new Roo.grid.ColumnModel( [
37283 xtype: 'ColumnModel',
37285 dataIndex : 'weekday0',
37287 renderer : cellRender
37290 xtype: 'ColumnModel',
37292 dataIndex : 'weekday1',
37294 renderer : cellRender
37297 xtype: 'ColumnModel',
37299 dataIndex : 'weekday2',
37300 header : 'Tuesday',
37301 renderer : cellRender
37304 xtype: 'ColumnModel',
37306 dataIndex : 'weekday3',
37307 header : 'Wednesday',
37308 renderer : cellRender
37311 xtype: 'ColumnModel',
37313 dataIndex : 'weekday4',
37314 header : 'Thursday',
37315 renderer : cellRender
37318 xtype: 'ColumnModel',
37320 dataIndex : 'weekday5',
37322 renderer : cellRender
37325 xtype: 'ColumnModel',
37327 dataIndex : 'weekday6',
37328 header : 'Saturday',
37329 renderer : cellRender
37332 this.cm = this.colModel;
37333 this.cm.xmodule = this.xmodule || false;
37337 //this.selModel = new Roo.grid.CellSelectionModel();
37338 //this.sm = this.selModel;
37339 //this.selModel.init(this);
37343 this.container.setWidth(this.width);
37347 this.container.setHeight(this.height);
37354 * The raw click event for the entire grid.
37355 * @param {Roo.EventObject} e
37360 * The raw dblclick event for the entire grid.
37361 * @param {Roo.EventObject} e
37365 * @event contextmenu
37366 * The raw contextmenu event for the entire grid.
37367 * @param {Roo.EventObject} e
37369 "contextmenu" : true,
37372 * The raw mousedown event for the entire grid.
37373 * @param {Roo.EventObject} e
37375 "mousedown" : true,
37378 * The raw mouseup event for the entire grid.
37379 * @param {Roo.EventObject} e
37384 * The raw mouseover event for the entire grid.
37385 * @param {Roo.EventObject} e
37387 "mouseover" : true,
37390 * The raw mouseout event for the entire grid.
37391 * @param {Roo.EventObject} e
37396 * The raw keypress event for the entire grid.
37397 * @param {Roo.EventObject} e
37402 * The raw keydown event for the entire grid.
37403 * @param {Roo.EventObject} e
37411 * Fires when a cell is clicked
37412 * @param {Grid} this
37413 * @param {Number} rowIndex
37414 * @param {Number} columnIndex
37415 * @param {Roo.EventObject} e
37417 "cellclick" : true,
37419 * @event celldblclick
37420 * Fires when a cell is double clicked
37421 * @param {Grid} this
37422 * @param {Number} rowIndex
37423 * @param {Number} columnIndex
37424 * @param {Roo.EventObject} e
37426 "celldblclick" : true,
37429 * Fires when a row is clicked
37430 * @param {Grid} this
37431 * @param {Number} rowIndex
37432 * @param {Roo.EventObject} e
37436 * @event rowdblclick
37437 * Fires when a row is double clicked
37438 * @param {Grid} this
37439 * @param {Number} rowIndex
37440 * @param {Roo.EventObject} e
37442 "rowdblclick" : true,
37444 * @event headerclick
37445 * Fires when a header is clicked
37446 * @param {Grid} this
37447 * @param {Number} columnIndex
37448 * @param {Roo.EventObject} e
37450 "headerclick" : true,
37452 * @event headerdblclick
37453 * Fires when a header cell is double clicked
37454 * @param {Grid} this
37455 * @param {Number} columnIndex
37456 * @param {Roo.EventObject} e
37458 "headerdblclick" : true,
37460 * @event rowcontextmenu
37461 * Fires when a row is right clicked
37462 * @param {Grid} this
37463 * @param {Number} rowIndex
37464 * @param {Roo.EventObject} e
37466 "rowcontextmenu" : true,
37468 * @event cellcontextmenu
37469 * Fires when a cell is right clicked
37470 * @param {Grid} this
37471 * @param {Number} rowIndex
37472 * @param {Number} cellIndex
37473 * @param {Roo.EventObject} e
37475 "cellcontextmenu" : true,
37477 * @event headercontextmenu
37478 * Fires when a header is right clicked
37479 * @param {Grid} this
37480 * @param {Number} columnIndex
37481 * @param {Roo.EventObject} e
37483 "headercontextmenu" : true,
37485 * @event bodyscroll
37486 * Fires when the body element is scrolled
37487 * @param {Number} scrollLeft
37488 * @param {Number} scrollTop
37490 "bodyscroll" : true,
37492 * @event columnresize
37493 * Fires when the user resizes a column
37494 * @param {Number} columnIndex
37495 * @param {Number} newSize
37497 "columnresize" : true,
37499 * @event columnmove
37500 * Fires when the user moves a column
37501 * @param {Number} oldIndex
37502 * @param {Number} newIndex
37504 "columnmove" : true,
37507 * Fires when row(s) start being dragged
37508 * @param {Grid} this
37509 * @param {Roo.GridDD} dd The drag drop object
37510 * @param {event} e The raw browser event
37512 "startdrag" : true,
37515 * Fires when a drag operation is complete
37516 * @param {Grid} this
37517 * @param {Roo.GridDD} dd The drag drop object
37518 * @param {event} e The raw browser event
37523 * Fires when dragged row(s) are dropped on a valid DD target
37524 * @param {Grid} this
37525 * @param {Roo.GridDD} dd The drag drop object
37526 * @param {String} targetId The target drag drop object
37527 * @param {event} e The raw browser event
37532 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37533 * @param {Grid} this
37534 * @param {Roo.GridDD} dd The drag drop object
37535 * @param {String} targetId The target drag drop object
37536 * @param {event} e The raw browser event
37541 * Fires when the dragged row(s) first cross another DD target while being dragged
37542 * @param {Grid} this
37543 * @param {Roo.GridDD} dd The drag drop object
37544 * @param {String} targetId The target drag drop object
37545 * @param {event} e The raw browser event
37547 "dragenter" : true,
37550 * Fires when the dragged row(s) leave another DD target while being dragged
37551 * @param {Grid} this
37552 * @param {Roo.GridDD} dd The drag drop object
37553 * @param {String} targetId The target drag drop object
37554 * @param {event} e The raw browser event
37559 * Fires when a row is rendered, so you can change add a style to it.
37560 * @param {GridView} gridview The grid view
37561 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
37567 * Fires when the grid is rendered
37568 * @param {Grid} grid
37573 * Fires when a date is selected
37574 * @param {DatePicker} this
37575 * @param {Date} date The selected date
37579 * @event monthchange
37580 * Fires when the displayed month changes
37581 * @param {DatePicker} this
37582 * @param {Date} date The selected month
37584 'monthchange': true,
37586 * @event evententer
37587 * Fires when mouse over an event
37588 * @param {Calendar} this
37589 * @param {event} Event
37591 'evententer': true,
37593 * @event eventleave
37594 * Fires when the mouse leaves an
37595 * @param {Calendar} this
37598 'eventleave': true,
37600 * @event eventclick
37601 * Fires when the mouse click an
37602 * @param {Calendar} this
37605 'eventclick': true,
37607 * @event eventrender
37608 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37609 * @param {Calendar} this
37610 * @param {data} data to be modified
37612 'eventrender': true
37616 Roo.grid.Grid.superclass.constructor.call(this);
37617 this.on('render', function() {
37618 this.view.el.addClass('x-grid-cal');
37620 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37624 if (!Roo.grid.Calendar.style) {
37625 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37628 '.x-grid-cal .x-grid-col' : {
37629 height: 'auto !important',
37630 'vertical-align': 'top'
37632 '.x-grid-cal .fc-event-hori' : {
37643 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37645 * @cfg {Store} eventStore The store that loads events.
37650 activeDate : false,
37653 monitorWindowResize : false,
37656 resizeColumns : function() {
37657 var col = (this.view.el.getWidth() / 7) - 3;
37658 // loop through cols, and setWidth
37659 for(var i =0 ; i < 7 ; i++){
37660 this.cm.setColumnWidth(i, col);
37663 setDate :function(date) {
37665 Roo.log('setDate?');
37667 this.resizeColumns();
37668 var vd = this.activeDate;
37669 this.activeDate = date;
37670 // if(vd && this.el){
37671 // var t = date.getTime();
37672 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37673 // Roo.log('using add remove');
37675 // this.fireEvent('monthchange', this, date);
37677 // this.cells.removeClass("fc-state-highlight");
37678 // this.cells.each(function(c){
37679 // if(c.dateValue == t){
37680 // c.addClass("fc-state-highlight");
37681 // setTimeout(function(){
37682 // try{c.dom.firstChild.focus();}catch(e){}
37692 var days = date.getDaysInMonth();
37694 var firstOfMonth = date.getFirstDateOfMonth();
37695 var startingPos = firstOfMonth.getDay()-this.startDay;
37697 if(startingPos < this.startDay){
37701 var pm = date.add(Date.MONTH, -1);
37702 var prevStart = pm.getDaysInMonth()-startingPos;
37706 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37708 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37709 //this.cells.addClassOnOver('fc-state-hover');
37711 var cells = this.cells.elements;
37712 var textEls = this.textNodes;
37714 //Roo.each(cells, function(cell){
37715 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37718 days += startingPos;
37720 // convert everything to numbers so it's fast
37721 var day = 86400000;
37722 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37725 //Roo.log(prevStart);
37727 var today = new Date().clearTime().getTime();
37728 var sel = date.clearTime().getTime();
37729 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37730 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37731 var ddMatch = this.disabledDatesRE;
37732 var ddText = this.disabledDatesText;
37733 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37734 var ddaysText = this.disabledDaysText;
37735 var format = this.format;
37737 var setCellClass = function(cal, cell){
37739 //Roo.log('set Cell Class');
37741 var t = d.getTime();
37746 cell.dateValue = t;
37748 cell.className += " fc-today";
37749 cell.className += " fc-state-highlight";
37750 cell.title = cal.todayText;
37753 // disable highlight in other month..
37754 cell.className += " fc-state-highlight";
37759 //cell.className = " fc-state-disabled";
37760 cell.title = cal.minText;
37764 //cell.className = " fc-state-disabled";
37765 cell.title = cal.maxText;
37769 if(ddays.indexOf(d.getDay()) != -1){
37770 // cell.title = ddaysText;
37771 // cell.className = " fc-state-disabled";
37774 if(ddMatch && format){
37775 var fvalue = d.dateFormat(format);
37776 if(ddMatch.test(fvalue)){
37777 cell.title = ddText.replace("%0", fvalue);
37778 cell.className = " fc-state-disabled";
37782 if (!cell.initialClassName) {
37783 cell.initialClassName = cell.dom.className;
37786 cell.dom.className = cell.initialClassName + ' ' + cell.className;
37791 for(; i < startingPos; i++) {
37792 cells[i].dayName = (++prevStart);
37793 Roo.log(textEls[i]);
37794 d.setDate(d.getDate()+1);
37796 //cells[i].className = "fc-past fc-other-month";
37797 setCellClass(this, cells[i]);
37802 for(; i < days; i++){
37803 intDay = i - startingPos + 1;
37804 cells[i].dayName = (intDay);
37805 d.setDate(d.getDate()+1);
37807 cells[i].className = ''; // "x-date-active";
37808 setCellClass(this, cells[i]);
37812 for(; i < 42; i++) {
37813 //textEls[i].innerHTML = (++extraDays);
37815 d.setDate(d.getDate()+1);
37816 cells[i].dayName = (++extraDays);
37817 cells[i].className = "fc-future fc-other-month";
37818 setCellClass(this, cells[i]);
37821 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37823 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37825 // this will cause all the cells to mis
37828 for (var r = 0;r < 6;r++) {
37829 for (var c =0;c < 7;c++) {
37830 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37834 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37835 for(i=0;i<cells.length;i++) {
37837 this.cells.elements[i].dayName = cells[i].dayName ;
37838 this.cells.elements[i].className = cells[i].className;
37839 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37840 this.cells.elements[i].title = cells[i].title ;
37841 this.cells.elements[i].dateValue = cells[i].dateValue ;
37847 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37848 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37850 ////if(totalRows != 6){
37851 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37852 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37855 this.fireEvent('monthchange', this, date);
37860 * Returns the grid's SelectionModel.
37861 * @return {SelectionModel}
37863 getSelectionModel : function(){
37864 if(!this.selModel){
37865 this.selModel = new Roo.grid.CellSelectionModel();
37867 return this.selModel;
37871 this.eventStore.load()
37877 findCell : function(dt) {
37878 dt = dt.clearTime().getTime();
37880 this.cells.each(function(c){
37881 //Roo.log("check " +c.dateValue + '?=' + dt);
37882 if(c.dateValue == dt){
37892 findCells : function(rec) {
37893 var s = rec.data.start_dt.clone().clearTime().getTime();
37895 var e= rec.data.end_dt.clone().clearTime().getTime();
37898 this.cells.each(function(c){
37899 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37901 if(c.dateValue > e){
37904 if(c.dateValue < s){
37913 findBestRow: function(cells)
37917 for (var i =0 ; i < cells.length;i++) {
37918 ret = Math.max(cells[i].rows || 0,ret);
37925 addItem : function(rec)
37927 // look for vertical location slot in
37928 var cells = this.findCells(rec);
37930 rec.row = this.findBestRow(cells);
37932 // work out the location.
37936 for(var i =0; i < cells.length; i++) {
37944 if (crow.start.getY() == cells[i].getY()) {
37946 crow.end = cells[i];
37962 for (var i = 0; i < cells.length;i++) {
37963 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37970 clearEvents: function() {
37972 if (!this.eventStore.getCount()) {
37975 // reset number of rows in cells.
37976 Roo.each(this.cells.elements, function(c){
37980 this.eventStore.each(function(e) {
37981 this.clearEvent(e);
37986 clearEvent : function(ev)
37989 Roo.each(ev.els, function(el) {
37990 el.un('mouseenter' ,this.onEventEnter, this);
37991 el.un('mouseleave' ,this.onEventLeave, this);
37999 renderEvent : function(ev,ctr) {
38001 ctr = this.view.el.select('.fc-event-container',true).first();
38005 this.clearEvent(ev);
38011 var cells = ev.cells;
38012 var rows = ev.rows;
38013 this.fireEvent('eventrender', this, ev);
38015 for(var i =0; i < rows.length; i++) {
38019 cls += ' fc-event-start';
38021 if ((i+1) == rows.length) {
38022 cls += ' fc-event-end';
38025 //Roo.log(ev.data);
38026 // how many rows should it span..
38027 var cg = this.eventTmpl.append(ctr,Roo.apply({
38030 }, ev.data) , true);
38033 cg.on('mouseenter' ,this.onEventEnter, this, ev);
38034 cg.on('mouseleave' ,this.onEventLeave, this, ev);
38035 cg.on('click', this.onEventClick, this, ev);
38039 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38040 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38043 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
38044 cg.setWidth(ebox.right - sbox.x -2);
38048 renderEvents: function()
38050 // first make sure there is enough space..
38052 if (!this.eventTmpl) {
38053 this.eventTmpl = new Roo.Template(
38054 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
38055 '<div class="fc-event-inner">' +
38056 '<span class="fc-event-time">{time}</span>' +
38057 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38059 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
38067 this.cells.each(function(c) {
38068 //Roo.log(c.select('.fc-day-content div',true).first());
38069 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38072 var ctr = this.view.el.select('.fc-event-container',true).first();
38075 this.eventStore.each(function(ev){
38077 this.renderEvent(ev);
38081 this.view.layout();
38085 onEventEnter: function (e, el,event,d) {
38086 this.fireEvent('evententer', this, el, event);
38089 onEventLeave: function (e, el,event,d) {
38090 this.fireEvent('eventleave', this, el, event);
38093 onEventClick: function (e, el,event,d) {
38094 this.fireEvent('eventclick', this, el, event);
38097 onMonthChange: function () {
38101 onLoad: function () {
38103 //Roo.log('calendar onload');
38105 if(this.eventStore.getCount() > 0){
38109 this.eventStore.each(function(d){
38114 if (typeof(add.end_dt) == 'undefined') {
38115 Roo.log("Missing End time in calendar data: ");
38119 if (typeof(add.start_dt) == 'undefined') {
38120 Roo.log("Missing Start time in calendar data: ");
38124 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38125 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38126 add.id = add.id || d.id;
38127 add.title = add.title || '??';
38135 this.renderEvents();
38145 render : function ()
38149 if (!this.view.el.hasClass('course-timesheet')) {
38150 this.view.el.addClass('course-timesheet');
38152 if (this.tsStyle) {
38157 Roo.log(_this.grid.view.el.getWidth());
38160 this.tsStyle = Roo.util.CSS.createStyleSheet({
38161 '.course-timesheet .x-grid-row' : {
38164 '.x-grid-row td' : {
38165 'vertical-align' : 0
38167 '.course-edit-link' : {
38169 'text-overflow' : 'ellipsis',
38170 'overflow' : 'hidden',
38171 'white-space' : 'nowrap',
38172 'cursor' : 'pointer'
38177 '.de-act-sup-link' : {
38178 'color' : 'purple',
38179 'text-decoration' : 'line-through'
38183 'text-decoration' : 'line-through'
38185 '.course-timesheet .course-highlight' : {
38186 'border-top-style': 'dashed !important',
38187 'border-bottom-bottom': 'dashed !important'
38189 '.course-timesheet .course-item' : {
38190 'font-family' : 'tahoma, arial, helvetica',
38191 'font-size' : '11px',
38192 'overflow' : 'hidden',
38193 'padding-left' : '10px',
38194 'padding-right' : '10px',
38195 'padding-top' : '10px'
38203 monitorWindowResize : false,
38204 cellrenderer : function(v,x,r)
38209 xtype: 'CellSelectionModel',
38216 beforeload : function (_self, options)
38218 options.params = options.params || {};
38219 options.params._month = _this.monthField.getValue();
38220 options.params.limit = 9999;
38221 options.params['sort'] = 'when_dt';
38222 options.params['dir'] = 'ASC';
38223 this.proxy.loadResponse = this.loadResponse;
38225 //this.addColumns();
38227 load : function (_self, records, options)
38229 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38230 // if you click on the translation.. you can edit it...
38231 var el = Roo.get(this);
38232 var id = el.dom.getAttribute('data-id');
38233 var d = el.dom.getAttribute('data-date');
38234 var t = el.dom.getAttribute('data-time');
38235 //var id = this.child('span').dom.textContent;
38238 Pman.Dialog.CourseCalendar.show({
38242 productitem_active : id ? 1 : 0
38244 _this.grid.ds.load({});
38249 _this.panel.fireEvent('resize', [ '', '' ]);
38252 loadResponse : function(o, success, response){
38253 // this is overridden on before load..
38255 Roo.log("our code?");
38256 //Roo.log(success);
38257 //Roo.log(response)
38258 delete this.activeRequest;
38260 this.fireEvent("loadexception", this, o, response);
38261 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38266 result = o.reader.read(response);
38268 Roo.log("load exception?");
38269 this.fireEvent("loadexception", this, o, response, e);
38270 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38273 Roo.log("ready...");
38274 // loop through result.records;
38275 // and set this.tdate[date] = [] << array of records..
38277 Roo.each(result.records, function(r){
38279 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38280 _this.tdata[r.data.when_dt.format('j')] = [];
38282 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38285 //Roo.log(_this.tdata);
38287 result.records = [];
38288 result.totalRecords = 6;
38290 // let's generate some duumy records for the rows.
38291 //var st = _this.dateField.getValue();
38293 // work out monday..
38294 //st = st.add(Date.DAY, -1 * st.format('w'));
38296 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38298 var firstOfMonth = date.getFirstDayOfMonth();
38299 var days = date.getDaysInMonth();
38301 var firstAdded = false;
38302 for (var i = 0; i < result.totalRecords ; i++) {
38303 //var d= st.add(Date.DAY, i);
38306 for(var w = 0 ; w < 7 ; w++){
38307 if(!firstAdded && firstOfMonth != w){
38314 var dd = (d > 0 && d < 10) ? "0"+d : d;
38315 row['weekday'+w] = String.format(
38316 '<span style="font-size: 16px;"><b>{0}</b></span>'+
38317 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38319 date.format('Y-m-')+dd
38322 if(typeof(_this.tdata[d]) != 'undefined'){
38323 Roo.each(_this.tdata[d], function(r){
38327 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38328 if(r.parent_id*1>0){
38329 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38332 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38333 deactive = 'de-act-link';
38336 row['weekday'+w] += String.format(
38337 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38339 r.product_id_name, //1
38340 r.when_dt.format('h:ia'), //2
38350 // only do this if something added..
38352 result.records.push(_this.grid.dataSource.reader.newRow(row));
38356 // push it twice. (second one with an hour..
38360 this.fireEvent("load", this, o, o.request.arg);
38361 o.request.callback.call(o.request.scope, result, o.request.arg, true);
38363 sortInfo : {field: 'when_dt', direction : 'ASC' },
38365 xtype: 'HttpProxy',
38368 url : baseURL + '/Roo/Shop_course.php'
38371 xtype: 'JsonReader',
38388 'name': 'parent_id',
38392 'name': 'product_id',
38396 'name': 'productitem_id',
38414 click : function (_self, e)
38416 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38417 sd.setMonth(sd.getMonth()-1);
38418 _this.monthField.setValue(sd.format('Y-m-d'));
38419 _this.grid.ds.load({});
38425 xtype: 'Separator',
38429 xtype: 'MonthField',
38432 render : function (_self)
38434 _this.monthField = _self;
38435 // _this.monthField.set today
38437 select : function (combo, date)
38439 _this.grid.ds.load({});
38442 value : (function() { return new Date(); })()
38445 xtype: 'Separator',
38451 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38461 click : function (_self, e)
38463 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38464 sd.setMonth(sd.getMonth()+1);
38465 _this.monthField.setValue(sd.format('Y-m-d'));
38466 _this.grid.ds.load({});
38479 * Ext JS Library 1.1.1
38480 * Copyright(c) 2006-2007, Ext JS, LLC.
38482 * Originally Released Under LGPL - original licence link has changed is not relivant.
38485 * <script type="text/javascript">
38489 * @class Roo.LoadMask
38490 * A simple utility class for generically masking elements while loading data. If the element being masked has
38491 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38492 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
38493 * element's UpdateManager load indicator and will be destroyed after the initial load.
38495 * Create a new LoadMask
38496 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38497 * @param {Object} config The config object
38499 Roo.LoadMask = function(el, config){
38500 this.el = Roo.get(el);
38501 Roo.apply(this, config);
38503 this.store.on('beforeload', this.onBeforeLoad, this);
38504 this.store.on('load', this.onLoad, this);
38505 this.store.on('loadexception', this.onLoadException, this);
38506 this.removeMask = false;
38508 var um = this.el.getUpdateManager();
38509 um.showLoadIndicator = false; // disable the default indicator
38510 um.on('beforeupdate', this.onBeforeLoad, this);
38511 um.on('update', this.onLoad, this);
38512 um.on('failure', this.onLoad, this);
38513 this.removeMask = true;
38517 Roo.LoadMask.prototype = {
38519 * @cfg {Boolean} removeMask
38520 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38521 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
38523 removeMask : false,
38525 * @cfg {String} msg
38526 * The text to display in a centered loading message box (defaults to 'Loading...')
38528 msg : 'Loading...',
38530 * @cfg {String} msgCls
38531 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38533 msgCls : 'x-mask-loading',
38536 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38542 * Disables the mask to prevent it from being displayed
38544 disable : function(){
38545 this.disabled = true;
38549 * Enables the mask so that it can be displayed
38551 enable : function(){
38552 this.disabled = false;
38555 onLoadException : function()
38557 Roo.log(arguments);
38559 if (typeof(arguments[3]) != 'undefined') {
38560 Roo.MessageBox.alert("Error loading",arguments[3]);
38564 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38565 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38572 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38575 onLoad : function()
38577 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38581 onBeforeLoad : function(){
38582 if(!this.disabled){
38583 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38588 destroy : function(){
38590 this.store.un('beforeload', this.onBeforeLoad, this);
38591 this.store.un('load', this.onLoad, this);
38592 this.store.un('loadexception', this.onLoadException, this);
38594 var um = this.el.getUpdateManager();
38595 um.un('beforeupdate', this.onBeforeLoad, this);
38596 um.un('update', this.onLoad, this);
38597 um.un('failure', this.onLoad, this);
38602 * Ext JS Library 1.1.1
38603 * Copyright(c) 2006-2007, Ext JS, LLC.
38605 * Originally Released Under LGPL - original licence link has changed is not relivant.
38608 * <script type="text/javascript">
38613 * @class Roo.XTemplate
38614 * @extends Roo.Template
38615 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38617 var t = new Roo.XTemplate(
38618 '<select name="{name}">',
38619 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
38623 // then append, applying the master template values
38626 * Supported features:
38631 {a_variable} - output encoded.
38632 {a_variable.format:("Y-m-d")} - call a method on the variable
38633 {a_variable:raw} - unencoded output
38634 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38635 {a_variable:this.method_on_template(...)} - call a method on the template object.
38640 <tpl for="a_variable or condition.."></tpl>
38641 <tpl if="a_variable or condition"></tpl>
38642 <tpl exec="some javascript"></tpl>
38643 <tpl name="named_template"></tpl> (experimental)
38645 <tpl for="."></tpl> - just iterate the property..
38646 <tpl for=".."></tpl> - iterates with the parent (probably the template)
38650 Roo.XTemplate = function()
38652 Roo.XTemplate.superclass.constructor.apply(this, arguments);
38659 Roo.extend(Roo.XTemplate, Roo.Template, {
38662 * The various sub templates
38667 * basic tag replacing syntax
38670 * // you can fake an object call by doing this
38674 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38677 * compile the template
38679 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38682 compile: function()
38686 s = ['<tpl>', s, '</tpl>'].join('');
38688 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38689 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38690 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
38691 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38692 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
38697 while(true == !!(m = s.match(re))){
38698 var forMatch = m[0].match(nameRe),
38699 ifMatch = m[0].match(ifRe),
38700 execMatch = m[0].match(execRe),
38701 namedMatch = m[0].match(namedRe),
38706 name = forMatch && forMatch[1] ? forMatch[1] : '';
38709 // if - puts fn into test..
38710 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38712 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38717 // exec - calls a function... returns empty if true is returned.
38718 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38720 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38728 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38729 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38730 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38733 var uid = namedMatch ? namedMatch[1] : id;
38737 id: namedMatch ? namedMatch[1] : id,
38744 s = s.replace(m[0], '');
38746 s = s.replace(m[0], '{xtpl'+ id + '}');
38751 for(var i = tpls.length-1; i >= 0; --i){
38752 this.compileTpl(tpls[i]);
38753 this.tpls[tpls[i].id] = tpls[i];
38755 this.master = tpls[tpls.length-1];
38759 * same as applyTemplate, except it's done to one of the subTemplates
38760 * when using named templates, you can do:
38762 * var str = pl.applySubTemplate('your-name', values);
38765 * @param {Number} id of the template
38766 * @param {Object} values to apply to template
38767 * @param {Object} parent (normaly the instance of this object)
38769 applySubTemplate : function(id, values, parent)
38773 var t = this.tpls[id];
38777 if(t.test && !t.test.call(this, values, parent)){
38781 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38782 Roo.log(e.toString());
38788 if(t.exec && t.exec.call(this, values, parent)){
38792 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38793 Roo.log(e.toString());
38798 var vs = t.target ? t.target.call(this, values, parent) : values;
38799 parent = t.target ? values : parent;
38800 if(t.target && vs instanceof Array){
38802 for(var i = 0, len = vs.length; i < len; i++){
38803 buf[buf.length] = t.compiled.call(this, vs[i], parent);
38805 return buf.join('');
38807 return t.compiled.call(this, vs, parent);
38809 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38810 Roo.log(e.toString());
38811 Roo.log(t.compiled);
38816 compileTpl : function(tpl)
38818 var fm = Roo.util.Format;
38819 var useF = this.disableFormats !== true;
38820 var sep = Roo.isGecko ? "+" : ",";
38821 var undef = function(str) {
38822 Roo.log("Property not found :" + str);
38826 var fn = function(m, name, format, args)
38828 //Roo.log(arguments);
38829 args = args ? args.replace(/\\'/g,"'") : args;
38830 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38831 if (typeof(format) == 'undefined') {
38832 format= 'htmlEncode';
38834 if (format == 'raw' ) {
38838 if(name.substr(0, 4) == 'xtpl'){
38839 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38842 // build an array of options to determine if value is undefined..
38844 // basically get 'xxxx.yyyy' then do
38845 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38846 // (function () { Roo.log("Property not found"); return ''; })() :
38851 Roo.each(name.split('.'), function(st) {
38852 lookfor += (lookfor.length ? '.': '') + st;
38853 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
38856 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38859 if(format && useF){
38861 args = args ? ',' + args : "";
38863 if(format.substr(0, 5) != "this."){
38864 format = "fm." + format + '(';
38866 format = 'this.call("'+ format.substr(5) + '", ';
38870 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
38874 // called with xxyx.yuu:(test,test)
38876 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
38878 // raw.. - :raw modifier..
38879 return "'"+ sep + udef_st + name + ")"+sep+"'";
38883 // branched to use + in gecko and [].join() in others
38885 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
38886 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38889 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
38890 body.push(tpl.body.replace(/(\r\n|\n)/g,
38891 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38892 body.push("'].join('');};};");
38893 body = body.join('');
38896 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38898 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
38904 applyTemplate : function(values){
38905 return this.master.compiled.call(this, values, {});
38906 //var s = this.subs;
38909 apply : function(){
38910 return this.applyTemplate.apply(this, arguments);
38915 Roo.XTemplate.from = function(el){
38916 el = Roo.getDom(el);
38917 return new Roo.XTemplate(el.value || el.innerHTML);