4 * Copyright(c) 2006-2007, Ext JS, LLC.
6 * Originally Released Under LGPL - original licence link has changed is not relivant.
9 * <script type="text/javascript">
14 * @class Roo.data.SortTypes
16 * Defines the default sorting (casting?) comparison functions used when sorting data.
18 Roo.data.SortTypes = {
20 * Default sort that does nothing
21 * @param {Mixed} s The value being converted
22 * @return {Mixed} The comparison value
29 * The regular expression used to strip tags
33 stripTagsRE : /<\/?[^>]+>/gi,
36 * Strips all HTML tags to sort on text only
37 * @param {Mixed} s The value being converted
38 * @return {String} The comparison value
41 return String(s).replace(this.stripTagsRE, "");
45 * Strips all HTML tags to sort on text only - Case insensitive
46 * @param {Mixed} s The value being converted
47 * @return {String} The comparison value
49 asUCText : function(s){
50 return String(s).toUpperCase().replace(this.stripTagsRE, "");
54 * Case insensitive string
55 * @param {Mixed} s The value being converted
56 * @return {String} The comparison value
58 asUCString : function(s) {
59 return String(s).toUpperCase();
64 * @param {Mixed} s The value being converted
65 * @return {Number} The comparison value
67 asDate : function(s) {
71 if(s instanceof Date){
74 return Date.parse(String(s));
79 * @param {Mixed} s The value being converted
80 * @return {Float} The comparison value
82 asFloat : function(s) {
83 var val = parseFloat(String(s).replace(/,/g, ""));
92 * @param {Mixed} s The value being converted
93 * @return {Number} The comparison value
96 var val = parseInt(String(s).replace(/,/g, ""));
104 * Ext JS Library 1.1.1
105 * Copyright(c) 2006-2007, Ext JS, LLC.
107 * Originally Released Under LGPL - original licence link has changed is not relivant.
110 * <script type="text/javascript">
114 * @class Roo.data.Record
115 * Instances of this class encapsulate both record <em>definition</em> information, and record
116 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117 * to access Records cached in an {@link Roo.data.Store} object.<br>
119 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
123 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
125 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126 * {@link #create}. The parameters are the same.
127 * @param {Array} data An associative Array of data values keyed by the field name.
128 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130 * not specified an integer id is generated.
132 Roo.data.Record = function(data, id){
133 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
138 * Generate a constructor for a specific record layout.
139 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141 * Each field definition object may contain the following properties: <ul>
142 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146 * is being used, then this is a string containing the javascript expression to reference the data relative to
147 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148 * to the data item relative to the record element. If the mapping expression is the same as the field name,
149 * this may be omitted.</p></li>
150 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151 * <ul><li>auto (Default, implies no conversion)</li>
156 * <li>date</li></ul></p></li>
157 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160 * by the Reader into an object that will be stored in the Record. It is passed the
161 * following parameters:<ul>
162 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
164 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
166 * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168 {name: 'title', mapping: 'topic_title'},
169 {name: 'author', mapping: 'username'},
170 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171 {name: 'lastPost', mapping: 'post_time', type: 'date'},
172 {name: 'lastPoster', mapping: 'user2'},
173 {name: 'excerpt', mapping: 'post_text'}
176 var myNewRecord = new TopicRecord({
177 title: 'Do my job please',
180 lastPost: new Date(),
181 lastPoster: 'Animal',
182 excerpt: 'No way dude!'
184 myStore.add(myNewRecord);
189 Roo.data.Record.create = function(o){
191 f.superclass.constructor.apply(this, arguments);
193 Roo.extend(f, Roo.data.Record);
195 p.fields = new Roo.util.MixedCollection(false, function(field){
198 for(var i = 0, len = o.length; i < len; i++){
199 p.fields.add(new Roo.data.Field(o[i]));
201 f.getField = function(name){
202 return p.fields.get(name);
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
212 Roo.data.Record.prototype = {
214 * Readonly flag - true if this record has been modified.
223 join : function(store){
228 * Set the named field to the specified value.
229 * @param {String} name The name of the field to set.
230 * @param {Object} value The value to set the field to.
232 set : function(name, value){
233 if(this.data[name] == value){
240 if(typeof this.modified[name] == 'undefined'){
241 this.modified[name] = this.data[name];
243 this.data[name] = value;
244 if(!this.editing && this.store){
245 this.store.afterEdit(this);
250 * Get the value of the named field.
251 * @param {String} name The name of the field to get the value of.
252 * @return {Object} The value of the field.
254 get : function(name){
255 return this.data[name];
259 beginEdit : function(){
265 cancelEdit : function(){
266 this.editing = false;
267 delete this.modified;
271 endEdit : function(){
272 this.editing = false;
273 if(this.dirty && this.store){
274 this.store.afterEdit(this);
279 * Usually called by the {@link Roo.data.Store} which owns the Record.
280 * Rejects all changes made to the Record since either creation, or the last commit operation.
281 * Modified fields are reverted to their original values.
283 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284 * of reject operations.
287 var m = this.modified;
289 if(typeof m[n] != "function"){
294 delete this.modified;
295 this.editing = false;
297 this.store.afterReject(this);
302 * Usually called by the {@link Roo.data.Store} which owns the Record.
303 * Commits all changes made to the Record since either creation, or the last commit operation.
305 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306 * of commit operations.
310 delete this.modified;
311 this.editing = false;
313 this.store.afterCommit(this);
318 hasError : function(){
319 return this.error != null;
323 clearError : function(){
328 * Creates a copy of this record.
329 * @param {String} id (optional) A new record id if you don't want to use this record's id
332 copy : function(newId) {
333 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
337 * Ext JS Library 1.1.1
338 * Copyright(c) 2006-2007, Ext JS, LLC.
340 * Originally Released Under LGPL - original licence link has changed is not relivant.
343 * <script type="text/javascript">
349 * @class Roo.data.Store
350 * @extends Roo.util.Observable
351 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
354 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355 * has no knowledge of the format of the data returned by the Proxy.<br>
357 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358 * instances from the data object. These records are cached and made available through accessor functions.
360 * Creates a new Store.
361 * @param {Object} config A config object containing the objects needed for the Store to access data,
362 * and read the data into Records.
364 Roo.data.Store = function(config){
365 this.data = new Roo.util.MixedCollection(false);
366 this.data.getKey = function(o){
369 this.baseParams = {};
376 "multisort" : "_multisort"
379 if(config && config.data){
380 this.inlineData = config.data;
384 Roo.apply(this, config);
386 if(this.reader){ // reader passed
387 this.reader = Roo.factory(this.reader, Roo.data);
388 this.reader.xmodule = this.xmodule || false;
389 if(!this.recordType){
390 this.recordType = this.reader.recordType;
392 if(this.reader.onMetaChange){
393 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
398 this.fields = this.recordType.prototype.fields;
405 * Fires when the data cache has changed, and a widget which is using this Store
406 * as a Record cache should refresh its view.
407 * @param {Store} this
412 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413 * @param {Store} this
414 * @param {Object} meta The JSON metadata
419 * Fires when Records have been added to the Store
420 * @param {Store} this
421 * @param {Roo.data.Record[]} records The array of Records added
422 * @param {Number} index The index at which the record(s) were added
427 * Fires when a Record has been removed from the Store
428 * @param {Store} this
429 * @param {Roo.data.Record} record The Record that was removed
430 * @param {Number} index The index at which the record was removed
435 * Fires when a Record has been updated
436 * @param {Store} this
437 * @param {Roo.data.Record} record The Record that was updated
438 * @param {String} operation The update operation being performed. Value may be one of:
441 Roo.data.Record.REJECT
442 Roo.data.Record.COMMIT
448 * Fires when the data cache has been cleared.
449 * @param {Store} this
454 * Fires before a request is made for a new data object. If the beforeload handler returns false
455 * the load action will be canceled.
456 * @param {Store} this
457 * @param {Object} options The loading options that were specified (see {@link #load} for details)
461 * @event beforeloadadd
462 * Fires after a new set of Records has been loaded.
463 * @param {Store} this
464 * @param {Roo.data.Record[]} records The Records that were loaded
465 * @param {Object} options The loading options that were specified (see {@link #load} for details)
467 beforeloadadd : true,
470 * Fires after a new set of Records has been loaded, before they are added to the store.
471 * @param {Store} this
472 * @param {Roo.data.Record[]} records The Records that were loaded
473 * @param {Object} options The loading options that were specified (see {@link #load} for details)
474 * @params {Object} return from reader
478 * @event loadexception
479 * Fires if an exception occurs in the Proxy during loading.
480 * Called with the signature of the Proxy's "loadexception" event.
481 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
484 * @param {Object} return from JsonData.reader() - success, totalRecords, records
485 * @param {Object} load options
486 * @param {Object} jsonData from your request (normally this contains the Exception)
492 this.proxy = Roo.factory(this.proxy, Roo.data);
493 this.proxy.xmodule = this.xmodule || false;
494 this.relayEvents(this.proxy, ["loadexception"]);
496 this.sortToggle = {};
497 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
499 Roo.data.Store.superclass.constructor.call(this);
502 this.loadData(this.inlineData);
503 delete this.inlineData;
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
509 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
510 * without a remote query - used by combo/forms at present.
514 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
517 * @cfg {Array} data Inline data to be loaded when the store is initialized.
520 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
521 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
524 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525 * on any HTTP request
528 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
531 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
535 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
541 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542 * loaded or when a record is removed. (defaults to false).
544 pruneModifiedRecords : false,
550 * Add Records to the Store and fires the add event.
551 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
553 add : function(records){
554 records = [].concat(records);
555 for(var i = 0, len = records.length; i < len; i++){
556 records[i].join(this);
558 var index = this.data.length;
559 this.data.addAll(records);
560 this.fireEvent("add", this, records, index);
564 * Remove a Record from the Store and fires the remove event.
565 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
567 remove : function(record){
568 var index = this.data.indexOf(record);
569 this.data.removeAt(index);
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){
715 if(!o || success === false){
716 if(success !== false){
717 this.fireEvent("load", this, [], options, o);
719 if(options.callback){
720 options.callback.call(options.scope || this, [], options, false);
724 // if data returned failure - throw an exception.
725 if (o.success === false) {
726 // show a message if no listener is registered.
727 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
730 // loadmask wil be hooked into this..
731 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
734 var r = o.records, t = o.totalRecords || r.length;
736 this.fireEvent("beforeloadadd", this, r, options, o);
738 if(!options || options.add !== true){
739 if(this.pruneModifiedRecords){
742 for(var i = 0, len = r.length; i < len; i++){
746 this.data = this.snapshot;
747 delete this.snapshot;
751 this.totalLength = t;
753 this.fireEvent("datachanged", this);
755 this.totalLength = Math.max(t, this.data.length+r.length);
759 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
761 var e = new Roo.data.Record({});
763 e.set(this.parent.displayField, this.parent.emptyTitle);
764 e.set(this.parent.valueField, '');
769 this.fireEvent("load", this, r, options, o);
770 if(options.callback){
771 options.callback.call(options.scope || this, r, options, true);
777 * Loads data from a passed data block. A Reader which understands the format of the data
778 * must have been configured in the constructor.
779 * @param {Object} data The data block from which to read the Records. The format of the data expected
780 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
783 loadData : function(o, append){
784 var r = this.reader.readRecords(o);
785 this.loadRecords(r, {add: append}, true);
789 * Gets the number of cached records.
791 * <em>If using paging, this may not be the total size of the dataset. If the data object
792 * used by the Reader contains the dataset size, then the getTotalCount() function returns
793 * the data set size</em>
795 getCount : function(){
796 return this.data.length || 0;
800 * Gets the total number of records in the dataset as returned by the server.
802 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
803 * the dataset size</em>
805 getTotalCount : function(){
806 return this.totalLength || 0;
810 * Returns the sort state of the Store as an object with two properties:
812 field {String} The name of the field by which the Records are sorted
813 direction {String} The sort order, "ASC" or "DESC"
816 getSortState : function(){
817 return this.sortInfo;
821 applySort : function(){
822 if(this.sortInfo && !this.remoteSort){
823 var s = this.sortInfo, f = s.field;
824 var st = this.fields.get(f).sortType;
825 var fn = function(r1, r2){
826 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
827 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
829 this.data.sort(s.direction, fn);
830 if(this.snapshot && this.snapshot != this.data){
831 this.snapshot.sort(s.direction, fn);
837 * Sets the default sort column and order to be used by the next load operation.
838 * @param {String} fieldName The name of the field to sort by.
839 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
841 setDefaultSort : function(field, dir){
842 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
847 * If remote sorting is used, the sort is performed on the server, and the cache is
848 * reloaded. If local sorting is used, the cache is sorted internally.
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 sort : function(fieldName, dir){
853 var f = this.fields.get(fieldName);
855 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
857 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
858 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
863 this.sortToggle[f.name] = dir;
864 this.sortInfo = {field: f.name, direction: dir};
865 if(!this.remoteSort){
867 this.fireEvent("datachanged", this);
869 this.load(this.lastOptions);
874 * Calls the specified function for each of the Records in the cache.
875 * @param {Function} fn The function to call. The Record is passed as the first parameter.
876 * Returning <em>false</em> aborts and exits the iteration.
877 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
879 each : function(fn, scope){
880 this.data.each(fn, scope);
884 * Gets all records modified since the last commit. Modified records are persisted across load operations
885 * (e.g., during paging).
886 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
888 getModifiedRecords : function(){
889 return this.modified;
893 createFilterFn : function(property, value, anyMatch){
894 if(!value.exec){ // not a regex
895 value = String(value);
896 if(value.length == 0){
899 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
902 return value.test(r.data[property]);
907 * Sums the value of <i>property</i> for each record between start and end and returns the result.
908 * @param {String} property A field on your records
909 * @param {Number} start The record index to start at (defaults to 0)
910 * @param {Number} end The last record index to include (defaults to length - 1)
911 * @return {Number} The sum
913 sum : function(property, start, end){
914 var rs = this.data.items, v = 0;
916 end = (end || end === 0) ? end : rs.length-1;
918 for(var i = start; i <= end; i++){
919 v += (rs[i].data[property] || 0);
925 * Filter the records by a specified property.
926 * @param {String} field A field on your records
927 * @param {String/RegExp} value Either a string that the field
928 * should start with or a RegExp to test against the field
929 * @param {Boolean} anyMatch True to match any part not just the beginning
931 filter : function(property, value, anyMatch){
932 var fn = this.createFilterFn(property, value, anyMatch);
933 return fn ? this.filterBy(fn) : this.clearFilter();
937 * Filter by a function. The specified function will be called with each
938 * record in this data source. If the function returns true the record is included,
939 * otherwise it is filtered.
940 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
941 * @param {Object} scope (optional) The scope of the function (defaults to this)
943 filterBy : function(fn, scope){
944 this.snapshot = this.snapshot || this.data;
945 this.data = this.queryBy(fn, scope||this);
946 this.fireEvent("datachanged", this);
950 * Query the records by a specified property.
951 * @param {String} field A field on your records
952 * @param {String/RegExp} value Either a string that the field
953 * should start with or a RegExp to test against the field
954 * @param {Boolean} anyMatch True to match any part not just the beginning
955 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
957 query : function(property, value, anyMatch){
958 var fn = this.createFilterFn(property, value, anyMatch);
959 return fn ? this.queryBy(fn) : this.data.clone();
963 * Query by a function. The specified function will be called with each
964 * record in this data source. If the function returns true the record is included
966 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
967 * @param {Object} scope (optional) The scope of the function (defaults to this)
968 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
970 queryBy : function(fn, scope){
971 var data = this.snapshot || this.data;
972 return data.filterBy(fn, scope||this);
976 * Collects unique values for a particular dataIndex from this store.
977 * @param {String} dataIndex The property to collect
978 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
979 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
980 * @return {Array} An array of the unique values
982 collect : function(dataIndex, allowNull, bypassFilter){
983 var d = (bypassFilter === true && this.snapshot) ?
984 this.snapshot.items : this.data.items;
985 var v, sv, r = [], l = {};
986 for(var i = 0, len = d.length; i < len; i++){
987 v = d[i].data[dataIndex];
989 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
998 * Revert to a view of the Record cache with no filtering applied.
999 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1001 clearFilter : function(suppressEvent){
1002 if(this.snapshot && this.snapshot != this.data){
1003 this.data = this.snapshot;
1004 delete this.snapshot;
1005 if(suppressEvent !== true){
1006 this.fireEvent("datachanged", this);
1012 afterEdit : function(record){
1013 if(this.modified.indexOf(record) == -1){
1014 this.modified.push(record);
1016 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1020 afterReject : function(record){
1021 this.modified.remove(record);
1022 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1026 afterCommit : function(record){
1027 this.modified.remove(record);
1028 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1032 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1033 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1035 commitChanges : function(){
1036 var m = this.modified.slice(0);
1038 for(var i = 0, len = m.length; i < len; i++){
1044 * Cancel outstanding changes on all changed records.
1046 rejectChanges : function(){
1047 var m = this.modified.slice(0);
1049 for(var i = 0, len = m.length; i < len; i++){
1054 onMetaChange : function(meta, rtype, o){
1055 this.recordType = rtype;
1056 this.fields = rtype.prototype.fields;
1057 delete this.snapshot;
1058 this.sortInfo = meta.sortInfo || this.sortInfo;
1060 this.fireEvent('metachange', this, this.reader.meta);
1063 moveIndex : function(data, type)
1065 var index = this.indexOf(data);
1067 var newIndex = index + type;
1071 this.insert(newIndex, data);
1076 * Ext JS Library 1.1.1
1077 * Copyright(c) 2006-2007, Ext JS, LLC.
1079 * Originally Released Under LGPL - original licence link has changed is not relivant.
1082 * <script type="text/javascript">
1086 * @class Roo.data.SimpleStore
1087 * @extends Roo.data.Store
1088 * Small helper class to make creating Stores from Array data easier.
1089 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1090 * @cfg {Array} fields An array of field definition objects, or field name strings.
1091 * @cfg {Array} data The multi-dimensional array of data
1093 * @param {Object} config
1095 Roo.data.SimpleStore = function(config){
1096 Roo.data.SimpleStore.superclass.constructor.call(this, {
1098 reader: new Roo.data.ArrayReader({
1101 Roo.data.Record.create(config.fields)
1103 proxy : new Roo.data.MemoryProxy(config.data)
1107 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1109 * Ext JS Library 1.1.1
1110 * Copyright(c) 2006-2007, Ext JS, LLC.
1112 * Originally Released Under LGPL - original licence link has changed is not relivant.
1115 * <script type="text/javascript">
1120 * @extends Roo.data.Store
1121 * @class Roo.data.JsonStore
1122 * Small helper class to make creating Stores for JSON data easier. <br/>
1124 var store = new Roo.data.JsonStore({
1125 url: 'get-images.php',
1127 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1130 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1131 * JsonReader and HttpProxy (unless inline data is provided).</b>
1132 * @cfg {Array} fields An array of field definition objects, or field name strings.
1134 * @param {Object} config
1136 Roo.data.JsonStore = function(c){
1137 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1138 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1139 reader: new Roo.data.JsonReader(c, c.fields)
1142 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1144 * Ext JS Library 1.1.1
1145 * Copyright(c) 2006-2007, Ext JS, LLC.
1147 * Originally Released Under LGPL - original licence link has changed is not relivant.
1150 * <script type="text/javascript">
1154 Roo.data.Field = function(config){
1155 if(typeof config == "string"){
1156 config = {name: config};
1158 Roo.apply(this, config);
1164 var st = Roo.data.SortTypes;
1165 // named sortTypes are supported, here we look them up
1166 if(typeof this.sortType == "string"){
1167 this.sortType = st[this.sortType];
1170 // set default sortType for strings and dates
1174 this.sortType = st.asUCString;
1177 this.sortType = st.asDate;
1180 this.sortType = st.none;
1185 var stripRe = /[\$,%]/g;
1187 // prebuilt conversion function for this field, instead of
1188 // switching every time we're reading a value
1190 var cv, dateFormat = this.dateFormat;
1195 cv = function(v){ return v; };
1198 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1202 return v !== undefined && v !== null && v !== '' ?
1203 parseInt(String(v).replace(stripRe, ""), 10) : '';
1208 return v !== undefined && v !== null && v !== '' ?
1209 parseFloat(String(v).replace(stripRe, ""), 10) : '';
1214 cv = function(v){ return v === true || v === "true" || v == 1; };
1221 if(v instanceof Date){
1225 if(dateFormat == "timestamp"){
1226 return new Date(v*1000);
1228 return Date.parseDate(v, dateFormat);
1230 var parsed = Date.parse(v);
1231 return parsed ? new Date(parsed) : null;
1240 Roo.data.Field.prototype = {
1248 * Ext JS Library 1.1.1
1249 * Copyright(c) 2006-2007, Ext JS, LLC.
1251 * Originally Released Under LGPL - original licence link has changed is not relivant.
1254 * <script type="text/javascript">
1257 // Base class for reading structured data from a data source. This class is intended to be
1258 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1261 * @class Roo.data.DataReader
1262 * Base class for reading structured data from a data source. This class is intended to be
1263 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1266 Roo.data.DataReader = function(meta, recordType){
1270 this.recordType = recordType instanceof Array ?
1271 Roo.data.Record.create(recordType) : recordType;
1274 Roo.data.DataReader.prototype = {
1276 * Create an empty record
1277 * @param {Object} data (optional) - overlay some values
1278 * @return {Roo.data.Record} record created.
1280 newRow : function(d) {
1282 this.recordType.prototype.fields.each(function(c) {
1284 case 'int' : da[c.name] = 0; break;
1285 case 'date' : da[c.name] = new Date(); break;
1286 case 'float' : da[c.name] = 0.0; break;
1287 case 'boolean' : da[c.name] = false; break;
1288 default : da[c.name] = ""; break;
1292 return new this.recordType(Roo.apply(da, d));
1297 * Ext JS Library 1.1.1
1298 * Copyright(c) 2006-2007, Ext JS, LLC.
1300 * Originally Released Under LGPL - original licence link has changed is not relivant.
1303 * <script type="text/javascript">
1307 * @class Roo.data.DataProxy
1308 * @extends Roo.data.Observable
1309 * This class is an abstract base class for implementations which provide retrieval of
1310 * unformatted data objects.<br>
1312 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1313 * (of the appropriate type which knows how to parse the data object) to provide a block of
1314 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1316 * Custom implementations must implement the load method as described in
1317 * {@link Roo.data.HttpProxy#load}.
1319 Roo.data.DataProxy = function(){
1323 * Fires before a network request is made to retrieve a data object.
1324 * @param {Object} This DataProxy object.
1325 * @param {Object} params The params parameter to the load function.
1330 * Fires before the load method's callback is called.
1331 * @param {Object} This DataProxy object.
1332 * @param {Object} o The data object.
1333 * @param {Object} arg The callback argument object passed to the load function.
1337 * @event loadexception
1338 * Fires if an Exception occurs during data retrieval.
1339 * @param {Object} This DataProxy object.
1340 * @param {Object} o The data object.
1341 * @param {Object} arg The callback argument object passed to the load function.
1342 * @param {Object} e The Exception.
1344 loadexception : true
1346 Roo.data.DataProxy.superclass.constructor.call(this);
1349 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1352 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1356 * Ext JS Library 1.1.1
1357 * Copyright(c) 2006-2007, Ext JS, LLC.
1359 * Originally Released Under LGPL - original licence link has changed is not relivant.
1362 * <script type="text/javascript">
1365 * @class Roo.data.MemoryProxy
1366 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1367 * to the Reader when its load method is called.
1369 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1371 Roo.data.MemoryProxy = function(data){
1375 Roo.data.MemoryProxy.superclass.constructor.call(this);
1379 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1382 * Load data from the requested source (in this case an in-memory
1383 * data object passed to the constructor), read the data object into
1384 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1385 * process that block using the passed callback.
1386 * @param {Object} params This parameter is not used by the MemoryProxy class.
1387 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1388 * object into a block of Roo.data.Records.
1389 * @param {Function} callback The function into which to pass the block of Roo.data.records.
1390 * The function must be passed <ul>
1391 * <li>The Record block object</li>
1392 * <li>The "arg" argument from the load function</li>
1393 * <li>A boolean success indicator</li>
1395 * @param {Object} scope The scope in which to call the callback
1396 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1398 load : function(params, reader, callback, scope, arg){
1399 params = params || {};
1402 result = reader.readRecords(params.data ? params.data :this.data);
1404 this.fireEvent("loadexception", this, arg, null, e);
1405 callback.call(scope, null, arg, false);
1408 callback.call(scope, result, arg, true);
1412 update : function(params, records){
1417 * Ext JS Library 1.1.1
1418 * Copyright(c) 2006-2007, Ext JS, LLC.
1420 * Originally Released Under LGPL - original licence link has changed is not relivant.
1423 * <script type="text/javascript">
1426 * @class Roo.data.HttpProxy
1427 * @extends Roo.data.DataProxy
1428 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1429 * configured to reference a certain URL.<br><br>
1431 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1432 * from which the running page was served.<br><br>
1434 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1436 * Be aware that to enable the browser to parse an XML document, the server must set
1437 * the Content-Type header in the HTTP response to "text/xml".
1439 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1440 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
1441 * will be used to make the request.
1443 Roo.data.HttpProxy = function(conn){
1444 Roo.data.HttpProxy.superclass.constructor.call(this);
1445 // is conn a conn config or a real conn?
1447 this.useAjax = !conn || !conn.events;
1451 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1452 // thse are take from connection...
1455 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1458 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1459 * extra parameters to each request made by this object. (defaults to undefined)
1462 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1463 * to each request made by this object. (defaults to undefined)
1466 * @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)
1469 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1472 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1478 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1482 * Return the {@link Roo.data.Connection} object being used by this Proxy.
1483 * @return {Connection} The Connection object. This object may be used to subscribe to events on
1484 * a finer-grained basis than the DataProxy events.
1486 getConnection : function(){
1487 return this.useAjax ? Roo.Ajax : this.conn;
1491 * Load data from the configured {@link Roo.data.Connection}, read the data object into
1492 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1493 * process that block using the passed callback.
1494 * @param {Object} params An object containing properties which are to be used as HTTP parameters
1495 * for the request to the remote server.
1496 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1497 * object into a block of Roo.data.Records.
1498 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1499 * The function must be passed <ul>
1500 * <li>The Record block object</li>
1501 * <li>The "arg" argument from the load function</li>
1502 * <li>A boolean success indicator</li>
1504 * @param {Object} scope The scope in which to call the callback
1505 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1507 load : function(params, reader, callback, scope, arg){
1508 if(this.fireEvent("beforeload", this, params) !== false){
1510 params : params || {},
1512 callback : callback,
1517 callback : this.loadResponse,
1521 Roo.applyIf(o, this.conn);
1522 if(this.activeRequest){
1523 Roo.Ajax.abort(this.activeRequest);
1525 this.activeRequest = Roo.Ajax.request(o);
1527 this.conn.request(o);
1530 callback.call(scope||this, null, arg, false);
1535 loadResponse : function(o, success, response){
1536 delete this.activeRequest;
1538 this.fireEvent("loadexception", this, o, response);
1539 o.request.callback.call(o.request.scope, null, o.request.arg, false);
1544 result = o.reader.read(response);
1546 this.fireEvent("loadexception", this, o, response, e);
1547 o.request.callback.call(o.request.scope, null, o.request.arg, false);
1551 this.fireEvent("load", this, o, o.request.arg);
1552 o.request.callback.call(o.request.scope, result, o.request.arg, true);
1556 update : function(dataSet){
1561 updateResponse : function(dataSet){
1566 * Ext JS Library 1.1.1
1567 * Copyright(c) 2006-2007, Ext JS, LLC.
1569 * Originally Released Under LGPL - original licence link has changed is not relivant.
1572 * <script type="text/javascript">
1576 * @class Roo.data.ScriptTagProxy
1577 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1578 * other than the originating domain of the running page.<br><br>
1580 * <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
1581 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1583 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1584 * source code that is used as the source inside a <script> tag.<br><br>
1586 * In order for the browser to process the returned data, the server must wrap the data object
1587 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1588 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1589 * depending on whether the callback name was passed:
1592 boolean scriptTag = false;
1593 String cb = request.getParameter("callback");
1596 response.setContentType("text/javascript");
1598 response.setContentType("application/x-json");
1600 Writer out = response.getWriter();
1602 out.write(cb + "(");
1604 out.print(dataBlock.toJsonString());
1611 * @param {Object} config A configuration object.
1613 Roo.data.ScriptTagProxy = function(config){
1614 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1615 Roo.apply(this, config);
1616 this.head = document.getElementsByTagName("head")[0];
1619 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1621 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1623 * @cfg {String} url The URL from which to request the data object.
1626 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1630 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1631 * the server the name of the callback function set up by the load call to process the returned data object.
1632 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1633 * javascript output which calls this named function passing the data object as its only parameter.
1635 callbackParam : "callback",
1637 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1638 * name to the request.
1643 * Load data from the configured URL, read the data object into
1644 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1645 * process that block using the passed callback.
1646 * @param {Object} params An object containing properties which are to be used as HTTP parameters
1647 * for the request to the remote server.
1648 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1649 * object into a block of Roo.data.Records.
1650 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1651 * The function must be passed <ul>
1652 * <li>The Record block object</li>
1653 * <li>The "arg" argument from the load function</li>
1654 * <li>A boolean success indicator</li>
1656 * @param {Object} scope The scope in which to call the callback
1657 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1659 load : function(params, reader, callback, scope, arg){
1660 if(this.fireEvent("beforeload", this, params) !== false){
1662 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1665 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1667 url += "&_dc=" + (new Date().getTime());
1669 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1672 cb : "stcCallback"+transId,
1673 scriptId : "stcScript"+transId,
1677 callback : callback,
1683 window[trans.cb] = function(o){
1684 conn.handleResponse(o, trans);
1687 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1689 if(this.autoAbort !== false){
1693 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1695 var script = document.createElement("script");
1696 script.setAttribute("src", url);
1697 script.setAttribute("type", "text/javascript");
1698 script.setAttribute("id", trans.scriptId);
1699 this.head.appendChild(script);
1703 callback.call(scope||this, null, arg, false);
1708 isLoading : function(){
1709 return this.trans ? true : false;
1713 * Abort the current server request.
1716 if(this.isLoading()){
1717 this.destroyTrans(this.trans);
1722 destroyTrans : function(trans, isLoaded){
1723 this.head.removeChild(document.getElementById(trans.scriptId));
1724 clearTimeout(trans.timeoutId);
1726 window[trans.cb] = undefined;
1728 delete window[trans.cb];
1731 // if hasn't been loaded, wait for load to remove it to prevent script error
1732 window[trans.cb] = function(){
1733 window[trans.cb] = undefined;
1735 delete window[trans.cb];
1742 handleResponse : function(o, trans){
1744 this.destroyTrans(trans, true);
1747 result = trans.reader.readRecords(o);
1749 this.fireEvent("loadexception", this, o, trans.arg, e);
1750 trans.callback.call(trans.scope||window, null, trans.arg, false);
1753 this.fireEvent("load", this, o, trans.arg);
1754 trans.callback.call(trans.scope||window, result, trans.arg, true);
1758 handleFailure : function(trans){
1760 this.destroyTrans(trans, false);
1761 this.fireEvent("loadexception", this, null, trans.arg);
1762 trans.callback.call(trans.scope||window, null, trans.arg, false);
1766 * Ext JS Library 1.1.1
1767 * Copyright(c) 2006-2007, Ext JS, LLC.
1769 * Originally Released Under LGPL - original licence link has changed is not relivant.
1772 * <script type="text/javascript">
1776 * @class Roo.data.JsonReader
1777 * @extends Roo.data.DataReader
1778 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1779 * based on mappings in a provided Roo.data.Record constructor.
1781 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1782 * in the reply previously.
1787 var RecordDef = Roo.data.Record.create([
1788 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
1789 {name: 'occupation'} // This field will use "occupation" as the mapping.
1791 var myReader = new Roo.data.JsonReader({
1792 totalProperty: "results", // The property which contains the total dataset size (optional)
1793 root: "rows", // The property which contains an Array of row objects
1794 id: "id" // The property within each row object that provides an ID for the record (optional)
1798 * This would consume a JSON file like this:
1800 { 'results': 2, 'rows': [
1801 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1802 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1805 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1806 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1807 * paged from the remote server.
1808 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1809 * @cfg {String} root name of the property which contains the Array of row objects.
1810 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1811 * @cfg {Array} fields Array of field definition objects
1813 * Create a new JsonReader
1814 * @param {Object} meta Metadata configuration options
1815 * @param {Object} recordType Either an Array of field definition objects,
1816 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1818 Roo.data.JsonReader = function(meta, recordType){
1821 // set some defaults:
1823 totalProperty: 'total',
1824 successProperty : 'success',
1829 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1831 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1834 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
1835 * Used by Store query builder to append _requestMeta to params.
1838 metaFromRemote : false,
1840 * This method is only used by a DataProxy which has retrieved data from a remote server.
1841 * @param {Object} response The XHR object which contains the JSON data in its responseText.
1842 * @return {Object} data A data block which is used by an Roo.data.Store object as
1843 * a cache of Roo.data.Records.
1845 read : function(response){
1846 var json = response.responseText;
1848 var o = /* eval:var:o */ eval("("+json+")");
1850 throw {message: "JsonReader.read: Json object not found"};
1856 this.metaFromRemote = true;
1857 this.meta = o.metaData;
1858 this.recordType = Roo.data.Record.create(o.metaData.fields);
1859 this.onMetaChange(this.meta, this.recordType, o);
1861 return this.readRecords(o);
1864 // private function a store will implement
1865 onMetaChange : function(meta, recordType, o){
1872 simpleAccess: function(obj, subsc) {
1879 getJsonAccessor: function(){
1881 return function(expr) {
1883 return(re.test(expr))
1884 ? new Function("obj", "return obj." + expr)
1894 * Create a data block containing Roo.data.Records from an XML document.
1895 * @param {Object} o An object which contains an Array of row objects in the property specified
1896 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1897 * which contains the total size of the dataset.
1898 * @return {Object} data A data block which is used by an Roo.data.Store object as
1899 * a cache of Roo.data.Records.
1901 readRecords : function(o){
1903 * After any data loads, the raw JSON data is available for further custom processing.
1907 var s = this.meta, Record = this.recordType,
1908 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1910 // Generate extraction functions for the totalProperty, the root, the id, and for each field
1912 if(s.totalProperty) {
1913 this.getTotal = this.getJsonAccessor(s.totalProperty);
1915 if(s.successProperty) {
1916 this.getSuccess = this.getJsonAccessor(s.successProperty);
1918 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1920 var g = this.getJsonAccessor(s.id);
1921 this.getId = function(rec) {
1923 return (r === undefined || r === "") ? null : r;
1926 this.getId = function(){return null;};
1929 for(var jj = 0; jj < fl; jj++){
1931 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1932 this.ef[jj] = this.getJsonAccessor(map);
1936 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1937 if(s.totalProperty){
1938 var vt = parseInt(this.getTotal(o), 10);
1943 if(s.successProperty){
1944 var vs = this.getSuccess(o);
1945 if(vs === false || vs === 'false'){
1950 for(var i = 0; i < c; i++){
1953 var id = this.getId(n);
1954 for(var j = 0; j < fl; j++){
1956 var v = this.ef[j](n);
1958 Roo.log('missing convert for ' + f.name);
1962 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1964 var record = new Record(values, id);
1966 records[i] = record;
1972 totalRecords : totalRecords
1977 * Ext JS Library 1.1.1
1978 * Copyright(c) 2006-2007, Ext JS, LLC.
1980 * Originally Released Under LGPL - original licence link has changed is not relivant.
1983 * <script type="text/javascript">
1987 * @class Roo.data.XmlReader
1988 * @extends Roo.data.DataReader
1989 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
1990 * based on mappings in a provided Roo.data.Record constructor.<br><br>
1992 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
1993 * header in the HTTP response must be set to "text/xml".</em>
1997 var RecordDef = Roo.data.Record.create([
1998 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
1999 {name: 'occupation'} // This field will use "occupation" as the mapping.
2001 var myReader = new Roo.data.XmlReader({
2002 totalRecords: "results", // The element which contains the total dataset size (optional)
2003 record: "row", // The repeated element which contains row information
2004 id: "id" // The element within the row that provides an ID for the record (optional)
2008 * This would consume an XML file like this:
2012 <results>2</results>
2015 <name>Bill</name>
2016 <occupation>Gardener</occupation>
2020 <name>Ben</name>
2021 <occupation>Horticulturalist</occupation>
2025 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2026 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2027 * paged from the remote server.
2028 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2029 * @cfg {String} success The DomQuery path to the success attribute used by forms.
2030 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2031 * a record identifier value.
2033 * Create a new XmlReader
2034 * @param {Object} meta Metadata configuration options
2035 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
2036 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2037 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
2039 Roo.data.XmlReader = function(meta, recordType){
2041 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2043 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2045 * This method is only used by a DataProxy which has retrieved data from a remote server.
2046 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
2047 * to contain a method called 'responseXML' that returns an XML document object.
2048 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2049 * a cache of Roo.data.Records.
2051 read : function(response){
2052 var doc = response.responseXML;
2054 throw {message: "XmlReader.read: XML Document not available"};
2056 return this.readRecords(doc);
2060 * Create a data block containing Roo.data.Records from an XML document.
2061 * @param {Object} doc A parsed XML document.
2062 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2063 * a cache of Roo.data.Records.
2065 readRecords : function(doc){
2067 * After any data loads/reads, the raw XML Document is available for further custom processing.
2071 var root = doc.documentElement || doc;
2072 var q = Roo.DomQuery;
2073 var recordType = this.recordType, fields = recordType.prototype.fields;
2074 var sid = this.meta.id;
2075 var totalRecords = 0, success = true;
2076 if(this.meta.totalRecords){
2077 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2080 if(this.meta.success){
2081 var sv = q.selectValue(this.meta.success, root, true);
2082 success = sv !== false && sv !== 'false';
2085 var ns = q.select(this.meta.record, root);
2086 for(var i = 0, len = ns.length; i < len; i++) {
2089 var id = sid ? q.selectValue(sid, n) : undefined;
2090 for(var j = 0, jlen = fields.length; j < jlen; j++){
2091 var f = fields.items[j];
2092 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2096 var record = new recordType(values, id);
2098 records[records.length] = record;
2104 totalRecords : totalRecords || records.length
2109 * Ext JS Library 1.1.1
2110 * Copyright(c) 2006-2007, Ext JS, LLC.
2112 * Originally Released Under LGPL - original licence link has changed is not relivant.
2115 * <script type="text/javascript">
2119 * @class Roo.data.ArrayReader
2120 * @extends Roo.data.DataReader
2121 * Data reader class to create an Array of Roo.data.Record objects from an Array.
2122 * Each element of that Array represents a row of data fields. The
2123 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2124 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2128 var RecordDef = Roo.data.Record.create([
2129 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
2130 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
2132 var myReader = new Roo.data.ArrayReader({
2133 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
2137 * This would consume an Array like this:
2139 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2143 * Create a new JsonReader
2144 * @param {Object} meta Metadata configuration options.
2145 * @param {Object|Array} recordType Either an Array of field definition objects
2147 * @cfg {Array} fields Array of field definition objects
2148 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2149 * as specified to {@link Roo.data.Record#create},
2150 * or an {@link Roo.data.Record} object
2153 * created using {@link Roo.data.Record#create}.
2155 Roo.data.ArrayReader = function(meta, recordType){
2158 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2161 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2163 * Create a data block containing Roo.data.Records from an XML document.
2164 * @param {Object} o An Array of row objects which represents the dataset.
2165 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2166 * a cache of Roo.data.Records.
2168 readRecords : function(o){
2169 var sid = this.meta ? this.meta.id : null;
2170 var recordType = this.recordType, fields = recordType.prototype.fields;
2173 for(var i = 0; i < root.length; i++){
2176 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2177 for(var j = 0, jlen = fields.length; j < jlen; j++){
2178 var f = fields.items[j];
2179 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2180 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2184 var record = new recordType(values, id);
2186 records[records.length] = record;
2190 totalRecords : records.length
2195 * Ext JS Library 1.1.1
2196 * Copyright(c) 2006-2007, Ext JS, LLC.
2198 * Originally Released Under LGPL - original licence link has changed is not relivant.
2201 * <script type="text/javascript">
2206 * @class Roo.data.Tree
2207 * @extends Roo.util.Observable
2208 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2209 * in the tree have most standard DOM functionality.
2211 * @param {Node} root (optional) The root node
2213 Roo.data.Tree = function(root){
2216 * The root node for this tree
2221 this.setRootNode(root);
2226 * Fires when a new child node is appended to a node in this tree.
2227 * @param {Tree} tree The owner tree
2228 * @param {Node} parent The parent node
2229 * @param {Node} node The newly appended node
2230 * @param {Number} index The index of the newly appended node
2235 * Fires when a child node is removed from a node in this tree.
2236 * @param {Tree} tree The owner tree
2237 * @param {Node} parent The parent node
2238 * @param {Node} node The child node removed
2243 * Fires when a node is moved to a new location in the tree
2244 * @param {Tree} tree The owner tree
2245 * @param {Node} node The node moved
2246 * @param {Node} oldParent The old parent of this node
2247 * @param {Node} newParent The new parent of this node
2248 * @param {Number} index The index it was moved to
2253 * Fires when a new child node is inserted in a node in this tree.
2254 * @param {Tree} tree The owner tree
2255 * @param {Node} parent The parent node
2256 * @param {Node} node The child node inserted
2257 * @param {Node} refNode The child node the node was inserted before
2261 * @event beforeappend
2262 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2263 * @param {Tree} tree The owner tree
2264 * @param {Node} parent The parent node
2265 * @param {Node} node The child node to be appended
2267 "beforeappend" : true,
2269 * @event beforeremove
2270 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2271 * @param {Tree} tree The owner tree
2272 * @param {Node} parent The parent node
2273 * @param {Node} node The child node to be removed
2275 "beforeremove" : true,
2278 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2279 * @param {Tree} tree The owner tree
2280 * @param {Node} node The node being moved
2281 * @param {Node} oldParent The parent of the node
2282 * @param {Node} newParent The new parent the node is moving to
2283 * @param {Number} index The index it is being moved to
2285 "beforemove" : true,
2287 * @event beforeinsert
2288 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2289 * @param {Tree} tree The owner tree
2290 * @param {Node} parent The parent node
2291 * @param {Node} node The child node to be inserted
2292 * @param {Node} refNode The child node the node is being inserted before
2294 "beforeinsert" : true
2297 Roo.data.Tree.superclass.constructor.call(this);
2300 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2303 proxyNodeEvent : function(){
2304 return this.fireEvent.apply(this, arguments);
2308 * Returns the root node for this tree.
2311 getRootNode : function(){
2316 * Sets the root node for this tree.
2317 * @param {Node} node
2320 setRootNode : function(node){
2322 node.ownerTree = this;
2324 this.registerNode(node);
2329 * Gets a node in this tree by its id.
2330 * @param {String} id
2333 getNodeById : function(id){
2334 return this.nodeHash[id];
2337 registerNode : function(node){
2338 this.nodeHash[node.id] = node;
2341 unregisterNode : function(node){
2342 delete this.nodeHash[node.id];
2345 toString : function(){
2346 return "[Tree"+(this.id?" "+this.id:"")+"]";
2351 * @class Roo.data.Node
2352 * @extends Roo.util.Observable
2353 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2354 * @cfg {String} id The id for this node. If one is not specified, one is generated.
2356 * @param {Object} attributes The attributes/config for the node
2358 Roo.data.Node = function(attributes){
2360 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2363 this.attributes = attributes || {};
2364 this.leaf = this.attributes.leaf;
2366 * The node id. @type String
2368 this.id = this.attributes.id;
2370 this.id = Roo.id(null, "ynode-");
2371 this.attributes.id = this.id;
2376 * All child nodes of this node. @type Array
2378 this.childNodes = [];
2379 if(!this.childNodes.indexOf){ // indexOf is a must
2380 this.childNodes.indexOf = function(o){
2381 for(var i = 0, len = this.length; i < len; i++){
2390 * The parent node for this node. @type Node
2392 this.parentNode = null;
2394 * The first direct child node of this node, or null if this node has no child nodes. @type Node
2396 this.firstChild = null;
2398 * The last direct child node of this node, or null if this node has no child nodes. @type Node
2400 this.lastChild = null;
2402 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2404 this.previousSibling = null;
2406 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2408 this.nextSibling = null;
2413 * Fires when a new child node is appended
2414 * @param {Tree} tree The owner tree
2415 * @param {Node} this This node
2416 * @param {Node} node The newly appended node
2417 * @param {Number} index The index of the newly appended node
2422 * Fires when a child node is removed
2423 * @param {Tree} tree The owner tree
2424 * @param {Node} this This node
2425 * @param {Node} node The removed node
2430 * Fires when this node is moved to a new location in the tree
2431 * @param {Tree} tree The owner tree
2432 * @param {Node} this This node
2433 * @param {Node} oldParent The old parent of this node
2434 * @param {Node} newParent The new parent of this node
2435 * @param {Number} index The index it was moved to
2440 * Fires when a new child node is inserted.
2441 * @param {Tree} tree The owner tree
2442 * @param {Node} this This node
2443 * @param {Node} node The child node inserted
2444 * @param {Node} refNode The child node the node was inserted before
2448 * @event beforeappend
2449 * Fires before a new child is appended, return false to cancel the append.
2450 * @param {Tree} tree The owner tree
2451 * @param {Node} this This node
2452 * @param {Node} node The child node to be appended
2454 "beforeappend" : true,
2456 * @event beforeremove
2457 * Fires before a child is removed, return false to cancel the remove.
2458 * @param {Tree} tree The owner tree
2459 * @param {Node} this This node
2460 * @param {Node} node The child node to be removed
2462 "beforeremove" : true,
2465 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2466 * @param {Tree} tree The owner tree
2467 * @param {Node} this This node
2468 * @param {Node} oldParent The parent of this node
2469 * @param {Node} newParent The new parent this node is moving to
2470 * @param {Number} index The index it is being moved to
2472 "beforemove" : true,
2474 * @event beforeinsert
2475 * Fires before a new child is inserted, return false to cancel the insert.
2476 * @param {Tree} tree The owner tree
2477 * @param {Node} this This node
2478 * @param {Node} node The child node to be inserted
2479 * @param {Node} refNode The child node the node is being inserted before
2481 "beforeinsert" : true
2483 this.listeners = this.attributes.listeners;
2484 Roo.data.Node.superclass.constructor.call(this);
2487 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2488 fireEvent : function(evtName){
2489 // first do standard event for this node
2490 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2493 // then bubble it up to the tree if the event wasn't cancelled
2494 var ot = this.getOwnerTree();
2496 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2504 * Returns true if this node is a leaf
2507 isLeaf : function(){
2508 return this.leaf === true;
2512 setFirstChild : function(node){
2513 this.firstChild = node;
2517 setLastChild : function(node){
2518 this.lastChild = node;
2523 * Returns true if this node is the last child of its parent
2526 isLast : function(){
2527 return (!this.parentNode ? true : this.parentNode.lastChild == this);
2531 * Returns true if this node is the first child of its parent
2534 isFirst : function(){
2535 return (!this.parentNode ? true : this.parentNode.firstChild == this);
2538 hasChildNodes : function(){
2539 return !this.isLeaf() && this.childNodes.length > 0;
2543 * Insert node(s) as the last child node of this node.
2544 * @param {Node/Array} node The node or Array of nodes to append
2545 * @return {Node} The appended node if single append, or null if an array was passed
2547 appendChild : function(node){
2548 Roo.log('tree.js appendChild');
2551 if(node instanceof Array){
2553 }else if(arguments.length > 1){
2557 // if passed an array or multiple args do them one by one
2559 for(var i = 0, len = multi.length; i < len; i++) {
2560 this.appendChild(multi[i]);
2563 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2566 var index = this.childNodes.length;
2567 var oldParent = node.parentNode;
2568 // it's a move, make sure we move it cleanly
2570 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2573 oldParent.removeChild(node);
2576 index = this.childNodes.length;
2578 this.setFirstChild(node);
2580 this.childNodes.push(node);
2581 node.parentNode = this;
2582 var ps = this.childNodes[index-1];
2584 node.previousSibling = ps;
2585 ps.nextSibling = node;
2587 node.previousSibling = null;
2589 node.nextSibling = null;
2590 this.setLastChild(node);
2591 node.setOwnerTree(this.getOwnerTree());
2592 this.fireEvent("append", this.ownerTree, this, node, index);
2594 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2601 * Removes a child node from this node.
2602 * @param {Node} node The node to remove
2603 * @return {Node} The removed node
2605 removeChild : function(node){
2606 var index = this.childNodes.indexOf(node);
2610 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2614 // remove it from childNodes collection
2615 this.childNodes.splice(index, 1);
2618 if(node.previousSibling){
2619 node.previousSibling.nextSibling = node.nextSibling;
2621 if(node.nextSibling){
2622 node.nextSibling.previousSibling = node.previousSibling;
2625 // update child refs
2626 if(this.firstChild == node){
2627 this.setFirstChild(node.nextSibling);
2629 if(this.lastChild == node){
2630 this.setLastChild(node.previousSibling);
2633 node.setOwnerTree(null);
2634 // clear any references from the node
2635 node.parentNode = null;
2636 node.previousSibling = null;
2637 node.nextSibling = null;
2638 this.fireEvent("remove", this.ownerTree, this, node);
2643 * Inserts the first node before the second node in this nodes childNodes collection.
2644 * @param {Node} node The node to insert
2645 * @param {Node} refNode The node to insert before (if null the node is appended)
2646 * @return {Node} The inserted node
2648 insertBefore : function(node, refNode){
2649 if(!refNode){ // like standard Dom, refNode can be null for append
2650 return this.appendChild(node);
2653 if(node == refNode){
2657 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2660 var index = this.childNodes.indexOf(refNode);
2661 var oldParent = node.parentNode;
2662 var refIndex = index;
2664 // when moving internally, indexes will change after remove
2665 if(oldParent == this && this.childNodes.indexOf(node) < index){
2669 // it's a move, make sure we move it cleanly
2671 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2674 oldParent.removeChild(node);
2677 this.setFirstChild(node);
2679 this.childNodes.splice(refIndex, 0, node);
2680 node.parentNode = this;
2681 var ps = this.childNodes[refIndex-1];
2683 node.previousSibling = ps;
2684 ps.nextSibling = node;
2686 node.previousSibling = null;
2688 node.nextSibling = refNode;
2689 refNode.previousSibling = node;
2690 node.setOwnerTree(this.getOwnerTree());
2691 this.fireEvent("insert", this.ownerTree, this, node, refNode);
2693 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2699 * Returns the child node at the specified index.
2700 * @param {Number} index
2703 item : function(index){
2704 return this.childNodes[index];
2708 * Replaces one child node in this node with another.
2709 * @param {Node} newChild The replacement node
2710 * @param {Node} oldChild The node to replace
2711 * @return {Node} The replaced node
2713 replaceChild : function(newChild, oldChild){
2714 this.insertBefore(newChild, oldChild);
2715 this.removeChild(oldChild);
2720 * Returns the index of a child node
2721 * @param {Node} node
2722 * @return {Number} The index of the node or -1 if it was not found
2724 indexOf : function(child){
2725 return this.childNodes.indexOf(child);
2729 * Returns the tree this node is in.
2732 getOwnerTree : function(){
2733 // if it doesn't have one, look for one
2734 if(!this.ownerTree){
2738 this.ownerTree = p.ownerTree;
2744 return this.ownerTree;
2748 * Returns depth of this node (the root node has a depth of 0)
2751 getDepth : function(){
2754 while(p.parentNode){
2762 setOwnerTree : function(tree){
2763 // if it's move, we need to update everyone
2764 if(tree != this.ownerTree){
2766 this.ownerTree.unregisterNode(this);
2768 this.ownerTree = tree;
2769 var cs = this.childNodes;
2770 for(var i = 0, len = cs.length; i < len; i++) {
2771 cs[i].setOwnerTree(tree);
2774 tree.registerNode(this);
2780 * Returns the path for this node. The path can be used to expand or select this node programmatically.
2781 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2782 * @return {String} The path
2784 getPath : function(attr){
2785 attr = attr || "id";
2786 var p = this.parentNode;
2787 var b = [this.attributes[attr]];
2789 b.unshift(p.attributes[attr]);
2792 var sep = this.getOwnerTree().pathSeparator;
2793 return sep + b.join(sep);
2797 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2798 * function call will be the scope provided or the current node. The arguments to the function
2799 * will be the args provided or the current node. If the function returns false at any point,
2800 * the bubble is stopped.
2801 * @param {Function} fn The function to call
2802 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2803 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2805 bubble : function(fn, scope, args){
2808 if(fn.call(scope || p, args || p) === false){
2816 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2817 * function call will be the scope provided or the current node. The arguments to the function
2818 * will be the args provided or the current node. If the function returns false at any point,
2819 * the cascade is stopped on that branch.
2820 * @param {Function} fn The function to call
2821 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2822 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2824 cascade : function(fn, scope, args){
2825 if(fn.call(scope || this, args || this) !== false){
2826 var cs = this.childNodes;
2827 for(var i = 0, len = cs.length; i < len; i++) {
2828 cs[i].cascade(fn, scope, args);
2834 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2835 * function call will be the scope provided or the current node. The arguments to the function
2836 * will be the args provided or the current node. If the function returns false at any point,
2837 * the iteration stops.
2838 * @param {Function} fn The function to call
2839 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2840 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2842 eachChild : function(fn, scope, args){
2843 var cs = this.childNodes;
2844 for(var i = 0, len = cs.length; i < len; i++) {
2845 if(fn.call(scope || this, args || cs[i]) === false){
2852 * Finds the first child that has the attribute with the specified value.
2853 * @param {String} attribute The attribute name
2854 * @param {Mixed} value The value to search for
2855 * @return {Node} The found child or null if none was found
2857 findChild : function(attribute, value){
2858 var cs = this.childNodes;
2859 for(var i = 0, len = cs.length; i < len; i++) {
2860 if(cs[i].attributes[attribute] == value){
2868 * Finds the first child by a custom function. The child matches if the function passed
2870 * @param {Function} fn
2871 * @param {Object} scope (optional)
2872 * @return {Node} The found child or null if none was found
2874 findChildBy : function(fn, scope){
2875 var cs = this.childNodes;
2876 for(var i = 0, len = cs.length; i < len; i++) {
2877 if(fn.call(scope||cs[i], cs[i]) === true){
2885 * Sorts this nodes children using the supplied sort function
2886 * @param {Function} fn
2887 * @param {Object} scope (optional)
2889 sort : function(fn, scope){
2890 var cs = this.childNodes;
2891 var len = cs.length;
2893 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2895 for(var i = 0; i < len; i++){
2897 n.previousSibling = cs[i-1];
2898 n.nextSibling = cs[i+1];
2900 this.setFirstChild(n);
2903 this.setLastChild(n);
2910 * Returns true if this node is an ancestor (at any point) of the passed node.
2911 * @param {Node} node
2914 contains : function(node){
2915 return node.isAncestor(this);
2919 * Returns true if the passed node is an ancestor (at any point) of this node.
2920 * @param {Node} node
2923 isAncestor : function(node){
2924 var p = this.parentNode;
2934 toString : function(){
2935 return "[Node"+(this.id?" "+this.id:"")+"]";
2939 * Ext JS Library 1.1.1
2940 * Copyright(c) 2006-2007, Ext JS, LLC.
2942 * Originally Released Under LGPL - original licence link has changed is not relivant.
2945 * <script type="text/javascript">
2950 * @extends Roo.Element
2951 * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2952 * automatic maintaining of shadow/shim positions.
2953 * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2954 * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2955 * you can pass a string with a CSS class name. False turns off the shadow.
2956 * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2957 * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2958 * @cfg {String} cls CSS class to add to the element
2959 * @cfg {Number} zindex Starting z-index (defaults to 11000)
2960 * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2962 * @param {Object} config An object with config options.
2963 * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
2966 Roo.Layer = function(config, existingEl){
2967 config = config || {};
2968 var dh = Roo.DomHelper;
2969 var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
2971 this.dom = Roo.getDom(existingEl);
2974 var o = config.dh || {tag: "div", cls: "x-layer"};
2975 this.dom = dh.append(pel, o);
2978 this.addClass(config.cls);
2980 this.constrain = config.constrain !== false;
2981 this.visibilityMode = Roo.Element.VISIBILITY;
2983 this.id = this.dom.id = config.id;
2985 this.id = Roo.id(this.dom);
2987 this.zindex = config.zindex || this.getZIndex();
2988 this.position("absolute", this.zindex);
2990 this.shadowOffset = config.shadowOffset || 4;
2991 this.shadow = new Roo.Shadow({
2992 offset : this.shadowOffset,
2993 mode : config.shadow
2996 this.shadowOffset = 0;
2998 this.useShim = config.shim !== false && Roo.useShims;
2999 this.useDisplay = config.useDisplay;
3003 var supr = Roo.Element.prototype;
3005 // shims are shared among layer to keep from having 100 iframes
3008 Roo.extend(Roo.Layer, Roo.Element, {
3010 getZIndex : function(){
3011 return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3014 getShim : function(){
3021 var shim = shims.shift();
3023 shim = this.createShim();
3024 shim.enableDisplayMode('block');
3025 shim.dom.style.display = 'none';
3026 shim.dom.style.visibility = 'visible';
3028 var pn = this.dom.parentNode;
3029 if(shim.dom.parentNode != pn){
3030 pn.insertBefore(shim.dom, this.dom);
3032 shim.setStyle('z-index', this.getZIndex()-2);
3037 hideShim : function(){
3039 this.shim.setDisplayed(false);
3040 shims.push(this.shim);
3045 disableShadow : function(){
3047 this.shadowDisabled = true;
3049 this.lastShadowOffset = this.shadowOffset;
3050 this.shadowOffset = 0;
3054 enableShadow : function(show){
3056 this.shadowDisabled = false;
3057 this.shadowOffset = this.lastShadowOffset;
3058 delete this.lastShadowOffset;
3066 // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3067 // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3068 sync : function(doShow){
3069 var sw = this.shadow;
3070 if(!this.updating && this.isVisible() && (sw || this.useShim)){
3071 var sh = this.getShim();
3073 var w = this.getWidth(),
3074 h = this.getHeight();
3076 var l = this.getLeft(true),
3077 t = this.getTop(true);
3079 if(sw && !this.shadowDisabled){
3080 if(doShow && !sw.isVisible()){
3083 sw.realign(l, t, w, h);
3089 // fit the shim behind the shadow, so it is shimmed too
3090 var a = sw.adjusts, s = sh.dom.style;
3091 s.left = (Math.min(l, l+a.l))+"px";
3092 s.top = (Math.min(t, t+a.t))+"px";
3093 s.width = (w+a.w)+"px";
3094 s.height = (h+a.h)+"px";
3101 sh.setLeftTop(l, t);
3108 destroy : function(){
3113 this.removeAllListeners();
3114 var pn = this.dom.parentNode;
3116 pn.removeChild(this.dom);
3118 Roo.Element.uncache(this.id);
3121 remove : function(){
3126 beginUpdate : function(){
3127 this.updating = true;
3131 endUpdate : function(){
3132 this.updating = false;
3137 hideUnders : function(negOffset){
3145 constrainXY : function(){
3147 var vw = Roo.lib.Dom.getViewWidth(),
3148 vh = Roo.lib.Dom.getViewHeight();
3149 var s = Roo.get(document).getScroll();
3151 var xy = this.getXY();
3152 var x = xy[0], y = xy[1];
3153 var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3154 // only move it if it needs it
3156 // first validate right/bottom
3157 if((x + w) > vw+s.left){
3158 x = vw - w - this.shadowOffset;
3161 if((y + h) > vh+s.top){
3162 y = vh - h - this.shadowOffset;
3165 // then make sure top/left isn't negative
3176 var ay = this.avoidY;
3177 if(y <= ay && (y+h) >= ay){
3183 supr.setXY.call(this, xy);
3189 isVisible : function(){
3190 return this.visible;
3194 showAction : function(){
3195 this.visible = true; // track visibility to prevent getStyle calls
3196 if(this.useDisplay === true){
3197 this.setDisplayed("");
3198 }else if(this.lastXY){
3199 supr.setXY.call(this, this.lastXY);
3200 }else if(this.lastLT){
3201 supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3206 hideAction : function(){
3207 this.visible = false;
3208 if(this.useDisplay === true){
3209 this.setDisplayed(false);
3211 this.setLeftTop(-10000,-10000);
3215 // overridden Element method
3216 setVisible : function(v, a, d, c, e){
3221 var cb = function(){
3226 }.createDelegate(this);
3227 supr.setVisible.call(this, true, true, d, cb, e);
3230 this.hideUnders(true);
3239 }.createDelegate(this);
3241 supr.setVisible.call(this, v, a, d, cb, e);
3250 storeXY : function(xy){
3255 storeLeftTop : function(left, top){
3257 this.lastLT = [left, top];
3261 beforeFx : function(){
3262 this.beforeAction();
3263 return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3267 afterFx : function(){
3268 Roo.Layer.superclass.afterFx.apply(this, arguments);
3269 this.sync(this.isVisible());
3273 beforeAction : function(){
3274 if(!this.updating && this.shadow){
3279 // overridden Element method
3280 setLeft : function(left){
3281 this.storeLeftTop(left, this.getTop(true));
3282 supr.setLeft.apply(this, arguments);
3286 setTop : function(top){
3287 this.storeLeftTop(this.getLeft(true), top);
3288 supr.setTop.apply(this, arguments);
3292 setLeftTop : function(left, top){
3293 this.storeLeftTop(left, top);
3294 supr.setLeftTop.apply(this, arguments);
3298 setXY : function(xy, a, d, c, e){
3300 this.beforeAction();
3302 var cb = this.createCB(c);
3303 supr.setXY.call(this, xy, a, d, cb, e);
3310 createCB : function(c){
3321 // overridden Element method
3322 setX : function(x, a, d, c, e){
3323 this.setXY([x, this.getY()], a, d, c, e);
3326 // overridden Element method
3327 setY : function(y, a, d, c, e){
3328 this.setXY([this.getX(), y], a, d, c, e);
3331 // overridden Element method
3332 setSize : function(w, h, a, d, c, e){
3333 this.beforeAction();
3334 var cb = this.createCB(c);
3335 supr.setSize.call(this, w, h, a, d, cb, e);
3341 // overridden Element method
3342 setWidth : function(w, a, d, c, e){
3343 this.beforeAction();
3344 var cb = this.createCB(c);
3345 supr.setWidth.call(this, w, a, d, cb, e);
3351 // overridden Element method
3352 setHeight : function(h, a, d, c, e){
3353 this.beforeAction();
3354 var cb = this.createCB(c);
3355 supr.setHeight.call(this, h, a, d, cb, e);
3361 // overridden Element method
3362 setBounds : function(x, y, w, h, a, d, c, e){
3363 this.beforeAction();
3364 var cb = this.createCB(c);
3366 this.storeXY([x, y]);
3367 supr.setXY.call(this, [x, y]);
3368 supr.setSize.call(this, w, h, a, d, cb, e);
3371 supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3377 * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3378 * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3379 * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3380 * @param {Number} zindex The new z-index to set
3381 * @return {this} The Layer
3383 setZIndex : function(zindex){
3384 this.zindex = zindex;
3385 this.setStyle("z-index", zindex + 2);
3387 this.shadow.setZIndex(zindex + 1);
3390 this.shim.setStyle("z-index", zindex);
3396 * Ext JS Library 1.1.1
3397 * Copyright(c) 2006-2007, Ext JS, LLC.
3399 * Originally Released Under LGPL - original licence link has changed is not relivant.
3402 * <script type="text/javascript">
3408 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
3409 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
3410 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3412 * Create a new Shadow
3413 * @param {Object} config The config object
3415 Roo.Shadow = function(config){
3416 Roo.apply(this, config);
3417 if(typeof this.mode != "string"){
3418 this.mode = this.defaultMode;
3420 var o = this.offset, a = {h: 0};
3421 var rad = Math.floor(this.offset/2);
3422 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3428 a.l -= this.offset + rad;
3429 a.t -= this.offset + rad;
3440 a.l -= (this.offset - rad);
3441 a.t -= this.offset + rad;
3443 a.w -= (this.offset - rad)*2;
3454 a.l -= (this.offset - rad);
3455 a.t -= (this.offset - rad);
3457 a.w -= (this.offset + rad + 1);
3458 a.h -= (this.offset + rad);
3467 Roo.Shadow.prototype = {
3469 * @cfg {String} mode
3470 * The shadow display mode. Supports the following options:<br />
3471 * sides: Shadow displays on both sides and bottom only<br />
3472 * frame: Shadow displays equally on all four sides<br />
3473 * drop: Traditional bottom-right drop shadow (default)
3476 * @cfg {String} offset
3477 * The number of pixels to offset the shadow from the element (defaults to 4)
3482 defaultMode: "drop",
3485 * Displays the shadow under the target element
3486 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3488 show : function(target){
3489 target = Roo.get(target);
3491 this.el = Roo.Shadow.Pool.pull();
3492 if(this.el.dom.nextSibling != target.dom){
3493 this.el.insertBefore(target);
3496 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3498 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3501 target.getLeft(true),
3502 target.getTop(true),
3506 this.el.dom.style.display = "block";
3510 * Returns true if the shadow is visible, else false
3512 isVisible : function(){
3513 return this.el ? true : false;
3517 * Direct alignment when values are already available. Show must be called at least once before
3518 * calling this method to ensure it is initialized.
3519 * @param {Number} left The target element left position
3520 * @param {Number} top The target element top position
3521 * @param {Number} width The target element width
3522 * @param {Number} height The target element height
3524 realign : function(l, t, w, h){
3528 var a = this.adjusts, d = this.el.dom, s = d.style;
3530 s.left = (l+a.l)+"px";
3531 s.top = (t+a.t)+"px";
3532 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3534 if(s.width != sws || s.height != shs){
3538 var cn = d.childNodes;
3539 var sww = Math.max(0, (sw-12))+"px";
3540 cn[0].childNodes[1].style.width = sww;
3541 cn[1].childNodes[1].style.width = sww;
3542 cn[2].childNodes[1].style.width = sww;
3543 cn[1].style.height = Math.max(0, (sh-12))+"px";
3553 this.el.dom.style.display = "none";
3554 Roo.Shadow.Pool.push(this.el);
3560 * Adjust the z-index of this shadow
3561 * @param {Number} zindex The new z-index
3563 setZIndex : function(z){
3566 this.el.setStyle("z-index", z);
3571 // Private utility class that manages the internal Shadow cache
3572 Roo.Shadow.Pool = function(){
3574 var markup = Roo.isIE ?
3575 '<div class="x-ie-shadow"></div>' :
3576 '<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>';
3581 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3582 sh.autoBoxAdjust = false;
3587 push : function(sh){
3593 * Ext JS Library 1.1.1
3594 * Copyright(c) 2006-2007, Ext JS, LLC.
3596 * Originally Released Under LGPL - original licence link has changed is not relivant.
3599 * <script type="text/javascript">
3604 * @class Roo.SplitBar
3605 * @extends Roo.util.Observable
3606 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3610 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3611 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3612 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3613 split.minSize = 100;
3614 split.maxSize = 600;
3615 split.animate = true;
3616 split.on('moved', splitterMoved);
3619 * Create a new SplitBar
3620 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
3621 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
3622 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3623 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
3624 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3625 position of the SplitBar).
3627 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3630 this.el = Roo.get(dragElement, true);
3631 this.el.dom.unselectable = "on";
3633 this.resizingEl = Roo.get(resizingElement, true);
3637 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3638 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3641 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3644 * The minimum size of the resizing element. (Defaults to 0)
3650 * The maximum size of the resizing element. (Defaults to 2000)
3653 this.maxSize = 2000;
3656 * Whether to animate the transition to the new size
3659 this.animate = false;
3662 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3665 this.useShim = false;
3672 this.proxy = Roo.SplitBar.createProxy(this.orientation);
3674 this.proxy = Roo.get(existingProxy).dom;
3677 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3680 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3683 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3686 this.dragSpecs = {};
3689 * @private The adapter to use to positon and resize elements
3691 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3692 this.adapter.init(this);
3694 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3696 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3697 this.el.addClass("x-splitbar-h");
3700 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3701 this.el.addClass("x-splitbar-v");
3707 * Fires when the splitter is moved (alias for {@link #event-moved})
3708 * @param {Roo.SplitBar} this
3709 * @param {Number} newSize the new width or height
3714 * Fires when the splitter is moved
3715 * @param {Roo.SplitBar} this
3716 * @param {Number} newSize the new width or height
3720 * @event beforeresize
3721 * Fires before the splitter is dragged
3722 * @param {Roo.SplitBar} this
3724 "beforeresize" : true,
3726 "beforeapply" : true
3729 Roo.util.Observable.call(this);
3732 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3733 onStartProxyDrag : function(x, y){
3734 this.fireEvent("beforeresize", this);
3736 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
3738 o.enableDisplayMode("block");
3739 // all splitbars share the same overlay
3740 Roo.SplitBar.prototype.overlay = o;
3742 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3743 this.overlay.show();
3744 Roo.get(this.proxy).setDisplayed("block");
3745 var size = this.adapter.getElementSize(this);
3746 this.activeMinSize = this.getMinimumSize();;
3747 this.activeMaxSize = this.getMaximumSize();;
3748 var c1 = size - this.activeMinSize;
3749 var c2 = Math.max(this.activeMaxSize - size, 0);
3750 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3751 this.dd.resetConstraints();
3752 this.dd.setXConstraint(
3753 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
3754 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3756 this.dd.setYConstraint(0, 0);
3758 this.dd.resetConstraints();
3759 this.dd.setXConstraint(0, 0);
3760 this.dd.setYConstraint(
3761 this.placement == Roo.SplitBar.TOP ? c1 : c2,
3762 this.placement == Roo.SplitBar.TOP ? c2 : c1
3765 this.dragSpecs.startSize = size;
3766 this.dragSpecs.startPoint = [x, y];
3767 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3771 * @private Called after the drag operation by the DDProxy
3773 onEndProxyDrag : function(e){
3774 Roo.get(this.proxy).setDisplayed(false);
3775 var endPoint = Roo.lib.Event.getXY(e);
3777 this.overlay.hide();
3780 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3781 newSize = this.dragSpecs.startSize +
3782 (this.placement == Roo.SplitBar.LEFT ?
3783 endPoint[0] - this.dragSpecs.startPoint[0] :
3784 this.dragSpecs.startPoint[0] - endPoint[0]
3787 newSize = this.dragSpecs.startSize +
3788 (this.placement == Roo.SplitBar.TOP ?
3789 endPoint[1] - this.dragSpecs.startPoint[1] :
3790 this.dragSpecs.startPoint[1] - endPoint[1]
3793 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3794 if(newSize != this.dragSpecs.startSize){
3795 if(this.fireEvent('beforeapply', this, newSize) !== false){
3796 this.adapter.setElementSize(this, newSize);
3797 this.fireEvent("moved", this, newSize);
3798 this.fireEvent("resize", this, newSize);
3804 * Get the adapter this SplitBar uses
3805 * @return The adapter object
3807 getAdapter : function(){
3808 return this.adapter;
3812 * Set the adapter this SplitBar uses
3813 * @param {Object} adapter A SplitBar adapter object
3815 setAdapter : function(adapter){
3816 this.adapter = adapter;
3817 this.adapter.init(this);
3821 * Gets the minimum size for the resizing element
3822 * @return {Number} The minimum size
3824 getMinimumSize : function(){
3825 return this.minSize;
3829 * Sets the minimum size for the resizing element
3830 * @param {Number} minSize The minimum size
3832 setMinimumSize : function(minSize){
3833 this.minSize = minSize;
3837 * Gets the maximum size for the resizing element
3838 * @return {Number} The maximum size
3840 getMaximumSize : function(){
3841 return this.maxSize;
3845 * Sets the maximum size for the resizing element
3846 * @param {Number} maxSize The maximum size
3848 setMaximumSize : function(maxSize){
3849 this.maxSize = maxSize;
3853 * Sets the initialize size for the resizing element
3854 * @param {Number} size The initial size
3856 setCurrentSize : function(size){
3857 var oldAnimate = this.animate;
3858 this.animate = false;
3859 this.adapter.setElementSize(this, size);
3860 this.animate = oldAnimate;
3864 * Destroy this splitbar.
3865 * @param {Boolean} removeEl True to remove the element
3867 destroy : function(removeEl){
3872 this.proxy.parentNode.removeChild(this.proxy);
3880 * @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.
3882 Roo.SplitBar.createProxy = function(dir){
3883 var proxy = new Roo.Element(document.createElement("div"));
3884 proxy.unselectable();
3885 var cls = 'x-splitbar-proxy';
3886 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3887 document.body.appendChild(proxy.dom);
3892 * @class Roo.SplitBar.BasicLayoutAdapter
3893 * Default Adapter. It assumes the splitter and resizing element are not positioned
3894 * elements and only gets/sets the width of the element. Generally used for table based layouts.
3896 Roo.SplitBar.BasicLayoutAdapter = function(){
3899 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3900 // do nothing for now
3905 * Called before drag operations to get the current size of the resizing element.
3906 * @param {Roo.SplitBar} s The SplitBar using this adapter
3908 getElementSize : function(s){
3909 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3910 return s.resizingEl.getWidth();
3912 return s.resizingEl.getHeight();
3917 * Called after drag operations to set the size of the resizing element.
3918 * @param {Roo.SplitBar} s The SplitBar using this adapter
3919 * @param {Number} newSize The new size to set
3920 * @param {Function} onComplete A function to be invoked when resizing is complete
3922 setElementSize : function(s, newSize, onComplete){
3923 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3925 s.resizingEl.setWidth(newSize);
3927 onComplete(s, newSize);
3930 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3935 s.resizingEl.setHeight(newSize);
3937 onComplete(s, newSize);
3940 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3947 *@class Roo.SplitBar.AbsoluteLayoutAdapter
3948 * @extends Roo.SplitBar.BasicLayoutAdapter
3949 * Adapter that moves the splitter element to align with the resized sizing element.
3950 * Used with an absolute positioned SplitBar.
3951 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3952 * document.body, make sure you assign an id to the body element.
3954 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3955 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3956 this.container = Roo.get(container);
3959 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3964 getElementSize : function(s){
3965 return this.basic.getElementSize(s);
3968 setElementSize : function(s, newSize, onComplete){
3969 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3972 moveSplitter : function(s){
3973 var yes = Roo.SplitBar;
3974 switch(s.placement){
3976 s.el.setX(s.resizingEl.getRight());
3979 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3982 s.el.setY(s.resizingEl.getBottom());
3985 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3992 * Orientation constant - Create a vertical SplitBar
3996 Roo.SplitBar.VERTICAL = 1;
3999 * Orientation constant - Create a horizontal SplitBar
4003 Roo.SplitBar.HORIZONTAL = 2;
4006 * Placement constant - The resizing element is to the left of the splitter element
4010 Roo.SplitBar.LEFT = 1;
4013 * Placement constant - The resizing element is to the right of the splitter element
4017 Roo.SplitBar.RIGHT = 2;
4020 * Placement constant - The resizing element is positioned above the splitter element
4024 Roo.SplitBar.TOP = 3;
4027 * Placement constant - The resizing element is positioned under splitter element
4031 Roo.SplitBar.BOTTOM = 4;
4034 * Ext JS Library 1.1.1
4035 * Copyright(c) 2006-2007, Ext JS, LLC.
4037 * Originally Released Under LGPL - original licence link has changed is not relivant.
4040 * <script type="text/javascript">
4045 * @extends Roo.util.Observable
4046 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
4047 * This class also supports single and multi selection modes. <br>
4048 * Create a data model bound view:
4050 var store = new Roo.data.Store(...);
4052 var view = new Roo.View({
4054 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
4057 selectedClass: "ydataview-selected",
4061 // listen for node click?
4062 view.on("click", function(vw, index, node, e){
4063 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4067 dataModel.load("foobar.xml");
4069 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4071 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4072 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4074 * Note: old style constructor is still suported (container, template, config)
4078 * @param {Object} config The config object
4081 Roo.View = function(config, depreciated_tpl, depreciated_config){
4083 this.parent = false;
4085 if (typeof(depreciated_tpl) == 'undefined') {
4086 // new way.. - universal constructor.
4087 Roo.apply(this, config);
4088 this.el = Roo.get(this.el);
4091 this.el = Roo.get(config);
4092 this.tpl = depreciated_tpl;
4093 Roo.apply(this, depreciated_config);
4095 this.wrapEl = this.el.wrap().wrap();
4096 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4099 if(typeof(this.tpl) == "string"){
4100 this.tpl = new Roo.Template(this.tpl);
4102 // support xtype ctors..
4103 this.tpl = new Roo.factory(this.tpl, Roo);
4112 * @event beforeclick
4113 * Fires before a click is processed. Returns false to cancel the default action.
4114 * @param {Roo.View} this
4115 * @param {Number} index The index of the target node
4116 * @param {HTMLElement} node The target node
4117 * @param {Roo.EventObject} e The raw event object
4119 "beforeclick" : true,
4122 * Fires when a template node is clicked.
4123 * @param {Roo.View} this
4124 * @param {Number} index The index of the target node
4125 * @param {HTMLElement} node The target node
4126 * @param {Roo.EventObject} e The raw event object
4131 * Fires when a template node is double clicked.
4132 * @param {Roo.View} this
4133 * @param {Number} index The index of the target node
4134 * @param {HTMLElement} node The target node
4135 * @param {Roo.EventObject} e The raw event object
4139 * @event contextmenu
4140 * Fires when a template node is right clicked.
4141 * @param {Roo.View} this
4142 * @param {Number} index The index of the target node
4143 * @param {HTMLElement} node The target node
4144 * @param {Roo.EventObject} e The raw event object
4146 "contextmenu" : true,
4148 * @event selectionchange
4149 * Fires when the selected nodes change.
4150 * @param {Roo.View} this
4151 * @param {Array} selections Array of the selected nodes
4153 "selectionchange" : true,
4156 * @event beforeselect
4157 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4158 * @param {Roo.View} this
4159 * @param {HTMLElement} node The node to be selected
4160 * @param {Array} selections Array of currently selected nodes
4162 "beforeselect" : true,
4164 * @event preparedata
4165 * Fires on every row to render, to allow you to change the data.
4166 * @param {Roo.View} this
4167 * @param {Object} data to be rendered (change this)
4169 "preparedata" : true
4177 "click": this.onClick,
4178 "dblclick": this.onDblClick,
4179 "contextmenu": this.onContextMenu,
4183 this.selections = [];
4185 this.cmp = new Roo.CompositeElementLite([]);
4187 this.store = Roo.factory(this.store, Roo.data);
4188 this.setStore(this.store, true);
4191 if ( this.footer && this.footer.xtype) {
4193 var fctr = this.wrapEl.appendChild(document.createElement("div"));
4195 this.footer.dataSource = this.store;
4196 this.footer.container = fctr;
4197 this.footer = Roo.factory(this.footer, Roo);
4198 fctr.insertFirst(this.el);
4200 // this is a bit insane - as the paging toolbar seems to detach the el..
4201 // dom.parentNode.parentNode.parentNode
4202 // they get detached?
4206 Roo.View.superclass.constructor.call(this);
4211 Roo.extend(Roo.View, Roo.util.Observable, {
4214 * @cfg {Roo.data.Store} store Data store to load data from.
4219 * @cfg {String|Roo.Element} el The container element.
4224 * @cfg {String|Roo.Template} tpl The template used by this View
4228 * @cfg {String} dataName the named area of the template to use as the data area
4229 * Works with domtemplates roo-name="name"
4233 * @cfg {String} selectedClass The css class to add to selected nodes
4235 selectedClass : "x-view-selected",
4237 * @cfg {String} emptyText The empty text to show when nothing is loaded.
4242 * @cfg {String} text to display on mask (default Loading)
4246 * @cfg {Boolean} multiSelect Allow multiple selection
4248 multiSelect : false,
4250 * @cfg {Boolean} singleSelect Allow single selection
4252 singleSelect: false,
4255 * @cfg {Boolean} toggleSelect - selecting
4257 toggleSelect : false,
4260 * @cfg {Boolean} tickable - selecting
4265 * Returns the element this view is bound to.
4266 * @return {Roo.Element}
4275 * Refreshes the view. - called by datachanged on the store. - do not call directly.
4277 refresh : function(){
4278 //Roo.log('refresh');
4281 // if we are using something like 'domtemplate', then
4282 // the what gets used is:
4283 // t.applySubtemplate(NAME, data, wrapping data..)
4284 // the outer template then get' applied with
4285 // the store 'extra data'
4286 // and the body get's added to the
4287 // roo-name="data" node?
4288 // <span class='roo-tpl-{name}'></span> ?????
4292 this.clearSelections();
4295 var records = this.store.getRange();
4296 if(records.length < 1) {
4298 // is this valid?? = should it render a template??
4300 this.el.update(this.emptyText);
4304 if (this.dataName) {
4305 this.el.update(t.apply(this.store.meta)); //????
4306 el = this.el.child('.roo-tpl-' + this.dataName);
4309 for(var i = 0, len = records.length; i < len; i++){
4310 var data = this.prepareData(records[i].data, i, records[i]);
4311 this.fireEvent("preparedata", this, data, i, records[i]);
4313 var d = Roo.apply({}, data);
4316 Roo.apply(d, {'roo-id' : Roo.id()});
4320 Roo.each(this.parent.item, function(item){
4321 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4324 Roo.apply(d, {'roo-data-checked' : 'checked'});
4328 html[html.length] = Roo.util.Format.trim(
4330 t.applySubtemplate(this.dataName, d, this.store.meta) :
4337 el.update(html.join(""));
4338 this.nodes = el.dom.childNodes;
4339 this.updateIndexes(0);
4344 * Function to override to reformat the data that is sent to
4345 * the template for each node.
4346 * DEPRICATED - use the preparedata event handler.
4347 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4348 * a JSON object for an UpdateManager bound view).
4350 prepareData : function(data, index, record)
4352 this.fireEvent("preparedata", this, data, index, record);
4356 onUpdate : function(ds, record){
4357 // Roo.log('on update');
4358 this.clearSelections();
4359 var index = this.store.indexOf(record);
4360 var n = this.nodes[index];
4361 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4362 n.parentNode.removeChild(n);
4363 this.updateIndexes(index, index);
4369 onAdd : function(ds, records, index)
4371 //Roo.log(['on Add', ds, records, index] );
4372 this.clearSelections();
4373 if(this.nodes.length == 0){
4377 var n = this.nodes[index];
4378 for(var i = 0, len = records.length; i < len; i++){
4379 var d = this.prepareData(records[i].data, i, records[i]);
4381 this.tpl.insertBefore(n, d);
4384 this.tpl.append(this.el, d);
4387 this.updateIndexes(index);
4390 onRemove : function(ds, record, index){
4391 // Roo.log('onRemove');
4392 this.clearSelections();
4393 var el = this.dataName ?
4394 this.el.child('.roo-tpl-' + this.dataName) :
4397 el.dom.removeChild(this.nodes[index]);
4398 this.updateIndexes(index);
4402 * Refresh an individual node.
4403 * @param {Number} index
4405 refreshNode : function(index){
4406 this.onUpdate(this.store, this.store.getAt(index));
4409 updateIndexes : function(startIndex, endIndex){
4410 var ns = this.nodes;
4411 startIndex = startIndex || 0;
4412 endIndex = endIndex || ns.length - 1;
4413 for(var i = startIndex; i <= endIndex; i++){
4414 ns[i].nodeIndex = i;
4419 * Changes the data store this view uses and refresh the view.
4420 * @param {Store} store
4422 setStore : function(store, initial){
4423 if(!initial && this.store){
4424 this.store.un("datachanged", this.refresh);
4425 this.store.un("add", this.onAdd);
4426 this.store.un("remove", this.onRemove);
4427 this.store.un("update", this.onUpdate);
4428 this.store.un("clear", this.refresh);
4429 this.store.un("beforeload", this.onBeforeLoad);
4430 this.store.un("load", this.onLoad);
4431 this.store.un("loadexception", this.onLoad);
4435 store.on("datachanged", this.refresh, this);
4436 store.on("add", this.onAdd, this);
4437 store.on("remove", this.onRemove, this);
4438 store.on("update", this.onUpdate, this);
4439 store.on("clear", this.refresh, this);
4440 store.on("beforeload", this.onBeforeLoad, this);
4441 store.on("load", this.onLoad, this);
4442 store.on("loadexception", this.onLoad, this);
4450 * onbeforeLoad - masks the loading area.
4453 onBeforeLoad : function(store,opts)
4455 //Roo.log('onBeforeLoad');
4459 this.el.mask(this.mask ? this.mask : "Loading" );
4461 onLoad : function ()
4468 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4469 * @param {HTMLElement} node
4470 * @return {HTMLElement} The template node
4472 findItemFromChild : function(node){
4473 var el = this.dataName ?
4474 this.el.child('.roo-tpl-' + this.dataName,true) :
4477 if(!node || node.parentNode == el){
4480 var p = node.parentNode;
4481 while(p && p != el){
4482 if(p.parentNode == el){
4491 onClick : function(e){
4492 var item = this.findItemFromChild(e.getTarget());
4494 var index = this.indexOf(item);
4495 if(this.onItemClick(item, index, e) !== false){
4496 this.fireEvent("click", this, index, item, e);
4499 this.clearSelections();
4504 onContextMenu : function(e){
4505 var item = this.findItemFromChild(e.getTarget());
4507 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4512 onDblClick : function(e){
4513 var item = this.findItemFromChild(e.getTarget());
4515 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4519 onItemClick : function(item, index, e)
4521 if(this.fireEvent("beforeclick", this, index, item, e) === false){
4524 if (this.toggleSelect) {
4525 var m = this.isSelected(item) ? 'unselect' : 'select';
4528 _t[m](item, true, false);
4531 if(this.multiSelect || this.singleSelect){
4532 if(this.multiSelect && e.shiftKey && this.lastSelection){
4533 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4535 this.select(item, this.multiSelect && e.ctrlKey);
4536 this.lastSelection = item;
4548 * Get the number of selected nodes.
4551 getSelectionCount : function(){
4552 return this.selections.length;
4556 * Get the currently selected nodes.
4557 * @return {Array} An array of HTMLElements
4559 getSelectedNodes : function(){
4560 return this.selections;
4564 * Get the indexes of the selected nodes.
4567 getSelectedIndexes : function(){
4568 var indexes = [], s = this.selections;
4569 for(var i = 0, len = s.length; i < len; i++){
4570 indexes.push(s[i].nodeIndex);
4576 * Clear all selections
4577 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4579 clearSelections : function(suppressEvent){
4580 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4581 this.cmp.elements = this.selections;
4582 this.cmp.removeClass(this.selectedClass);
4583 this.selections = [];
4585 this.fireEvent("selectionchange", this, this.selections);
4591 * Returns true if the passed node is selected
4592 * @param {HTMLElement/Number} node The node or node index
4595 isSelected : function(node){
4596 var s = this.selections;
4600 node = this.getNode(node);
4601 return s.indexOf(node) !== -1;
4606 * @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
4607 * @param {Boolean} keepExisting (optional) true to keep existing selections
4608 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4610 select : function(nodeInfo, keepExisting, suppressEvent){
4611 if(nodeInfo instanceof Array){
4613 this.clearSelections(true);
4615 for(var i = 0, len = nodeInfo.length; i < len; i++){
4616 this.select(nodeInfo[i], true, true);
4620 var node = this.getNode(nodeInfo);
4621 if(!node || this.isSelected(node)){
4622 return; // already selected.
4625 this.clearSelections(true);
4628 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4629 Roo.fly(node).addClass(this.selectedClass);
4630 this.selections.push(node);
4632 this.fireEvent("selectionchange", this, this.selections);
4640 * @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
4641 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4642 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4644 unselect : function(nodeInfo, keepExisting, suppressEvent)
4646 if(nodeInfo instanceof Array){
4647 Roo.each(this.selections, function(s) {
4648 this.unselect(s, nodeInfo);
4652 var node = this.getNode(nodeInfo);
4653 if(!node || !this.isSelected(node)){
4654 //Roo.log("not selected");
4655 return; // not selected.
4659 Roo.each(this.selections, function(s) {
4661 Roo.fly(node).removeClass(this.selectedClass);
4668 this.selections= ns;
4669 this.fireEvent("selectionchange", this, this.selections);
4673 * Gets a template node.
4674 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4675 * @return {HTMLElement} The node or null if it wasn't found
4677 getNode : function(nodeInfo){
4678 if(typeof nodeInfo == "string"){
4679 return document.getElementById(nodeInfo);
4680 }else if(typeof nodeInfo == "number"){
4681 return this.nodes[nodeInfo];
4687 * Gets a range template nodes.
4688 * @param {Number} startIndex
4689 * @param {Number} endIndex
4690 * @return {Array} An array of nodes
4692 getNodes : function(start, end){
4693 var ns = this.nodes;
4695 end = typeof end == "undefined" ? ns.length - 1 : end;
4698 for(var i = start; i <= end; i++){
4702 for(var i = start; i >= end; i--){
4710 * Finds the index of the passed node
4711 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4712 * @return {Number} The index of the node or -1
4714 indexOf : function(node){
4715 node = this.getNode(node);
4716 if(typeof node.nodeIndex == "number"){
4717 return node.nodeIndex;
4719 var ns = this.nodes;
4720 for(var i = 0, len = ns.length; i < len; i++){
4730 * Ext JS Library 1.1.1
4731 * Copyright(c) 2006-2007, Ext JS, LLC.
4733 * Originally Released Under LGPL - original licence link has changed is not relivant.
4736 * <script type="text/javascript">
4740 * @class Roo.JsonView
4742 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4744 var view = new Roo.JsonView({
4745 container: "my-element",
4746 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
4751 // listen for node click?
4752 view.on("click", function(vw, index, node, e){
4753 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4756 // direct load of JSON data
4757 view.load("foobar.php");
4759 // Example from my blog list
4760 var tpl = new Roo.Template(
4761 '<div class="entry">' +
4762 '<a class="entry-title" href="{link}">{title}</a>' +
4763 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
4764 "</div><hr />"
4767 var moreView = new Roo.JsonView({
4768 container : "entry-list",
4772 moreView.on("beforerender", this.sortEntries, this);
4774 url: "/blog/get-posts.php",
4775 params: "allposts=true",
4776 text: "Loading Blog Entries..."
4780 * Note: old code is supported with arguments : (container, template, config)
4784 * Create a new JsonView
4786 * @param {Object} config The config object
4789 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4792 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4794 var um = this.el.getUpdateManager();
4795 um.setRenderer(this);
4796 um.on("update", this.onLoad, this);
4797 um.on("failure", this.onLoadException, this);
4800 * @event beforerender
4801 * Fires before rendering of the downloaded JSON data.
4802 * @param {Roo.JsonView} this
4803 * @param {Object} data The JSON data loaded
4807 * Fires when data is loaded.
4808 * @param {Roo.JsonView} this
4809 * @param {Object} data The JSON data loaded
4810 * @param {Object} response The raw Connect response object
4813 * @event loadexception
4814 * Fires when loading fails.
4815 * @param {Roo.JsonView} this
4816 * @param {Object} response The raw Connect response object
4819 'beforerender' : true,
4821 'loadexception' : true
4824 Roo.extend(Roo.JsonView, Roo.View, {
4826 * @type {String} The root property in the loaded JSON object that contains the data
4831 * Refreshes the view.
4833 refresh : function(){
4834 this.clearSelections();
4837 var o = this.jsonData;
4838 if(o && o.length > 0){
4839 for(var i = 0, len = o.length; i < len; i++){
4840 var data = this.prepareData(o[i], i, o);
4841 html[html.length] = this.tpl.apply(data);
4844 html.push(this.emptyText);
4846 this.el.update(html.join(""));
4847 this.nodes = this.el.dom.childNodes;
4848 this.updateIndexes(0);
4852 * 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.
4853 * @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:
4856 url: "your-url.php",
4857 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4858 callback: yourFunction,
4859 scope: yourObject, //(optional scope)
4867 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4868 * 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.
4869 * @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}
4870 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4871 * @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.
4874 var um = this.el.getUpdateManager();
4875 um.update.apply(um, arguments);
4878 // note - render is a standard framework call...
4879 // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4880 render : function(el, response){
4882 this.clearSelections();
4886 if (response != '') {
4887 o = Roo.util.JSON.decode(response.responseText);
4890 o = o[this.jsonRoot];
4896 * The current JSON data or null
4899 this.beforeRender();
4904 * Get the number of records in the current JSON dataset
4907 getCount : function(){
4908 return this.jsonData ? this.jsonData.length : 0;
4912 * Returns the JSON object for the specified node(s)
4913 * @param {HTMLElement/Array} node The node or an array of nodes
4914 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4915 * you get the JSON object for the node
4917 getNodeData : function(node){
4918 if(node instanceof Array){
4920 for(var i = 0, len = node.length; i < len; i++){
4921 data.push(this.getNodeData(node[i]));
4925 return this.jsonData[this.indexOf(node)] || null;
4928 beforeRender : function(){
4929 this.snapshot = this.jsonData;
4931 this.sort.apply(this, this.sortInfo);
4933 this.fireEvent("beforerender", this, this.jsonData);
4936 onLoad : function(el, o){
4937 this.fireEvent("load", this, this.jsonData, o);
4940 onLoadException : function(el, o){
4941 this.fireEvent("loadexception", this, o);
4945 * Filter the data by a specific property.
4946 * @param {String} property A property on your JSON objects
4947 * @param {String/RegExp} value Either string that the property values
4948 * should start with, or a RegExp to test against the property
4950 filter : function(property, value){
4953 var ss = this.snapshot;
4954 if(typeof value == "string"){
4955 var vlen = value.length;
4960 value = value.toLowerCase();
4961 for(var i = 0, len = ss.length; i < len; i++){
4963 if(o[property].substr(0, vlen).toLowerCase() == value){
4967 } else if(value.exec){ // regex?
4968 for(var i = 0, len = ss.length; i < len; i++){
4970 if(value.test(o[property])){
4977 this.jsonData = data;
4983 * Filter by a function. The passed function will be called with each
4984 * object in the current dataset. If the function returns true the value is kept,
4985 * otherwise it is filtered.
4986 * @param {Function} fn
4987 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4989 filterBy : function(fn, scope){
4992 var ss = this.snapshot;
4993 for(var i = 0, len = ss.length; i < len; i++){
4995 if(fn.call(scope || this, o)){
4999 this.jsonData = data;
5005 * Clears the current filter.
5007 clearFilter : function(){
5008 if(this.snapshot && this.jsonData != this.snapshot){
5009 this.jsonData = this.snapshot;
5016 * Sorts the data for this view and refreshes it.
5017 * @param {String} property A property on your JSON objects to sort on
5018 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5019 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5021 sort : function(property, dir, sortType){
5022 this.sortInfo = Array.prototype.slice.call(arguments, 0);
5025 var dsc = dir && dir.toLowerCase() == "desc";
5026 var f = function(o1, o2){
5027 var v1 = sortType ? sortType(o1[p]) : o1[p];
5028 var v2 = sortType ? sortType(o2[p]) : o2[p];
5031 return dsc ? +1 : -1;
5033 return dsc ? -1 : +1;
5038 this.jsonData.sort(f);
5040 if(this.jsonData != this.snapshot){
5041 this.snapshot.sort(f);
5047 * Ext JS Library 1.1.1
5048 * Copyright(c) 2006-2007, Ext JS, LLC.
5050 * Originally Released Under LGPL - original licence link has changed is not relivant.
5053 * <script type="text/javascript">
5058 * @class Roo.ColorPalette
5059 * @extends Roo.Component
5060 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
5061 * Here's an example of typical usage:
5063 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
5064 cp.render('my-div');
5066 cp.on('select', function(palette, selColor){
5067 // do something with selColor
5071 * Create a new ColorPalette
5072 * @param {Object} config The config object
5074 Roo.ColorPalette = function(config){
5075 Roo.ColorPalette.superclass.constructor.call(this, config);
5079 * Fires when a color is selected
5080 * @param {ColorPalette} this
5081 * @param {String} color The 6-digit color hex code (without the # symbol)
5087 this.on("select", this.handler, this.scope, true);
5090 Roo.extend(Roo.ColorPalette, Roo.Component, {
5092 * @cfg {String} itemCls
5093 * The CSS class to apply to the containing element (defaults to "x-color-palette")
5095 itemCls : "x-color-palette",
5097 * @cfg {String} value
5098 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
5099 * the hex codes are case-sensitive.
5104 ctype: "Roo.ColorPalette",
5107 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5109 allowReselect : false,
5112 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
5113 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
5114 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5115 * of colors with the width setting until the box is symmetrical.</p>
5116 * <p>You can override individual colors if needed:</p>
5118 var cp = new Roo.ColorPalette();
5119 cp.colors[0] = "FF0000"; // change the first box to red
5122 Or you can provide a custom array of your own for complete control:
5124 var cp = new Roo.ColorPalette();
5125 cp.colors = ["000000", "993300", "333300"];
5130 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5131 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5132 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5133 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5134 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5138 onRender : function(container, position){
5139 var t = new Roo.MasterTemplate(
5140 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
5142 var c = this.colors;
5143 for(var i = 0, len = c.length; i < len; i++){
5146 var el = document.createElement("div");
5147 el.className = this.itemCls;
5149 container.dom.insertBefore(el, position);
5150 this.el = Roo.get(el);
5151 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
5152 if(this.clickEvent != 'click'){
5153 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
5158 afterRender : function(){
5159 Roo.ColorPalette.superclass.afterRender.call(this);
5168 handleClick : function(e, t){
5171 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5172 this.select(c.toUpperCase());
5177 * Selects the specified color in the palette (fires the select event)
5178 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5180 select : function(color){
5181 color = color.replace("#", "");
5182 if(color != this.value || this.allowReselect){
5185 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5187 el.child("a.color-"+color).addClass("x-color-palette-sel");
5189 this.fireEvent("select", this, color);
5194 * Ext JS Library 1.1.1
5195 * Copyright(c) 2006-2007, Ext JS, LLC.
5197 * Originally Released Under LGPL - original licence link has changed is not relivant.
5200 * <script type="text/javascript">
5204 * @class Roo.DatePicker
5205 * @extends Roo.Component
5206 * Simple date picker class.
5208 * Create a new DatePicker
5209 * @param {Object} config The config object
5211 Roo.DatePicker = function(config){
5212 Roo.DatePicker.superclass.constructor.call(this, config);
5214 this.value = config && config.value ?
5215 config.value.clearTime() : new Date().clearTime();
5220 * Fires when a date is selected
5221 * @param {DatePicker} this
5222 * @param {Date} date The selected date
5226 * @event monthchange
5227 * Fires when the displayed month changes
5228 * @param {DatePicker} this
5229 * @param {Date} date The selected month
5235 this.on("select", this.handler, this.scope || this);
5237 // build the disabledDatesRE
5238 if(!this.disabledDatesRE && this.disabledDates){
5239 var dd = this.disabledDates;
5241 for(var i = 0; i < dd.length; i++){
5243 if(i != dd.length-1) {
5247 this.disabledDatesRE = new RegExp(re + ")");
5251 Roo.extend(Roo.DatePicker, Roo.Component, {
5253 * @cfg {String} todayText
5254 * The text to display on the button that selects the current date (defaults to "Today")
5256 todayText : "Today",
5258 * @cfg {String} okText
5259 * The text to display on the ok button
5261 okText : " OK ", //   to give the user extra clicking room
5263 * @cfg {String} cancelText
5264 * The text to display on the cancel button
5266 cancelText : "Cancel",
5268 * @cfg {String} todayTip
5269 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5271 todayTip : "{0} (Spacebar)",
5273 * @cfg {Date} minDate
5274 * Minimum allowable date (JavaScript date object, defaults to null)
5278 * @cfg {Date} maxDate
5279 * Maximum allowable date (JavaScript date object, defaults to null)
5283 * @cfg {String} minText
5284 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5286 minText : "This date is before the minimum date",
5288 * @cfg {String} maxText
5289 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5291 maxText : "This date is after the maximum date",
5293 * @cfg {String} format
5294 * The default date format string which can be overriden for localization support. The format must be
5295 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5299 * @cfg {Array} disabledDays
5300 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5302 disabledDays : null,
5304 * @cfg {String} disabledDaysText
5305 * The tooltip to display when the date falls on a disabled day (defaults to "")
5307 disabledDaysText : "",
5309 * @cfg {RegExp} disabledDatesRE
5310 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5312 disabledDatesRE : null,
5314 * @cfg {String} disabledDatesText
5315 * The tooltip text to display when the date falls on a disabled date (defaults to "")
5317 disabledDatesText : "",
5319 * @cfg {Boolean} constrainToViewport
5320 * True to constrain the date picker to the viewport (defaults to true)
5322 constrainToViewport : true,
5324 * @cfg {Array} monthNames
5325 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5327 monthNames : Date.monthNames,
5329 * @cfg {Array} dayNames
5330 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5332 dayNames : Date.dayNames,
5334 * @cfg {String} nextText
5335 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5337 nextText: 'Next Month (Control+Right)',
5339 * @cfg {String} prevText
5340 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5342 prevText: 'Previous Month (Control+Left)',
5344 * @cfg {String} monthYearText
5345 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5347 monthYearText: 'Choose a month (Control+Up/Down to move years)',
5349 * @cfg {Number} startDay
5350 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5354 * @cfg {Bool} showClear
5355 * Show a clear button (usefull for date form elements that can be blank.)
5361 * Sets the value of the date field
5362 * @param {Date} value The date to set
5364 setValue : function(value){
5365 var old = this.value;
5367 if (typeof(value) == 'string') {
5369 value = Date.parseDate(value, this.format);
5375 this.value = value.clearTime(true);
5377 this.update(this.value);
5382 * Gets the current selected value of the date field
5383 * @return {Date} The selected date
5385 getValue : function(){
5392 this.update(this.activeDate);
5397 onRender : function(container, position){
5400 '<table cellspacing="0">',
5401 '<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>',
5402 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5403 var dn = this.dayNames;
5404 for(var i = 0; i < 7; i++){
5405 var d = this.startDay+i;
5409 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5411 m[m.length] = "</tr></thead><tbody><tr>";
5412 for(var i = 0; i < 42; i++) {
5413 if(i % 7 == 0 && i != 0){
5414 m[m.length] = "</tr><tr>";
5416 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5418 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5419 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5421 var el = document.createElement("div");
5422 el.className = "x-date-picker";
5423 el.innerHTML = m.join("");
5425 container.dom.insertBefore(el, position);
5427 this.el = Roo.get(el);
5428 this.eventEl = Roo.get(el.firstChild);
5430 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5431 handler: this.showPrevMonth,
5433 preventDefault:true,
5437 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5438 handler: this.showNextMonth,
5440 preventDefault:true,
5444 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
5446 this.monthPicker = this.el.down('div.x-date-mp');
5447 this.monthPicker.enableDisplayMode('block');
5449 var kn = new Roo.KeyNav(this.eventEl, {
5450 "left" : function(e){
5452 this.showPrevMonth() :
5453 this.update(this.activeDate.add("d", -1));
5456 "right" : function(e){
5458 this.showNextMonth() :
5459 this.update(this.activeDate.add("d", 1));
5464 this.showNextYear() :
5465 this.update(this.activeDate.add("d", -7));
5468 "down" : function(e){
5470 this.showPrevYear() :
5471 this.update(this.activeDate.add("d", 7));
5474 "pageUp" : function(e){
5475 this.showNextMonth();
5478 "pageDown" : function(e){
5479 this.showPrevMonth();
5482 "enter" : function(e){
5483 e.stopPropagation();
5490 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
5492 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
5494 this.el.unselectable();
5496 this.cells = this.el.select("table.x-date-inner tbody td");
5497 this.textNodes = this.el.query("table.x-date-inner tbody span");
5499 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5501 tooltip: this.monthYearText
5504 this.mbtn.on('click', this.showMonthPicker, this);
5505 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5508 var today = (new Date()).dateFormat(this.format);
5510 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5511 if (this.showClear) {
5512 baseTb.add( new Roo.Toolbar.Fill());
5515 text: String.format(this.todayText, today),
5516 tooltip: String.format(this.todayTip, today),
5517 handler: this.selectToday,
5521 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5524 if (this.showClear) {
5526 baseTb.add( new Roo.Toolbar.Fill());
5529 cls: 'x-btn-icon x-btn-clear',
5530 handler: function() {
5532 this.fireEvent("select", this, '');
5542 this.update(this.value);
5545 createMonthPicker : function(){
5546 if(!this.monthPicker.dom.firstChild){
5547 var buf = ['<table border="0" cellspacing="0">'];
5548 for(var i = 0; i < 6; i++){
5550 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5551 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5553 '<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>' :
5554 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5558 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5560 '</button><button type="button" class="x-date-mp-cancel">',
5562 '</button></td></tr>',
5565 this.monthPicker.update(buf.join(''));
5566 this.monthPicker.on('click', this.onMonthClick, this);
5567 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5569 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5570 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5572 this.mpMonths.each(function(m, a, i){
5575 m.dom.xmonth = 5 + Math.round(i * .5);
5577 m.dom.xmonth = Math.round((i-1) * .5);
5583 showMonthPicker : function(){
5584 this.createMonthPicker();
5585 var size = this.el.getSize();
5586 this.monthPicker.setSize(size);
5587 this.monthPicker.child('table').setSize(size);
5589 this.mpSelMonth = (this.activeDate || this.value).getMonth();
5590 this.updateMPMonth(this.mpSelMonth);
5591 this.mpSelYear = (this.activeDate || this.value).getFullYear();
5592 this.updateMPYear(this.mpSelYear);
5594 this.monthPicker.slideIn('t', {duration:.2});
5597 updateMPYear : function(y){
5599 var ys = this.mpYears.elements;
5600 for(var i = 1; i <= 10; i++){
5601 var td = ys[i-1], y2;
5603 y2 = y + Math.round(i * .5);
5604 td.firstChild.innerHTML = y2;
5607 y2 = y - (5-Math.round(i * .5));
5608 td.firstChild.innerHTML = y2;
5611 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5615 updateMPMonth : function(sm){
5616 this.mpMonths.each(function(m, a, i){
5617 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5621 selectMPMonth: function(m){
5625 onMonthClick : function(e, t){
5627 var el = new Roo.Element(t), pn;
5628 if(el.is('button.x-date-mp-cancel')){
5629 this.hideMonthPicker();
5631 else if(el.is('button.x-date-mp-ok')){
5632 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5633 this.hideMonthPicker();
5635 else if(pn = el.up('td.x-date-mp-month', 2)){
5636 this.mpMonths.removeClass('x-date-mp-sel');
5637 pn.addClass('x-date-mp-sel');
5638 this.mpSelMonth = pn.dom.xmonth;
5640 else if(pn = el.up('td.x-date-mp-year', 2)){
5641 this.mpYears.removeClass('x-date-mp-sel');
5642 pn.addClass('x-date-mp-sel');
5643 this.mpSelYear = pn.dom.xyear;
5645 else if(el.is('a.x-date-mp-prev')){
5646 this.updateMPYear(this.mpyear-10);
5648 else if(el.is('a.x-date-mp-next')){
5649 this.updateMPYear(this.mpyear+10);
5653 onMonthDblClick : function(e, t){
5655 var el = new Roo.Element(t), pn;
5656 if(pn = el.up('td.x-date-mp-month', 2)){
5657 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5658 this.hideMonthPicker();
5660 else if(pn = el.up('td.x-date-mp-year', 2)){
5661 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5662 this.hideMonthPicker();
5666 hideMonthPicker : function(disableAnim){
5667 if(this.monthPicker){
5668 if(disableAnim === true){
5669 this.monthPicker.hide();
5671 this.monthPicker.slideOut('t', {duration:.2});
5677 showPrevMonth : function(e){
5678 this.update(this.activeDate.add("mo", -1));
5682 showNextMonth : function(e){
5683 this.update(this.activeDate.add("mo", 1));
5687 showPrevYear : function(){
5688 this.update(this.activeDate.add("y", -1));
5692 showNextYear : function(){
5693 this.update(this.activeDate.add("y", 1));
5697 handleMouseWheel : function(e){
5698 var delta = e.getWheelDelta();
5700 this.showPrevMonth();
5702 } else if(delta < 0){
5703 this.showNextMonth();
5709 handleDateClick : function(e, t){
5711 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5712 this.setValue(new Date(t.dateValue));
5713 this.fireEvent("select", this, this.value);
5718 selectToday : function(){
5719 this.setValue(new Date().clearTime());
5720 this.fireEvent("select", this, this.value);
5724 update : function(date)
5726 var vd = this.activeDate;
5727 this.activeDate = date;
5729 var t = date.getTime();
5730 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5731 this.cells.removeClass("x-date-selected");
5732 this.cells.each(function(c){
5733 if(c.dom.firstChild.dateValue == t){
5734 c.addClass("x-date-selected");
5735 setTimeout(function(){
5736 try{c.dom.firstChild.focus();}catch(e){}
5745 var days = date.getDaysInMonth();
5746 var firstOfMonth = date.getFirstDateOfMonth();
5747 var startingPos = firstOfMonth.getDay()-this.startDay;
5749 if(startingPos <= this.startDay){
5753 var pm = date.add("mo", -1);
5754 var prevStart = pm.getDaysInMonth()-startingPos;
5756 var cells = this.cells.elements;
5757 var textEls = this.textNodes;
5758 days += startingPos;
5760 // convert everything to numbers so it's fast
5762 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5763 var today = new Date().clearTime().getTime();
5764 var sel = date.clearTime().getTime();
5765 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5766 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5767 var ddMatch = this.disabledDatesRE;
5768 var ddText = this.disabledDatesText;
5769 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5770 var ddaysText = this.disabledDaysText;
5771 var format = this.format;
5773 var setCellClass = function(cal, cell){
5775 var t = d.getTime();
5776 cell.firstChild.dateValue = t;
5778 cell.className += " x-date-today";
5779 cell.title = cal.todayText;
5782 cell.className += " x-date-selected";
5783 setTimeout(function(){
5784 try{cell.firstChild.focus();}catch(e){}
5789 cell.className = " x-date-disabled";
5790 cell.title = cal.minText;
5794 cell.className = " x-date-disabled";
5795 cell.title = cal.maxText;
5799 if(ddays.indexOf(d.getDay()) != -1){
5800 cell.title = ddaysText;
5801 cell.className = " x-date-disabled";
5804 if(ddMatch && format){
5805 var fvalue = d.dateFormat(format);
5806 if(ddMatch.test(fvalue)){
5807 cell.title = ddText.replace("%0", fvalue);
5808 cell.className = " x-date-disabled";
5814 for(; i < startingPos; i++) {
5815 textEls[i].innerHTML = (++prevStart);
5816 d.setDate(d.getDate()+1);
5817 cells[i].className = "x-date-prevday";
5818 setCellClass(this, cells[i]);
5820 for(; i < days; i++){
5821 intDay = i - startingPos + 1;
5822 textEls[i].innerHTML = (intDay);
5823 d.setDate(d.getDate()+1);
5824 cells[i].className = "x-date-active";
5825 setCellClass(this, cells[i]);
5828 for(; i < 42; i++) {
5829 textEls[i].innerHTML = (++extraDays);
5830 d.setDate(d.getDate()+1);
5831 cells[i].className = "x-date-nextday";
5832 setCellClass(this, cells[i]);
5835 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5836 this.fireEvent('monthchange', this, date);
5838 if(!this.internalRender){
5839 var main = this.el.dom.firstChild;
5840 var w = main.offsetWidth;
5841 this.el.setWidth(w + this.el.getBorderWidth("lr"));
5842 Roo.fly(main).setWidth(w);
5843 this.internalRender = true;
5844 // opera does not respect the auto grow header center column
5845 // then, after it gets a width opera refuses to recalculate
5846 // without a second pass
5847 if(Roo.isOpera && !this.secondPass){
5848 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5849 this.secondPass = true;
5850 this.update.defer(10, this, [date]);
5858 * Ext JS Library 1.1.1
5859 * Copyright(c) 2006-2007, Ext JS, LLC.
5861 * Originally Released Under LGPL - original licence link has changed is not relivant.
5864 * <script type="text/javascript">
5867 * @class Roo.TabPanel
5868 * @extends Roo.util.Observable
5869 * A lightweight tab container.
5873 // basic tabs 1, built from existing content
5874 var tabs = new Roo.TabPanel("tabs1");
5875 tabs.addTab("script", "View Script");
5876 tabs.addTab("markup", "View Markup");
5877 tabs.activate("script");
5879 // more advanced tabs, built from javascript
5880 var jtabs = new Roo.TabPanel("jtabs");
5881 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5883 // set up the UpdateManager
5884 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5885 var updater = tab2.getUpdateManager();
5886 updater.setDefaultUrl("ajax1.htm");
5887 tab2.on('activate', updater.refresh, updater, true);
5889 // Use setUrl for Ajax loading
5890 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5891 tab3.setUrl("ajax2.htm", null, true);
5894 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5897 jtabs.activate("jtabs-1");
5900 * Create a new TabPanel.
5901 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5902 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5904 Roo.TabPanel = function(container, config){
5906 * The container element for this TabPanel.
5909 this.el = Roo.get(container, true);
5911 if(typeof config == "boolean"){
5912 this.tabPosition = config ? "bottom" : "top";
5914 Roo.apply(this, config);
5917 if(this.tabPosition == "bottom"){
5918 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5919 this.el.addClass("x-tabs-bottom");
5921 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5922 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5923 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5925 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5927 if(this.tabPosition != "bottom"){
5928 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5931 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5932 this.el.addClass("x-tabs-top");
5936 this.bodyEl.setStyle("position", "relative");
5939 this.activateDelegate = this.activate.createDelegate(this);
5944 * Fires when the active tab changes
5945 * @param {Roo.TabPanel} this
5946 * @param {Roo.TabPanelItem} activePanel The new active tab
5950 * @event beforetabchange
5951 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5952 * @param {Roo.TabPanel} this
5953 * @param {Object} e Set cancel to true on this object to cancel the tab change
5954 * @param {Roo.TabPanelItem} tab The tab being changed to
5956 "beforetabchange" : true
5959 Roo.EventManager.onWindowResize(this.onResize, this);
5960 this.cpad = this.el.getPadding("lr");
5961 this.hiddenCount = 0;
5964 // toolbar on the tabbar support...
5966 var tcfg = this.toolbar;
5967 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
5968 this.toolbar = new Roo.Toolbar(tcfg);
5970 var tbl = tcfg.container.child('table', true);
5971 tbl.setAttribute('width', '100%');
5978 Roo.TabPanel.superclass.constructor.call(this);
5981 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5983 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5985 tabPosition : "top",
5987 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5989 currentTabWidth : 0,
5991 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5995 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5999 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
6001 preferredTabWidth : 175,
6003 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
6007 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
6009 monitorResize : true,
6011 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
6016 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6017 * @param {String} id The id of the div to use <b>or create</b>
6018 * @param {String} text The text for the tab
6019 * @param {String} content (optional) Content to put in the TabPanelItem body
6020 * @param {Boolean} closable (optional) True to create a close icon on the tab
6021 * @return {Roo.TabPanelItem} The created TabPanelItem
6023 addTab : function(id, text, content, closable){
6024 var item = new Roo.TabPanelItem(this, id, text, closable);
6025 this.addTabItem(item);
6027 item.setContent(content);
6033 * Returns the {@link Roo.TabPanelItem} with the specified id/index
6034 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6035 * @return {Roo.TabPanelItem}
6037 getTab : function(id){
6038 return this.items[id];
6042 * Hides the {@link Roo.TabPanelItem} with the specified id/index
6043 * @param {String/Number} id The id or index of the TabPanelItem to hide.
6045 hideTab : function(id){
6046 var t = this.items[id];
6050 this.autoSizeTabs();
6055 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6056 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6058 unhideTab : function(id){
6059 var t = this.items[id];
6063 this.autoSizeTabs();
6068 * Adds an existing {@link Roo.TabPanelItem}.
6069 * @param {Roo.TabPanelItem} item The TabPanelItem to add
6071 addTabItem : function(item){
6072 this.items[item.id] = item;
6073 this.items.push(item);
6074 if(this.resizeTabs){
6075 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6076 this.autoSizeTabs();
6083 * Removes a {@link Roo.TabPanelItem}.
6084 * @param {String/Number} id The id or index of the TabPanelItem to remove.
6086 removeTab : function(id){
6087 var items = this.items;
6088 var tab = items[id];
6089 if(!tab) { return; }
6090 var index = items.indexOf(tab);
6091 if(this.active == tab && items.length > 1){
6092 var newTab = this.getNextAvailable(index);
6097 this.stripEl.dom.removeChild(tab.pnode.dom);
6098 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6099 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6101 items.splice(index, 1);
6102 delete this.items[tab.id];
6103 tab.fireEvent("close", tab);
6104 tab.purgeListeners();
6105 this.autoSizeTabs();
6108 getNextAvailable : function(start){
6109 var items = this.items;
6111 // look for a next tab that will slide over to
6112 // replace the one being removed
6113 while(index < items.length){
6114 var item = items[++index];
6115 if(item && !item.isHidden()){
6119 // if one isn't found select the previous tab (on the left)
6122 var item = items[--index];
6123 if(item && !item.isHidden()){
6131 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6132 * @param {String/Number} id The id or index of the TabPanelItem to disable.
6134 disableTab : function(id){
6135 var tab = this.items[id];
6136 if(tab && this.active != tab){
6142 * Enables a {@link Roo.TabPanelItem} that is disabled.
6143 * @param {String/Number} id The id or index of the TabPanelItem to enable.
6145 enableTab : function(id){
6146 var tab = this.items[id];
6151 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6152 * @param {String/Number} id The id or index of the TabPanelItem to activate.
6153 * @return {Roo.TabPanelItem} The TabPanelItem.
6155 activate : function(id){
6156 var tab = this.items[id];
6160 if(tab == this.active || tab.disabled){
6164 this.fireEvent("beforetabchange", this, e, tab);
6165 if(e.cancel !== true && !tab.disabled){
6169 this.active = this.items[id];
6171 this.fireEvent("tabchange", this, this.active);
6177 * Gets the active {@link Roo.TabPanelItem}.
6178 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6180 getActiveTab : function(){
6185 * Updates the tab body element to fit the height of the container element
6186 * for overflow scrolling
6187 * @param {Number} targetHeight (optional) Override the starting height from the elements height
6189 syncHeight : function(targetHeight){
6190 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6191 var bm = this.bodyEl.getMargins();
6192 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6193 this.bodyEl.setHeight(newHeight);
6197 onResize : function(){
6198 if(this.monitorResize){
6199 this.autoSizeTabs();
6204 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6206 beginUpdate : function(){
6207 this.updating = true;
6211 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6213 endUpdate : function(){
6214 this.updating = false;
6215 this.autoSizeTabs();
6219 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6221 autoSizeTabs : function(){
6222 var count = this.items.length;
6223 var vcount = count - this.hiddenCount;
6224 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6227 var w = Math.max(this.el.getWidth() - this.cpad, 10);
6228 var availWidth = Math.floor(w / vcount);
6229 var b = this.stripBody;
6230 if(b.getWidth() > w){
6231 var tabs = this.items;
6232 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6233 if(availWidth < this.minTabWidth){
6234 /*if(!this.sleft){ // incomplete scrolling code
6235 this.createScrollButtons();
6238 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6241 if(this.currentTabWidth < this.preferredTabWidth){
6242 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6248 * Returns the number of tabs in this TabPanel.
6251 getCount : function(){
6252 return this.items.length;
6256 * Resizes all the tabs to the passed width
6257 * @param {Number} The new width
6259 setTabWidth : function(width){
6260 this.currentTabWidth = width;
6261 for(var i = 0, len = this.items.length; i < len; i++) {
6262 if(!this.items[i].isHidden()) {
6263 this.items[i].setWidth(width);
6269 * Destroys this TabPanel
6270 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6272 destroy : function(removeEl){
6273 Roo.EventManager.removeResizeListener(this.onResize, this);
6274 for(var i = 0, len = this.items.length; i < len; i++){
6275 this.items[i].purgeListeners();
6277 if(removeEl === true){
6285 * @class Roo.TabPanelItem
6286 * @extends Roo.util.Observable
6287 * Represents an individual item (tab plus body) in a TabPanel.
6288 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6289 * @param {String} id The id of this TabPanelItem
6290 * @param {String} text The text for the tab of this TabPanelItem
6291 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6293 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6295 * The {@link Roo.TabPanel} this TabPanelItem belongs to
6296 * @type Roo.TabPanel
6298 this.tabPanel = tabPanel;
6300 * The id for this TabPanelItem
6305 this.disabled = false;
6309 this.loaded = false;
6310 this.closable = closable;
6313 * The body element for this TabPanelItem.
6316 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6317 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6318 this.bodyEl.setStyle("display", "block");
6319 this.bodyEl.setStyle("zoom", "1");
6322 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6324 this.el = Roo.get(els.el, true);
6325 this.inner = Roo.get(els.inner, true);
6326 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6327 this.pnode = Roo.get(els.el.parentNode, true);
6328 this.el.on("mousedown", this.onTabMouseDown, this);
6329 this.el.on("click", this.onTabClick, this);
6332 var c = Roo.get(els.close, true);
6333 c.dom.title = this.closeText;
6334 c.addClassOnOver("close-over");
6335 c.on("click", this.closeClick, this);
6341 * Fires when this tab becomes the active tab.
6342 * @param {Roo.TabPanel} tabPanel The parent TabPanel
6343 * @param {Roo.TabPanelItem} this
6347 * @event beforeclose
6348 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6349 * @param {Roo.TabPanelItem} this
6350 * @param {Object} e Set cancel to true on this object to cancel the close.
6352 "beforeclose": true,
6355 * Fires when this tab is closed.
6356 * @param {Roo.TabPanelItem} this
6361 * Fires when this tab is no longer the active tab.
6362 * @param {Roo.TabPanel} tabPanel The parent TabPanel
6363 * @param {Roo.TabPanelItem} this
6367 this.hidden = false;
6369 Roo.TabPanelItem.superclass.constructor.call(this);
6372 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6373 purgeListeners : function(){
6374 Roo.util.Observable.prototype.purgeListeners.call(this);
6375 this.el.removeAllListeners();
6378 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6381 this.pnode.addClass("on");
6384 this.tabPanel.stripWrap.repaint();
6386 this.fireEvent("activate", this.tabPanel, this);
6390 * Returns true if this tab is the active tab.
6393 isActive : function(){
6394 return this.tabPanel.getActiveTab() == this;
6398 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6401 this.pnode.removeClass("on");
6403 this.fireEvent("deactivate", this.tabPanel, this);
6406 hideAction : function(){
6408 this.bodyEl.setStyle("position", "absolute");
6409 this.bodyEl.setLeft("-20000px");
6410 this.bodyEl.setTop("-20000px");
6413 showAction : function(){
6414 this.bodyEl.setStyle("position", "relative");
6415 this.bodyEl.setTop("");
6416 this.bodyEl.setLeft("");
6421 * Set the tooltip for the tab.
6422 * @param {String} tooltip The tab's tooltip
6424 setTooltip : function(text){
6425 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6426 this.textEl.dom.qtip = text;
6427 this.textEl.dom.removeAttribute('title');
6429 this.textEl.dom.title = text;
6433 onTabClick : function(e){
6435 this.tabPanel.activate(this.id);
6438 onTabMouseDown : function(e){
6440 this.tabPanel.activate(this.id);
6443 getWidth : function(){
6444 return this.inner.getWidth();
6447 setWidth : function(width){
6448 var iwidth = width - this.pnode.getPadding("lr");
6449 this.inner.setWidth(iwidth);
6450 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6451 this.pnode.setWidth(width);
6455 * Show or hide the tab
6456 * @param {Boolean} hidden True to hide or false to show.
6458 setHidden : function(hidden){
6459 this.hidden = hidden;
6460 this.pnode.setStyle("display", hidden ? "none" : "");
6464 * Returns true if this tab is "hidden"
6467 isHidden : function(){
6472 * Returns the text for this tab
6475 getText : function(){
6479 autoSize : function(){
6480 //this.el.beginMeasure();
6481 this.textEl.setWidth(1);
6483 * #2804 [new] Tabs in Roojs
6484 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6486 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6487 //this.el.endMeasure();
6491 * Sets the text for the tab (Note: this also sets the tooltip text)
6492 * @param {String} text The tab's text and tooltip
6494 setText : function(text){
6496 this.textEl.update(text);
6497 this.setTooltip(text);
6498 if(!this.tabPanel.resizeTabs){
6503 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6505 activate : function(){
6506 this.tabPanel.activate(this.id);
6510 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6512 disable : function(){
6513 if(this.tabPanel.active != this){
6514 this.disabled = true;
6515 this.pnode.addClass("disabled");
6520 * Enables this TabPanelItem if it was previously disabled.
6522 enable : function(){
6523 this.disabled = false;
6524 this.pnode.removeClass("disabled");
6528 * Sets the content for this TabPanelItem.
6529 * @param {String} content The content
6530 * @param {Boolean} loadScripts true to look for and load scripts
6532 setContent : function(content, loadScripts){
6533 this.bodyEl.update(content, loadScripts);
6537 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6538 * @return {Roo.UpdateManager} The UpdateManager
6540 getUpdateManager : function(){
6541 return this.bodyEl.getUpdateManager();
6545 * Set a URL to be used to load the content for this TabPanelItem.
6546 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6547 * @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)
6548 * @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)
6549 * @return {Roo.UpdateManager} The UpdateManager
6551 setUrl : function(url, params, loadOnce){
6552 if(this.refreshDelegate){
6553 this.un('activate', this.refreshDelegate);
6555 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6556 this.on("activate", this.refreshDelegate);
6557 return this.bodyEl.getUpdateManager();
6561 _handleRefresh : function(url, params, loadOnce){
6562 if(!loadOnce || !this.loaded){
6563 var updater = this.bodyEl.getUpdateManager();
6564 updater.update(url, params, this._setLoaded.createDelegate(this));
6569 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
6570 * Will fail silently if the setUrl method has not been called.
6571 * This does not activate the panel, just updates its content.
6573 refresh : function(){
6574 if(this.refreshDelegate){
6575 this.loaded = false;
6576 this.refreshDelegate();
6581 _setLoaded : function(){
6586 closeClick : function(e){
6589 this.fireEvent("beforeclose", this, o);
6590 if(o.cancel !== true){
6591 this.tabPanel.removeTab(this.id);
6595 * The text displayed in the tooltip for the close icon.
6598 closeText : "Close this tab"
6602 Roo.TabPanel.prototype.createStrip = function(container){
6603 var strip = document.createElement("div");
6604 strip.className = "x-tabs-wrap";
6605 container.appendChild(strip);
6609 Roo.TabPanel.prototype.createStripList = function(strip){
6610 // div wrapper for retard IE
6611 // returns the "tr" element.
6612 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6613 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6614 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6615 return strip.firstChild.firstChild.firstChild.firstChild;
6618 Roo.TabPanel.prototype.createBody = function(container){
6619 var body = document.createElement("div");
6620 Roo.id(body, "tab-body");
6621 Roo.fly(body).addClass("x-tabs-body");
6622 container.appendChild(body);
6626 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6627 var body = Roo.getDom(id);
6629 body = document.createElement("div");
6632 Roo.fly(body).addClass("x-tabs-item-body");
6633 bodyEl.insertBefore(body, bodyEl.firstChild);
6637 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6638 var td = document.createElement("td");
6639 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6640 //stripEl.appendChild(td);
6642 td.className = "x-tabs-closable";
6644 this.closeTpl = new Roo.Template(
6645 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6646 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6647 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
6650 var el = this.closeTpl.overwrite(td, {"text": text});
6651 var close = el.getElementsByTagName("div")[0];
6652 var inner = el.getElementsByTagName("em")[0];
6653 return {"el": el, "close": close, "inner": inner};
6656 this.tabTpl = new Roo.Template(
6657 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6658 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6661 var el = this.tabTpl.overwrite(td, {"text": text});
6662 var inner = el.getElementsByTagName("em")[0];
6663 return {"el": el, "inner": inner};
6667 * Ext JS Library 1.1.1
6668 * Copyright(c) 2006-2007, Ext JS, LLC.
6670 * Originally Released Under LGPL - original licence link has changed is not relivant.
6673 * <script type="text/javascript">
6678 * @extends Roo.util.Observable
6679 * Simple Button class
6680 * @cfg {String} text The button text
6681 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6682 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6683 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6684 * @cfg {Object} scope The scope of the handler
6685 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6686 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6687 * @cfg {Boolean} hidden True to start hidden (defaults to false)
6688 * @cfg {Boolean} disabled True to start disabled (defaults to false)
6689 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6690 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6691 applies if enableToggle = true)
6692 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6693 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6694 an {@link Roo.util.ClickRepeater} config object (defaults to false).
6696 * Create a new button
6697 * @param {Object} config The config object
6699 Roo.Button = function(renderTo, config)
6703 renderTo = config.renderTo || false;
6706 Roo.apply(this, config);
6710 * Fires when this button is clicked
6711 * @param {Button} this
6712 * @param {EventObject} e The click event
6717 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6718 * @param {Button} this
6719 * @param {Boolean} pressed
6724 * Fires when the mouse hovers over the button
6725 * @param {Button} this
6726 * @param {Event} e The event object
6731 * Fires when the mouse exits the button
6732 * @param {Button} this
6733 * @param {Event} e The event object
6738 * Fires when the button is rendered
6739 * @param {Button} this
6744 this.menu = Roo.menu.MenuMgr.get(this.menu);
6746 // register listeners first!! - so render can be captured..
6747 Roo.util.Observable.call(this);
6749 this.render(renderTo);
6755 Roo.extend(Roo.Button, Roo.util.Observable, {
6761 * Read-only. True if this button is hidden
6766 * Read-only. True if this button is disabled
6771 * Read-only. True if this button is pressed (only if enableToggle = true)
6777 * @cfg {Number} tabIndex
6778 * The DOM tabIndex for this button (defaults to undefined)
6780 tabIndex : undefined,
6783 * @cfg {Boolean} enableToggle
6784 * True to enable pressed/not pressed toggling (defaults to false)
6786 enableToggle: false,
6789 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6793 * @cfg {String} menuAlign
6794 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6796 menuAlign : "tl-bl?",
6799 * @cfg {String} iconCls
6800 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6802 iconCls : undefined,
6804 * @cfg {String} type
6805 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
6810 menuClassTarget: 'tr',
6813 * @cfg {String} clickEvent
6814 * The type of event to map to the button's event handler (defaults to 'click')
6816 clickEvent : 'click',
6819 * @cfg {Boolean} handleMouseEvents
6820 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6822 handleMouseEvents : true,
6825 * @cfg {String} tooltipType
6826 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6828 tooltipType : 'qtip',
6832 * A CSS class to apply to the button's main element.
6836 * @cfg {Roo.Template} template (Optional)
6837 * An {@link Roo.Template} with which to create the Button's main element. This Template must
6838 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6839 * require code modifications if required elements (e.g. a button) aren't present.
6843 render : function(renderTo){
6845 if(this.hideParent){
6846 this.parentEl = Roo.get(renderTo);
6850 if(!Roo.Button.buttonTemplate){
6851 // hideous table template
6852 Roo.Button.buttonTemplate = new Roo.Template(
6853 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6854 '<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>',
6855 "</tr></tbody></table>");
6857 this.template = Roo.Button.buttonTemplate;
6859 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
6860 var btnEl = btn.child("button:first");
6861 btnEl.on('focus', this.onFocus, this);
6862 btnEl.on('blur', this.onBlur, this);
6864 btn.addClass(this.cls);
6867 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6870 btnEl.addClass(this.iconCls);
6872 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6875 if(this.tabIndex !== undefined){
6876 btnEl.dom.tabIndex = this.tabIndex;
6879 if(typeof this.tooltip == 'object'){
6880 Roo.QuickTips.tips(Roo.apply({
6884 btnEl.dom[this.tooltipType] = this.tooltip;
6888 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6892 this.el.dom.id = this.el.id = this.id;
6895 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6896 this.menu.on("show", this.onMenuShow, this);
6897 this.menu.on("hide", this.onMenuHide, this);
6899 btn.addClass("x-btn");
6900 if(Roo.isIE && !Roo.isIE7){
6901 this.autoWidth.defer(1, this);
6905 if(this.handleMouseEvents){
6906 btn.on("mouseover", this.onMouseOver, this);
6907 btn.on("mouseout", this.onMouseOut, this);
6908 btn.on("mousedown", this.onMouseDown, this);
6910 btn.on(this.clickEvent, this.onClick, this);
6911 //btn.on("mouseup", this.onMouseUp, this);
6918 Roo.ButtonToggleMgr.register(this);
6920 this.el.addClass("x-btn-pressed");
6923 var repeater = new Roo.util.ClickRepeater(btn,
6924 typeof this.repeat == "object" ? this.repeat : {}
6926 repeater.on("click", this.onClick, this);
6929 this.fireEvent('render', this);
6933 * Returns the button's underlying element
6934 * @return {Roo.Element} The element
6941 * Destroys this Button and removes any listeners.
6943 destroy : function(){
6944 Roo.ButtonToggleMgr.unregister(this);
6945 this.el.removeAllListeners();
6946 this.purgeListeners();
6951 autoWidth : function(){
6953 this.el.setWidth("auto");
6954 if(Roo.isIE7 && Roo.isStrict){
6955 var ib = this.el.child('button');
6956 if(ib && ib.getWidth() > 20){
6958 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6963 this.el.beginMeasure();
6965 if(this.el.getWidth() < this.minWidth){
6966 this.el.setWidth(this.minWidth);
6969 this.el.endMeasure();
6976 * Assigns this button's click handler
6977 * @param {Function} handler The function to call when the button is clicked
6978 * @param {Object} scope (optional) Scope for the function passed in
6980 setHandler : function(handler, scope){
6981 this.handler = handler;
6986 * Sets this button's text
6987 * @param {String} text The button text
6989 setText : function(text){
6992 this.el.child("td.x-btn-center button.x-btn-text").update(text);
6998 * Gets the text for this button
6999 * @return {String} The button text
7001 getText : function(){
7009 this.hidden = false;
7011 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7021 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7026 * Convenience function for boolean show/hide
7027 * @param {Boolean} visible True to show, false to hide
7029 setVisible: function(visible){
7038 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7039 * @param {Boolean} state (optional) Force a particular state
7041 toggle : function(state){
7042 state = state === undefined ? !this.pressed : state;
7043 if(state != this.pressed){
7045 this.el.addClass("x-btn-pressed");
7046 this.pressed = true;
7047 this.fireEvent("toggle", this, true);
7049 this.el.removeClass("x-btn-pressed");
7050 this.pressed = false;
7051 this.fireEvent("toggle", this, false);
7053 if(this.toggleHandler){
7054 this.toggleHandler.call(this.scope || this, this, state);
7063 this.el.child('button:first').focus();
7067 * Disable this button
7069 disable : function(){
7071 this.el.addClass("x-btn-disabled");
7073 this.disabled = true;
7077 * Enable this button
7079 enable : function(){
7081 this.el.removeClass("x-btn-disabled");
7083 this.disabled = false;
7087 * Convenience function for boolean enable/disable
7088 * @param {Boolean} enabled True to enable, false to disable
7090 setDisabled : function(v){
7091 this[v !== true ? "enable" : "disable"]();
7095 onClick : function(e)
7104 if(this.enableToggle){
7107 if(this.menu && !this.menu.isVisible()){
7108 this.menu.show(this.el, this.menuAlign);
7110 this.fireEvent("click", this, e);
7112 this.el.removeClass("x-btn-over");
7113 this.handler.call(this.scope || this, this, e);
7118 onMouseOver : function(e){
7120 this.el.addClass("x-btn-over");
7121 this.fireEvent('mouseover', this, e);
7125 onMouseOut : function(e){
7126 if(!e.within(this.el, true)){
7127 this.el.removeClass("x-btn-over");
7128 this.fireEvent('mouseout', this, e);
7132 onFocus : function(e){
7134 this.el.addClass("x-btn-focus");
7138 onBlur : function(e){
7139 this.el.removeClass("x-btn-focus");
7142 onMouseDown : function(e){
7143 if(!this.disabled && e.button == 0){
7144 this.el.addClass("x-btn-click");
7145 Roo.get(document).on('mouseup', this.onMouseUp, this);
7149 onMouseUp : function(e){
7151 this.el.removeClass("x-btn-click");
7152 Roo.get(document).un('mouseup', this.onMouseUp, this);
7156 onMenuShow : function(e){
7157 this.el.addClass("x-btn-menu-active");
7160 onMenuHide : function(e){
7161 this.el.removeClass("x-btn-menu-active");
7165 // Private utility class used by Button
7166 Roo.ButtonToggleMgr = function(){
7169 function toggleGroup(btn, state){
7171 var g = groups[btn.toggleGroup];
7172 for(var i = 0, l = g.length; i < l; i++){
7181 register : function(btn){
7182 if(!btn.toggleGroup){
7185 var g = groups[btn.toggleGroup];
7187 g = groups[btn.toggleGroup] = [];
7190 btn.on("toggle", toggleGroup);
7193 unregister : function(btn){
7194 if(!btn.toggleGroup){
7197 var g = groups[btn.toggleGroup];
7200 btn.un("toggle", toggleGroup);
7206 * Ext JS Library 1.1.1
7207 * Copyright(c) 2006-2007, Ext JS, LLC.
7209 * Originally Released Under LGPL - original licence link has changed is not relivant.
7212 * <script type="text/javascript">
7216 * @class Roo.SplitButton
7217 * @extends Roo.Button
7218 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7219 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
7220 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7221 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7222 * @cfg {String} arrowTooltip The title attribute of the arrow
7224 * Create a new menu button
7225 * @param {String/HTMLElement/Element} renderTo The element to append the button to
7226 * @param {Object} config The config object
7228 Roo.SplitButton = function(renderTo, config){
7229 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7232 * Fires when this button's arrow is clicked
7233 * @param {SplitButton} this
7234 * @param {EventObject} e The click event
7236 this.addEvents({"arrowclick":true});
7239 Roo.extend(Roo.SplitButton, Roo.Button, {
7240 render : function(renderTo){
7241 // this is one sweet looking template!
7242 var tpl = new Roo.Template(
7243 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7244 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7245 '<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>',
7246 "</tbody></table></td><td>",
7247 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7248 '<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>',
7249 "</tbody></table></td></tr></table>"
7251 var btn = tpl.append(renderTo, [this.text, this.type], true);
7252 var btnEl = btn.child("button");
7254 btn.addClass(this.cls);
7257 btnEl.setStyle('background-image', 'url(' +this.icon +')');
7260 btnEl.addClass(this.iconCls);
7262 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7266 if(this.handleMouseEvents){
7267 btn.on("mouseover", this.onMouseOver, this);
7268 btn.on("mouseout", this.onMouseOut, this);
7269 btn.on("mousedown", this.onMouseDown, this);
7270 btn.on("mouseup", this.onMouseUp, this);
7272 btn.on(this.clickEvent, this.onClick, this);
7274 if(typeof this.tooltip == 'object'){
7275 Roo.QuickTips.tips(Roo.apply({
7279 btnEl.dom[this.tooltipType] = this.tooltip;
7282 if(this.arrowTooltip){
7283 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7292 this.el.addClass("x-btn-pressed");
7294 if(Roo.isIE && !Roo.isIE7){
7295 this.autoWidth.defer(1, this);
7300 this.menu.on("show", this.onMenuShow, this);
7301 this.menu.on("hide", this.onMenuHide, this);
7303 this.fireEvent('render', this);
7307 autoWidth : function(){
7309 var tbl = this.el.child("table:first");
7310 var tbl2 = this.el.child("table:last");
7311 this.el.setWidth("auto");
7312 tbl.setWidth("auto");
7313 if(Roo.isIE7 && Roo.isStrict){
7314 var ib = this.el.child('button:first');
7315 if(ib && ib.getWidth() > 20){
7317 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7322 this.el.beginMeasure();
7324 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7325 tbl.setWidth(this.minWidth-tbl2.getWidth());
7328 this.el.endMeasure();
7331 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7335 * Sets this button's click handler
7336 * @param {Function} handler The function to call when the button is clicked
7337 * @param {Object} scope (optional) Scope for the function passed above
7339 setHandler : function(handler, scope){
7340 this.handler = handler;
7345 * Sets this button's arrow click handler
7346 * @param {Function} handler The function to call when the arrow is clicked
7347 * @param {Object} scope (optional) Scope for the function passed above
7349 setArrowHandler : function(handler, scope){
7350 this.arrowHandler = handler;
7359 this.el.child("button:first").focus();
7364 onClick : function(e){
7367 if(e.getTarget(".x-btn-menu-arrow-wrap")){
7368 if(this.menu && !this.menu.isVisible()){
7369 this.menu.show(this.el, this.menuAlign);
7371 this.fireEvent("arrowclick", this, e);
7372 if(this.arrowHandler){
7373 this.arrowHandler.call(this.scope || this, this, e);
7376 this.fireEvent("click", this, e);
7378 this.handler.call(this.scope || this, this, e);
7384 onMouseDown : function(e){
7386 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7390 onMouseUp : function(e){
7391 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7397 Roo.MenuButton = Roo.SplitButton;/*
7399 * Ext JS Library 1.1.1
7400 * Copyright(c) 2006-2007, Ext JS, LLC.
7402 * Originally Released Under LGPL - original licence link has changed is not relivant.
7405 * <script type="text/javascript">
7409 * @class Roo.Toolbar
7410 * Basic Toolbar class.
7412 * Creates a new Toolbar
7413 * @param {Object} container The config object
7415 Roo.Toolbar = function(container, buttons, config)
7417 /// old consturctor format still supported..
7418 if(container instanceof Array){ // omit the container for later rendering
7419 buttons = container;
7423 if (typeof(container) == 'object' && container.xtype) {
7425 container = config.container;
7426 buttons = config.buttons || []; // not really - use items!!
7429 if (config && config.items) {
7430 xitems = config.items;
7431 delete config.items;
7433 Roo.apply(this, config);
7434 this.buttons = buttons;
7437 this.render(container);
7439 this.xitems = xitems;
7440 Roo.each(xitems, function(b) {
7446 Roo.Toolbar.prototype = {
7448 * @cfg {Array} items
7449 * array of button configs or elements to add (will be converted to a MixedCollection)
7453 * @cfg {String/HTMLElement/Element} container
7454 * The id or element that will contain the toolbar
7457 render : function(ct){
7458 this.el = Roo.get(ct);
7460 this.el.addClass(this.cls);
7462 // using a table allows for vertical alignment
7463 // 100% width is needed by Safari...
7464 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7465 this.tr = this.el.child("tr", true);
7467 this.items = new Roo.util.MixedCollection(false, function(o){
7468 return o.id || ("item" + (++autoId));
7471 this.add.apply(this, this.buttons);
7472 delete this.buttons;
7477 * Adds element(s) to the toolbar -- this function takes a variable number of
7478 * arguments of mixed type and adds them to the toolbar.
7479 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7481 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7482 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7483 * <li>Field: Any form field (equivalent to {@link #addField})</li>
7484 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7485 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7486 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7487 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7488 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7489 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7491 * @param {Mixed} arg2
7492 * @param {Mixed} etc.
7495 var a = arguments, l = a.length;
7496 for(var i = 0; i < l; i++){
7501 _add : function(el) {
7504 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7507 if (el.applyTo){ // some kind of form field
7508 return this.addField(el);
7510 if (el.render){ // some kind of Toolbar.Item
7511 return this.addItem(el);
7513 if (typeof el == "string"){ // string
7514 if(el == "separator" || el == "-"){
7515 return this.addSeparator();
7518 return this.addSpacer();
7521 return this.addFill();
7523 return this.addText(el);
7526 if(el.tagName){ // element
7527 return this.addElement(el);
7529 if(typeof el == "object"){ // must be button config?
7530 return this.addButton(el);
7538 * Add an Xtype element
7539 * @param {Object} xtype Xtype Object
7540 * @return {Object} created Object
7542 addxtype : function(e){
7547 * Returns the Element for this toolbar.
7548 * @return {Roo.Element}
7556 * @return {Roo.Toolbar.Item} The separator item
7558 addSeparator : function(){
7559 return this.addItem(new Roo.Toolbar.Separator());
7563 * Adds a spacer element
7564 * @return {Roo.Toolbar.Spacer} The spacer item
7566 addSpacer : function(){
7567 return this.addItem(new Roo.Toolbar.Spacer());
7571 * Adds a fill element that forces subsequent additions to the right side of the toolbar
7572 * @return {Roo.Toolbar.Fill} The fill item
7574 addFill : function(){
7575 return this.addItem(new Roo.Toolbar.Fill());
7579 * Adds any standard HTML element to the toolbar
7580 * @param {String/HTMLElement/Element} el The element or id of the element to add
7581 * @return {Roo.Toolbar.Item} The element's item
7583 addElement : function(el){
7584 return this.addItem(new Roo.Toolbar.Item(el));
7587 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7588 * @type Roo.util.MixedCollection
7593 * Adds any Toolbar.Item or subclass
7594 * @param {Roo.Toolbar.Item} item
7595 * @return {Roo.Toolbar.Item} The item
7597 addItem : function(item){
7598 var td = this.nextBlock();
7600 this.items.add(item);
7605 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7606 * @param {Object/Array} config A button config or array of configs
7607 * @return {Roo.Toolbar.Button/Array}
7609 addButton : function(config){
7610 if(config instanceof Array){
7612 for(var i = 0, len = config.length; i < len; i++) {
7613 buttons.push(this.addButton(config[i]));
7618 if(!(config instanceof Roo.Toolbar.Button)){
7620 new Roo.Toolbar.SplitButton(config) :
7621 new Roo.Toolbar.Button(config);
7623 var td = this.nextBlock();
7630 * Adds text to the toolbar
7631 * @param {String} text The text to add
7632 * @return {Roo.Toolbar.Item} The element's item
7634 addText : function(text){
7635 return this.addItem(new Roo.Toolbar.TextItem(text));
7639 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7640 * @param {Number} index The index where the item is to be inserted
7641 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7642 * @return {Roo.Toolbar.Button/Item}
7644 insertButton : function(index, item){
7645 if(item instanceof Array){
7647 for(var i = 0, len = item.length; i < len; i++) {
7648 buttons.push(this.insertButton(index + i, item[i]));
7652 if (!(item instanceof Roo.Toolbar.Button)){
7653 item = new Roo.Toolbar.Button(item);
7655 var td = document.createElement("td");
7656 this.tr.insertBefore(td, this.tr.childNodes[index]);
7658 this.items.insert(index, item);
7663 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7664 * @param {Object} config
7665 * @return {Roo.Toolbar.Item} The element's item
7667 addDom : function(config, returnEl){
7668 var td = this.nextBlock();
7669 Roo.DomHelper.overwrite(td, config);
7670 var ti = new Roo.Toolbar.Item(td.firstChild);
7677 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7678 * @type Roo.util.MixedCollection
7683 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7684 * Note: the field should not have been rendered yet. For a field that has already been
7685 * rendered, use {@link #addElement}.
7686 * @param {Roo.form.Field} field
7687 * @return {Roo.ToolbarItem}
7691 addField : function(field) {
7694 this.fields = new Roo.util.MixedCollection(false, function(o){
7695 return o.id || ("item" + (++autoId));
7700 var td = this.nextBlock();
7702 var ti = new Roo.Toolbar.Item(td.firstChild);
7705 this.fields.add(field);
7716 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7717 this.el.child('div').hide();
7725 this.el.child('div').show();
7729 nextBlock : function(){
7730 var td = document.createElement("td");
7731 this.tr.appendChild(td);
7736 destroy : function(){
7737 if(this.items){ // rendered?
7738 Roo.destroy.apply(Roo, this.items.items);
7740 if(this.fields){ // rendered?
7741 Roo.destroy.apply(Roo, this.fields.items);
7743 Roo.Element.uncache(this.el, this.tr);
7748 * @class Roo.Toolbar.Item
7749 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7751 * Creates a new Item
7752 * @param {HTMLElement} el
7754 Roo.Toolbar.Item = function(el){
7756 if (typeof (el.xtype) != 'undefined') {
7761 this.el = Roo.getDom(el);
7762 this.id = Roo.id(this.el);
7763 this.hidden = false;
7768 * Fires when the button is rendered
7769 * @param {Button} this
7773 Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7775 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7776 //Roo.Toolbar.Item.prototype = {
7779 * Get this item's HTML Element
7780 * @return {HTMLElement}
7787 render : function(td){
7790 td.appendChild(this.el);
7792 this.fireEvent('render', this);
7796 * Removes and destroys this item.
7798 destroy : function(){
7799 this.td.parentNode.removeChild(this.td);
7806 this.hidden = false;
7807 this.td.style.display = "";
7815 this.td.style.display = "none";
7819 * Convenience function for boolean show/hide.
7820 * @param {Boolean} visible true to show/false to hide
7822 setVisible: function(visible){
7831 * Try to focus this item.
7834 Roo.fly(this.el).focus();
7838 * Disables this item.
7840 disable : function(){
7841 Roo.fly(this.td).addClass("x-item-disabled");
7842 this.disabled = true;
7843 this.el.disabled = true;
7847 * Enables this item.
7849 enable : function(){
7850 Roo.fly(this.td).removeClass("x-item-disabled");
7851 this.disabled = false;
7852 this.el.disabled = false;
7858 * @class Roo.Toolbar.Separator
7859 * @extends Roo.Toolbar.Item
7860 * A simple toolbar separator class
7862 * Creates a new Separator
7864 Roo.Toolbar.Separator = function(cfg){
7866 var s = document.createElement("span");
7867 s.className = "ytb-sep";
7872 Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7874 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7876 disable:Roo.emptyFn,
7881 * @class Roo.Toolbar.Spacer
7882 * @extends Roo.Toolbar.Item
7883 * A simple element that adds extra horizontal space to a toolbar.
7885 * Creates a new Spacer
7887 Roo.Toolbar.Spacer = function(cfg){
7888 var s = document.createElement("div");
7889 s.className = "ytb-spacer";
7893 Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7895 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7897 disable:Roo.emptyFn,
7902 * @class Roo.Toolbar.Fill
7903 * @extends Roo.Toolbar.Spacer
7904 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7906 * Creates a new Spacer
7908 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7910 render : function(td){
7911 td.style.width = '100%';
7912 Roo.Toolbar.Fill.superclass.render.call(this, td);
7917 * @class Roo.Toolbar.TextItem
7918 * @extends Roo.Toolbar.Item
7919 * A simple class that renders text directly into a toolbar.
7921 * Creates a new TextItem
7922 * @param {String} text
7924 Roo.Toolbar.TextItem = function(cfg){
7925 var text = cfg || "";
7926 if (typeof(cfg) == 'object') {
7927 text = cfg.text || "";
7931 var s = document.createElement("span");
7932 s.className = "ytb-text";
7938 Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg || s);
7940 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7944 disable:Roo.emptyFn,
7949 * @class Roo.Toolbar.Button
7950 * @extends Roo.Button
7951 * A button that renders into a toolbar.
7953 * Creates a new Button
7954 * @param {Object} config A standard {@link Roo.Button} config object
7956 Roo.Toolbar.Button = function(config){
7957 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7959 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7960 render : function(td){
7962 Roo.Toolbar.Button.superclass.render.call(this, td);
7966 * Removes and destroys this button
7968 destroy : function(){
7969 Roo.Toolbar.Button.superclass.destroy.call(this);
7970 this.td.parentNode.removeChild(this.td);
7977 this.hidden = false;
7978 this.td.style.display = "";
7986 this.td.style.display = "none";
7990 * Disables this item
7992 disable : function(){
7993 Roo.fly(this.td).addClass("x-item-disabled");
7994 this.disabled = true;
8000 enable : function(){
8001 Roo.fly(this.td).removeClass("x-item-disabled");
8002 this.disabled = false;
8006 Roo.ToolbarButton = Roo.Toolbar.Button;
8009 * @class Roo.Toolbar.SplitButton
8010 * @extends Roo.SplitButton
8011 * A menu button that renders into a toolbar.
8013 * Creates a new SplitButton
8014 * @param {Object} config A standard {@link Roo.SplitButton} config object
8016 Roo.Toolbar.SplitButton = function(config){
8017 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8019 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8020 render : function(td){
8022 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8026 * Removes and destroys this button
8028 destroy : function(){
8029 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8030 this.td.parentNode.removeChild(this.td);
8037 this.hidden = false;
8038 this.td.style.display = "";
8046 this.td.style.display = "none";
8051 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8053 * Ext JS Library 1.1.1
8054 * Copyright(c) 2006-2007, Ext JS, LLC.
8056 * Originally Released Under LGPL - original licence link has changed is not relivant.
8059 * <script type="text/javascript">
8063 * @class Roo.PagingToolbar
8064 * @extends Roo.Toolbar
8065 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8067 * Create a new PagingToolbar
8068 * @param {Object} config The config object
8070 Roo.PagingToolbar = function(el, ds, config)
8072 // old args format still supported... - xtype is prefered..
8073 if (typeof(el) == 'object' && el.xtype) {
8074 // created from xtype...
8077 el = config.container;
8081 items = config.items;
8085 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8088 this.renderButtons(this.el);
8091 // supprot items array.
8093 Roo.each(items, function(e) {
8094 this.add(Roo.factory(e));
8099 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8101 * @cfg {Roo.data.Store} dataSource
8102 * The underlying data store providing the paged data
8105 * @cfg {String/HTMLElement/Element} container
8106 * container The id or element that will contain the toolbar
8109 * @cfg {Boolean} displayInfo
8110 * True to display the displayMsg (defaults to false)
8113 * @cfg {Number} pageSize
8114 * The number of records to display per page (defaults to 20)
8118 * @cfg {String} displayMsg
8119 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8121 displayMsg : 'Displaying {0} - {1} of {2}',
8123 * @cfg {String} emptyMsg
8124 * The message to display when no records are found (defaults to "No data to display")
8126 emptyMsg : 'No data to display',
8128 * Customizable piece of the default paging text (defaults to "Page")
8131 beforePageText : "Page",
8133 * Customizable piece of the default paging text (defaults to "of %0")
8136 afterPageText : "of {0}",
8138 * Customizable piece of the default paging text (defaults to "First Page")
8141 firstText : "First Page",
8143 * Customizable piece of the default paging text (defaults to "Previous Page")
8146 prevText : "Previous Page",
8148 * Customizable piece of the default paging text (defaults to "Next Page")
8151 nextText : "Next Page",
8153 * Customizable piece of the default paging text (defaults to "Last Page")
8156 lastText : "Last Page",
8158 * Customizable piece of the default paging text (defaults to "Refresh")
8161 refreshText : "Refresh",
8164 renderButtons : function(el){
8165 Roo.PagingToolbar.superclass.render.call(this, el);
8166 this.first = this.addButton({
8167 tooltip: this.firstText,
8168 cls: "x-btn-icon x-grid-page-first",
8170 handler: this.onClick.createDelegate(this, ["first"])
8172 this.prev = this.addButton({
8173 tooltip: this.prevText,
8174 cls: "x-btn-icon x-grid-page-prev",
8176 handler: this.onClick.createDelegate(this, ["prev"])
8178 //this.addSeparator();
8179 this.add(this.beforePageText);
8180 this.field = Roo.get(this.addDom({
8185 cls: "x-grid-page-number"
8187 this.field.on("keydown", this.onPagingKeydown, this);
8188 this.field.on("focus", function(){this.dom.select();});
8189 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8190 this.field.setHeight(18);
8191 //this.addSeparator();
8192 this.next = this.addButton({
8193 tooltip: this.nextText,
8194 cls: "x-btn-icon x-grid-page-next",
8196 handler: this.onClick.createDelegate(this, ["next"])
8198 this.last = this.addButton({
8199 tooltip: this.lastText,
8200 cls: "x-btn-icon x-grid-page-last",
8202 handler: this.onClick.createDelegate(this, ["last"])
8204 //this.addSeparator();
8205 this.loading = this.addButton({
8206 tooltip: this.refreshText,
8207 cls: "x-btn-icon x-grid-loading",
8208 handler: this.onClick.createDelegate(this, ["refresh"])
8211 if(this.displayInfo){
8212 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8217 updateInfo : function(){
8219 var count = this.ds.getCount();
8220 var msg = count == 0 ?
8224 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
8226 this.displayEl.update(msg);
8231 onLoad : function(ds, r, o){
8232 this.cursor = o.params ? o.params.start : 0;
8233 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8235 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8236 this.field.dom.value = ap;
8237 this.first.setDisabled(ap == 1);
8238 this.prev.setDisabled(ap == 1);
8239 this.next.setDisabled(ap == ps);
8240 this.last.setDisabled(ap == ps);
8241 this.loading.enable();
8246 getPageData : function(){
8247 var total = this.ds.getTotalCount();
8250 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8251 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8256 onLoadError : function(){
8257 this.loading.enable();
8261 onPagingKeydown : function(e){
8263 var d = this.getPageData();
8265 var v = this.field.dom.value, pageNum;
8266 if(!v || isNaN(pageNum = parseInt(v, 10))){
8267 this.field.dom.value = d.activePage;
8270 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8271 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8274 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))
8276 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8277 this.field.dom.value = pageNum;
8278 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8281 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8283 var v = this.field.dom.value, pageNum;
8284 var increment = (e.shiftKey) ? 10 : 1;
8285 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8288 if(!v || isNaN(pageNum = parseInt(v, 10))) {
8289 this.field.dom.value = d.activePage;
8292 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8294 this.field.dom.value = parseInt(v, 10) + increment;
8295 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8296 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8303 beforeLoad : function(){
8305 this.loading.disable();
8310 onClick : function(which){
8314 ds.load({params:{start: 0, limit: this.pageSize}});
8317 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8320 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8323 var total = ds.getTotalCount();
8324 var extra = total % this.pageSize;
8325 var lastStart = extra ? (total - extra) : total-this.pageSize;
8326 ds.load({params:{start: lastStart, limit: this.pageSize}});
8329 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8335 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8336 * @param {Roo.data.Store} store The data store to unbind
8338 unbind : function(ds){
8339 ds.un("beforeload", this.beforeLoad, this);
8340 ds.un("load", this.onLoad, this);
8341 ds.un("loadexception", this.onLoadError, this);
8342 ds.un("remove", this.updateInfo, this);
8343 ds.un("add", this.updateInfo, this);
8344 this.ds = undefined;
8348 * Binds the paging toolbar to the specified {@link Roo.data.Store}
8349 * @param {Roo.data.Store} store The data store to bind
8351 bind : function(ds){
8352 ds.on("beforeload", this.beforeLoad, this);
8353 ds.on("load", this.onLoad, this);
8354 ds.on("loadexception", this.onLoadError, this);
8355 ds.on("remove", this.updateInfo, this);
8356 ds.on("add", this.updateInfo, this);
8361 * Ext JS Library 1.1.1
8362 * Copyright(c) 2006-2007, Ext JS, LLC.
8364 * Originally Released Under LGPL - original licence link has changed is not relivant.
8367 * <script type="text/javascript">
8371 * @class Roo.Resizable
8372 * @extends Roo.util.Observable
8373 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8374 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8375 * 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
8376 * the element will be wrapped for you automatically.</p>
8377 * <p>Here is the list of valid resize handles:</p>
8380 ------ -------------------
8389 'hd' horizontal drag
8392 * <p>Here's an example showing the creation of a typical Resizable:</p>
8394 var resizer = new Roo.Resizable("element-id", {
8402 resizer.on("resize", myHandler);
8404 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8405 * resizer.east.setDisplayed(false);</p>
8406 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8407 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8408 * resize operation's new size (defaults to [0, 0])
8409 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8410 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8411 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8412 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8413 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8414 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8415 * @cfg {Number} width The width of the element in pixels (defaults to null)
8416 * @cfg {Number} height The height of the element in pixels (defaults to null)
8417 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8418 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8419 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8420 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8421 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
8422 * in favor of the handles config option (defaults to false)
8423 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8424 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8425 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8426 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8427 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8428 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8429 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8430 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8431 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8432 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8433 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8435 * Create a new resizable component
8436 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8437 * @param {Object} config configuration options
8439 Roo.Resizable = function(el, config)
8441 this.el = Roo.get(el);
8443 if(config && config.wrap){
8444 config.resizeChild = this.el;
8445 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8446 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8447 this.el.setStyle("overflow", "hidden");
8448 this.el.setPositioning(config.resizeChild.getPositioning());
8449 config.resizeChild.clearPositioning();
8450 if(!config.width || !config.height){
8451 var csize = config.resizeChild.getSize();
8452 this.el.setSize(csize.width, csize.height);
8454 if(config.pinned && !config.adjustments){
8455 config.adjustments = "auto";
8459 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8460 this.proxy.unselectable();
8461 this.proxy.enableDisplayMode('block');
8463 Roo.apply(this, config);
8466 this.disableTrackOver = true;
8467 this.el.addClass("x-resizable-pinned");
8469 // if the element isn't positioned, make it relative
8470 var position = this.el.getStyle("position");
8471 if(position != "absolute" && position != "fixed"){
8472 this.el.setStyle("position", "relative");
8474 if(!this.handles){ // no handles passed, must be legacy style
8475 this.handles = 's,e,se';
8476 if(this.multiDirectional){
8477 this.handles += ',n,w';
8480 if(this.handles == "all"){
8481 this.handles = "n s e w ne nw se sw";
8483 var hs = this.handles.split(/\s*?[,;]\s*?| /);
8484 var ps = Roo.Resizable.positions;
8485 for(var i = 0, len = hs.length; i < len; i++){
8486 if(hs[i] && ps[hs[i]]){
8487 var pos = ps[hs[i]];
8488 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8492 this.corner = this.southeast;
8494 // updateBox = the box can move..
8495 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8496 this.updateBox = true;
8499 this.activeHandle = null;
8501 if(this.resizeChild){
8502 if(typeof this.resizeChild == "boolean"){
8503 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8505 this.resizeChild = Roo.get(this.resizeChild, true);
8509 if(this.adjustments == "auto"){
8510 var rc = this.resizeChild;
8511 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8512 if(rc && (hw || hn)){
8513 rc.position("relative");
8514 rc.setLeft(hw ? hw.el.getWidth() : 0);
8515 rc.setTop(hn ? hn.el.getHeight() : 0);
8517 this.adjustments = [
8518 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8519 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8524 this.dd = this.dynamic ?
8525 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8526 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8532 * @event beforeresize
8533 * Fired before resize is allowed. Set enabled to false to cancel resize.
8534 * @param {Roo.Resizable} this
8535 * @param {Roo.EventObject} e The mousedown event
8537 "beforeresize" : true,
8541 * @param {Roo.Resizable} this
8542 * @param {Number} x The new x position
8543 * @param {Number} y The new y position
8544 * @param {Number} w The new w width
8545 * @param {Number} h The new h hight
8546 * @param {Roo.EventObject} e The mouseup event
8551 * Fired after a resize.
8552 * @param {Roo.Resizable} this
8553 * @param {Number} width The new width
8554 * @param {Number} height The new height
8555 * @param {Roo.EventObject} e The mouseup event
8560 if(this.width !== null && this.height !== null){
8561 this.resizeTo(this.width, this.height);
8563 this.updateChildSize();
8566 this.el.dom.style.zoom = 1;
8568 Roo.Resizable.superclass.constructor.call(this);
8571 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8572 resizeChild : false,
8573 adjustments : [0, 0],
8583 multiDirectional : false,
8584 disableTrackOver : false,
8585 easing : 'easeOutStrong',
8587 heightIncrement : 0,
8591 preserveRatio : false,
8598 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8600 constrainTo: undefined,
8602 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8604 resizeRegion: undefined,
8608 * Perform a manual resize
8609 * @param {Number} width
8610 * @param {Number} height
8612 resizeTo : function(width, height){
8613 this.el.setSize(width, height);
8614 this.updateChildSize();
8615 this.fireEvent("resize", this, width, height, null);
8619 startSizing : function(e, handle){
8620 this.fireEvent("beforeresize", this, e);
8621 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8624 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
8625 this.overlay.unselectable();
8626 this.overlay.enableDisplayMode("block");
8627 this.overlay.on("mousemove", this.onMouseMove, this);
8628 this.overlay.on("mouseup", this.onMouseUp, this);
8630 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8632 this.resizing = true;
8633 this.startBox = this.el.getBox();
8634 this.startPoint = e.getXY();
8635 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8636 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8638 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8639 this.overlay.show();
8641 if(this.constrainTo) {
8642 var ct = Roo.get(this.constrainTo);
8643 this.resizeRegion = ct.getRegion().adjust(
8644 ct.getFrameWidth('t'),
8645 ct.getFrameWidth('l'),
8646 -ct.getFrameWidth('b'),
8647 -ct.getFrameWidth('r')
8651 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8653 this.proxy.setBox(this.startBox);
8655 this.proxy.setStyle('visibility', 'visible');
8661 onMouseDown : function(handle, e){
8664 this.activeHandle = handle;
8665 this.startSizing(e, handle);
8670 onMouseUp : function(e){
8671 var size = this.resizeElement();
8672 this.resizing = false;
8674 this.overlay.hide();
8676 this.fireEvent("resize", this, size.width, size.height, e);
8680 updateChildSize : function(){
8682 if(this.resizeChild){
8684 var child = this.resizeChild;
8685 var adj = this.adjustments;
8686 if(el.dom.offsetWidth){
8687 var b = el.getSize(true);
8688 child.setSize(b.width+adj[0], b.height+adj[1]);
8690 // Second call here for IE
8691 // The first call enables instant resizing and
8692 // the second call corrects scroll bars if they
8695 setTimeout(function(){
8696 if(el.dom.offsetWidth){
8697 var b = el.getSize(true);
8698 child.setSize(b.width+adj[0], b.height+adj[1]);
8706 snap : function(value, inc, min){
8707 if(!inc || !value) {
8710 var newValue = value;
8711 var m = value % inc;
8714 newValue = value + (inc-m);
8716 newValue = value - m;
8719 return Math.max(min, newValue);
8723 resizeElement : function(){
8724 var box = this.proxy.getBox();
8726 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8728 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8730 this.updateChildSize();
8738 constrain : function(v, diff, m, mx){
8741 }else if(v - diff > mx){
8748 onMouseMove : function(e){
8751 try{// try catch so if something goes wrong the user doesn't get hung
8753 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8757 //var curXY = this.startPoint;
8758 var curSize = this.curSize || this.startBox;
8759 var x = this.startBox.x, y = this.startBox.y;
8761 var w = curSize.width, h = curSize.height;
8763 var mw = this.minWidth, mh = this.minHeight;
8764 var mxw = this.maxWidth, mxh = this.maxHeight;
8765 var wi = this.widthIncrement;
8766 var hi = this.heightIncrement;
8768 var eventXY = e.getXY();
8769 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8770 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8772 var pos = this.activeHandle.position;
8777 w = Math.min(Math.max(mw, w), mxw);
8782 h = Math.min(Math.max(mh, h), mxh);
8787 w = Math.min(Math.max(mw, w), mxw);
8788 h = Math.min(Math.max(mh, h), mxh);
8791 diffY = this.constrain(h, diffY, mh, mxh);
8798 var adiffX = Math.abs(diffX);
8799 var sub = (adiffX % wi); // how much
8800 if (sub > (wi/2)) { // far enough to snap
8801 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8803 // remove difference..
8804 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8808 x = Math.max(this.minX, x);
8811 diffX = this.constrain(w, diffX, mw, mxw);
8817 w = Math.min(Math.max(mw, w), mxw);
8818 diffY = this.constrain(h, diffY, mh, mxh);
8823 diffX = this.constrain(w, diffX, mw, mxw);
8824 diffY = this.constrain(h, diffY, mh, mxh);
8831 diffX = this.constrain(w, diffX, mw, mxw);
8833 h = Math.min(Math.max(mh, h), mxh);
8839 var sw = this.snap(w, wi, mw);
8840 var sh = this.snap(h, hi, mh);
8841 if(sw != w || sh != h){
8864 if(this.preserveRatio){
8869 h = Math.min(Math.max(mh, h), mxh);
8874 w = Math.min(Math.max(mw, w), mxw);
8879 w = Math.min(Math.max(mw, w), mxw);
8885 w = Math.min(Math.max(mw, w), mxw);
8891 h = Math.min(Math.max(mh, h), mxh);
8899 h = Math.min(Math.max(mh, h), mxh);
8909 h = Math.min(Math.max(mh, h), mxh);
8917 if (pos == 'hdrag') {
8920 this.proxy.setBounds(x, y, w, h);
8922 this.resizeElement();
8926 this.fireEvent("resizing", this, x, y, w, h, e);
8930 handleOver : function(){
8932 this.el.addClass("x-resizable-over");
8937 handleOut : function(){
8939 this.el.removeClass("x-resizable-over");
8944 * Returns the element this component is bound to.
8945 * @return {Roo.Element}
8952 * Returns the resizeChild element (or null).
8953 * @return {Roo.Element}
8955 getResizeChild : function(){
8956 return this.resizeChild;
8958 groupHandler : function()
8963 * Destroys this resizable. If the element was wrapped and
8964 * removeEl is not true then the element remains.
8965 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8967 destroy : function(removeEl){
8968 this.proxy.remove();
8970 this.overlay.removeAllListeners();
8971 this.overlay.remove();
8973 var ps = Roo.Resizable.positions;
8975 if(typeof ps[k] != "function" && this[ps[k]]){
8976 var h = this[ps[k]];
8977 h.el.removeAllListeners();
8989 // hash to map config positions to true positions
8990 Roo.Resizable.positions = {
8991 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
8996 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8998 // only initialize the template if resizable is used
8999 var tpl = Roo.DomHelper.createTemplate(
9000 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
9003 Roo.Resizable.Handle.prototype.tpl = tpl;
9005 this.position = pos;
9007 // show north drag fro topdra
9008 var handlepos = pos == 'hdrag' ? 'north' : pos;
9010 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
9011 if (pos == 'hdrag') {
9012 this.el.setStyle('cursor', 'pointer');
9014 this.el.unselectable();
9016 this.el.setOpacity(0);
9018 this.el.on("mousedown", this.onMouseDown, this);
9019 if(!disableTrackOver){
9020 this.el.on("mouseover", this.onMouseOver, this);
9021 this.el.on("mouseout", this.onMouseOut, this);
9026 Roo.Resizable.Handle.prototype = {
9027 afterResize : function(rz){
9032 onMouseDown : function(e){
9033 this.rz.onMouseDown(this, e);
9036 onMouseOver : function(e){
9037 this.rz.handleOver(this, e);
9040 onMouseOut : function(e){
9041 this.rz.handleOut(this, e);
9045 * Ext JS Library 1.1.1
9046 * Copyright(c) 2006-2007, Ext JS, LLC.
9048 * Originally Released Under LGPL - original licence link has changed is not relivant.
9051 * <script type="text/javascript">
9056 * @extends Roo.Component
9057 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9059 * Create a new Editor
9060 * @param {Roo.form.Field} field The Field object (or descendant)
9061 * @param {Object} config The config object
9063 Roo.Editor = function(field, config){
9064 Roo.Editor.superclass.constructor.call(this, config);
9068 * @event beforestartedit
9069 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
9070 * false from the handler of this event.
9071 * @param {Editor} this
9072 * @param {Roo.Element} boundEl The underlying element bound to this editor
9073 * @param {Mixed} value The field value being set
9075 "beforestartedit" : true,
9078 * Fires when this editor is displayed
9079 * @param {Roo.Element} boundEl The underlying element bound to this editor
9080 * @param {Mixed} value The starting field value
9084 * @event beforecomplete
9085 * Fires after a change has been made to the field, but before the change is reflected in the underlying
9086 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
9087 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9088 * event will not fire since no edit actually occurred.
9089 * @param {Editor} this
9090 * @param {Mixed} value The current field value
9091 * @param {Mixed} startValue The original field value
9093 "beforecomplete" : true,
9096 * Fires after editing is complete and any changed value has been written to the underlying field.
9097 * @param {Editor} this
9098 * @param {Mixed} value The current field value
9099 * @param {Mixed} startValue The original field value
9104 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9105 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9106 * @param {Roo.form.Field} this
9107 * @param {Roo.EventObject} e The event object
9113 Roo.extend(Roo.Editor, Roo.Component, {
9115 * @cfg {Boolean/String} autosize
9116 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9117 * or "height" to adopt the height only (defaults to false)
9120 * @cfg {Boolean} revertInvalid
9121 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9122 * validation fails (defaults to true)
9125 * @cfg {Boolean} ignoreNoChange
9126 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9127 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
9128 * will never be ignored.
9131 * @cfg {Boolean} hideEl
9132 * False to keep the bound element visible while the editor is displayed (defaults to true)
9135 * @cfg {Mixed} value
9136 * The data value of the underlying field (defaults to "")
9140 * @cfg {String} alignment
9141 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9145 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9146 * for bottom-right shadow (defaults to "frame")
9150 * @cfg {Boolean} constrain True to constrain the editor to the viewport
9154 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9156 completeOnEnter : false,
9158 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9160 cancelOnEsc : false,
9162 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9167 onRender : function(ct, position){
9168 this.el = new Roo.Layer({
9169 shadow: this.shadow,
9175 constrain: this.constrain
9177 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9178 if(this.field.msgTarget != 'title'){
9179 this.field.msgTarget = 'qtip';
9181 this.field.render(this.el);
9183 this.field.el.dom.setAttribute('autocomplete', 'off');
9185 this.field.on("specialkey", this.onSpecialKey, this);
9186 if(this.swallowKeys){
9187 this.field.el.swallowEvent(['keydown','keypress']);
9190 this.field.on("blur", this.onBlur, this);
9191 if(this.field.grow){
9192 this.field.on("autosize", this.el.sync, this.el, {delay:1});
9196 onSpecialKey : function(field, e)
9198 //Roo.log('editor onSpecialKey');
9199 if(this.completeOnEnter && e.getKey() == e.ENTER){
9201 this.completeEdit();
9204 // do not fire special key otherwise it might hide close the editor...
9205 if(e.getKey() == e.ENTER){
9208 if(this.cancelOnEsc && e.getKey() == e.ESC){
9212 this.fireEvent('specialkey', field, e);
9217 * Starts the editing process and shows the editor.
9218 * @param {String/HTMLElement/Element} el The element to edit
9219 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9220 * to the innerHTML of el.
9222 startEdit : function(el, value){
9224 this.completeEdit();
9226 this.boundEl = Roo.get(el);
9227 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9229 this.render(this.parentEl || document.body);
9231 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9234 this.startValue = v;
9235 this.field.setValue(v);
9237 var sz = this.boundEl.getSize();
9238 switch(this.autoSize){
9240 this.setSize(sz.width, "");
9243 this.setSize("", sz.height);
9246 this.setSize(sz.width, sz.height);
9249 this.el.alignTo(this.boundEl, this.alignment);
9250 this.editing = true;
9252 Roo.QuickTips.disable();
9258 * Sets the height and width of this editor.
9259 * @param {Number} width The new width
9260 * @param {Number} height The new height
9262 setSize : function(w, h){
9263 this.field.setSize(w, h);
9270 * Realigns the editor to the bound field based on the current alignment config value.
9272 realign : function(){
9273 this.el.alignTo(this.boundEl, this.alignment);
9277 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9278 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9280 completeEdit : function(remainVisible){
9284 var v = this.getValue();
9285 if(this.revertInvalid !== false && !this.field.isValid()){
9286 v = this.startValue;
9287 this.cancelEdit(true);
9289 if(String(v) === String(this.startValue) && this.ignoreNoChange){
9290 this.editing = false;
9294 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9295 this.editing = false;
9296 if(this.updateEl && this.boundEl){
9297 this.boundEl.update(v);
9299 if(remainVisible !== true){
9302 this.fireEvent("complete", this, v, this.startValue);
9307 onShow : function(){
9309 if(this.hideEl !== false){
9310 this.boundEl.hide();
9313 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9314 this.fixIEFocus = true;
9315 this.deferredFocus.defer(50, this);
9319 this.fireEvent("startedit", this.boundEl, this.startValue);
9322 deferredFocus : function(){
9329 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
9330 * reverted to the original starting value.
9331 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9332 * cancel (defaults to false)
9334 cancelEdit : function(remainVisible){
9336 this.setValue(this.startValue);
9337 if(remainVisible !== true){
9344 onBlur : function(){
9345 if(this.allowBlur !== true && this.editing){
9346 this.completeEdit();
9351 onHide : function(){
9353 this.completeEdit();
9357 if(this.field.collapse){
9358 this.field.collapse();
9361 if(this.hideEl !== false){
9362 this.boundEl.show();
9365 Roo.QuickTips.enable();
9370 * Sets the data value of the editor
9371 * @param {Mixed} value Any valid value supported by the underlying field
9373 setValue : function(v){
9374 this.field.setValue(v);
9378 * Gets the data value of the editor
9379 * @return {Mixed} The data value
9381 getValue : function(){
9382 return this.field.getValue();
9386 * Ext JS Library 1.1.1
9387 * Copyright(c) 2006-2007, Ext JS, LLC.
9389 * Originally Released Under LGPL - original licence link has changed is not relivant.
9392 * <script type="text/javascript">
9396 * @class Roo.BasicDialog
9397 * @extends Roo.util.Observable
9398 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
9400 var dlg = new Roo.BasicDialog("my-dlg", {
9409 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9410 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
9411 dlg.addButton('Cancel', dlg.hide, dlg);
9414 <b>A Dialog should always be a direct child of the body element.</b>
9415 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9416 * @cfg {String} title Default text to display in the title bar (defaults to null)
9417 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9418 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9419 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9420 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9421 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9422 * (defaults to null with no animation)
9423 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9424 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9425 * property for valid values (defaults to 'all')
9426 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9427 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9428 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9429 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9430 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9431 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9432 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9433 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9434 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9435 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9436 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9437 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9438 * draggable = true (defaults to false)
9439 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9440 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9441 * shadow (defaults to false)
9442 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9443 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9444 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9445 * @cfg {Array} buttons Array of buttons
9446 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9448 * Create a new BasicDialog.
9449 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9450 * @param {Object} config Configuration options
9452 Roo.BasicDialog = function(el, config){
9453 this.el = Roo.get(el);
9454 var dh = Roo.DomHelper;
9455 if(!this.el && config && config.autoCreate){
9456 if(typeof config.autoCreate == "object"){
9457 if(!config.autoCreate.id){
9458 config.autoCreate.id = el;
9460 this.el = dh.append(document.body,
9461 config.autoCreate, true);
9463 this.el = dh.append(document.body,
9464 {tag: "div", id: el, style:'visibility:hidden;'}, true);
9468 el.setDisplayed(true);
9469 el.hide = this.hideAction;
9471 el.addClass("x-dlg");
9473 Roo.apply(this, config);
9475 this.proxy = el.createProxy("x-dlg-proxy");
9476 this.proxy.hide = this.hideAction;
9477 this.proxy.setOpacity(.5);
9481 el.setWidth(config.width);
9484 el.setHeight(config.height);
9486 this.size = el.getSize();
9487 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9488 this.xy = [config.x,config.y];
9490 this.xy = el.getCenterXY(true);
9492 /** The header element @type Roo.Element */
9493 this.header = el.child("> .x-dlg-hd");
9494 /** The body element @type Roo.Element */
9495 this.body = el.child("> .x-dlg-bd");
9496 /** The footer element @type Roo.Element */
9497 this.footer = el.child("> .x-dlg-ft");
9500 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
9503 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9506 this.header.unselectable();
9508 this.header.update(this.title);
9510 // this element allows the dialog to be focused for keyboard event
9511 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9512 this.focusEl.swallowEvent("click", true);
9514 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9516 // wrap the body and footer for special rendering
9517 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9519 this.bwrap.dom.appendChild(this.footer.dom);
9522 this.bg = this.el.createChild({
9523 tag: "div", cls:"x-dlg-bg",
9524 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
9526 this.centerBg = this.bg.child("div.x-dlg-bg-center");
9529 if(this.autoScroll !== false && !this.autoTabs){
9530 this.body.setStyle("overflow", "auto");
9533 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9535 if(this.closable !== false){
9536 this.el.addClass("x-dlg-closable");
9537 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9538 this.close.on("click", this.closeClick, this);
9539 this.close.addClassOnOver("x-dlg-close-over");
9541 if(this.collapsible !== false){
9542 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9543 this.collapseBtn.on("click", this.collapseClick, this);
9544 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9545 this.header.on("dblclick", this.collapseClick, this);
9547 if(this.resizable !== false){
9548 this.el.addClass("x-dlg-resizable");
9549 this.resizer = new Roo.Resizable(el, {
9550 minWidth: this.minWidth || 80,
9551 minHeight:this.minHeight || 80,
9552 handles: this.resizeHandles || "all",
9555 this.resizer.on("beforeresize", this.beforeResize, this);
9556 this.resizer.on("resize", this.onResize, this);
9558 if(this.draggable !== false){
9559 el.addClass("x-dlg-draggable");
9560 if (!this.proxyDrag) {
9561 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9564 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9566 dd.setHandleElId(this.header.id);
9567 dd.endDrag = this.endMove.createDelegate(this);
9568 dd.startDrag = this.startMove.createDelegate(this);
9569 dd.onDrag = this.onDrag.createDelegate(this);
9574 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9575 this.mask.enableDisplayMode("block");
9577 this.el.addClass("x-dlg-modal");
9580 this.shadow = new Roo.Shadow({
9581 mode : typeof this.shadow == "string" ? this.shadow : "sides",
9582 offset : this.shadowOffset
9585 this.shadowOffset = 0;
9587 if(Roo.useShims && this.shim !== false){
9588 this.shim = this.el.createShim();
9589 this.shim.hide = this.hideAction;
9598 var bts= this.buttons;
9600 Roo.each(bts, function(b) {
9609 * Fires when a key is pressed
9610 * @param {Roo.BasicDialog} this
9611 * @param {Roo.EventObject} e
9616 * Fires when this dialog is moved by the user.
9617 * @param {Roo.BasicDialog} this
9618 * @param {Number} x The new page X
9619 * @param {Number} y The new page Y
9624 * Fires when this dialog is resized by the user.
9625 * @param {Roo.BasicDialog} this
9626 * @param {Number} width The new width
9627 * @param {Number} height The new height
9632 * Fires before this dialog is hidden.
9633 * @param {Roo.BasicDialog} this
9635 "beforehide" : true,
9638 * Fires when this dialog is hidden.
9639 * @param {Roo.BasicDialog} this
9644 * Fires before this dialog is shown.
9645 * @param {Roo.BasicDialog} this
9647 "beforeshow" : true,
9650 * Fires when this dialog is shown.
9651 * @param {Roo.BasicDialog} this
9655 el.on("keydown", this.onKeyDown, this);
9656 el.on("mousedown", this.toFront, this);
9657 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9659 Roo.DialogManager.register(this);
9660 Roo.BasicDialog.superclass.constructor.call(this);
9663 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9664 shadowOffset: Roo.isIE ? 6 : 5,
9668 defaultButton: null,
9669 buttonAlign: "right",
9674 * Sets the dialog title text
9675 * @param {String} text The title text to display
9676 * @return {Roo.BasicDialog} this
9678 setTitle : function(text){
9679 this.header.update(text);
9684 closeClick : function(){
9689 collapseClick : function(){
9690 this[this.collapsed ? "expand" : "collapse"]();
9694 * Collapses the dialog to its minimized state (only the title bar is visible).
9695 * Equivalent to the user clicking the collapse dialog button.
9697 collapse : function(){
9698 if(!this.collapsed){
9699 this.collapsed = true;
9700 this.el.addClass("x-dlg-collapsed");
9701 this.restoreHeight = this.el.getHeight();
9702 this.resizeTo(this.el.getWidth(), this.header.getHeight());
9707 * Expands a collapsed dialog back to its normal state. Equivalent to the user
9708 * clicking the expand dialog button.
9710 expand : function(){
9712 this.collapsed = false;
9713 this.el.removeClass("x-dlg-collapsed");
9714 this.resizeTo(this.el.getWidth(), this.restoreHeight);
9719 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9720 * @return {Roo.TabPanel} The tabs component
9722 initTabs : function(){
9723 var tabs = this.getTabs();
9724 while(tabs.getTab(0)){
9727 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9729 tabs.addTab(Roo.id(dom), dom.title);
9737 beforeResize : function(){
9738 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9742 onResize : function(){
9744 this.syncBodyHeight();
9745 this.adjustAssets();
9747 this.fireEvent("resize", this, this.size.width, this.size.height);
9751 onKeyDown : function(e){
9752 if(this.isVisible()){
9753 this.fireEvent("keydown", this, e);
9758 * Resizes the dialog.
9759 * @param {Number} width
9760 * @param {Number} height
9761 * @return {Roo.BasicDialog} this
9763 resizeTo : function(width, height){
9764 this.el.setSize(width, height);
9765 this.size = {width: width, height: height};
9766 this.syncBodyHeight();
9767 if(this.fixedcenter){
9770 if(this.isVisible()){
9772 this.adjustAssets();
9774 this.fireEvent("resize", this, width, height);
9780 * Resizes the dialog to fit the specified content size.
9781 * @param {Number} width
9782 * @param {Number} height
9783 * @return {Roo.BasicDialog} this
9785 setContentSize : function(w, h){
9786 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9787 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9788 //if(!this.el.isBorderBox()){
9789 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9790 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9793 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9794 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9796 this.resizeTo(w, h);
9801 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
9802 * executed in response to a particular key being pressed while the dialog is active.
9803 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9804 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9805 * @param {Function} fn The function to call
9806 * @param {Object} scope (optional) The scope of the function
9807 * @return {Roo.BasicDialog} this
9809 addKeyListener : function(key, fn, scope){
9810 var keyCode, shift, ctrl, alt;
9811 if(typeof key == "object" && !(key instanceof Array)){
9812 keyCode = key["key"];
9813 shift = key["shift"];
9819 var handler = function(dlg, e){
9820 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
9822 if(keyCode instanceof Array){
9823 for(var i = 0, len = keyCode.length; i < len; i++){
9824 if(keyCode[i] == k){
9825 fn.call(scope || window, dlg, k, e);
9831 fn.call(scope || window, dlg, k, e);
9836 this.on("keydown", handler);
9841 * Returns the TabPanel component (creates it if it doesn't exist).
9842 * Note: If you wish to simply check for the existence of tabs without creating them,
9843 * check for a null 'tabs' property.
9844 * @return {Roo.TabPanel} The tabs component
9846 getTabs : function(){
9848 this.el.addClass("x-dlg-auto-tabs");
9849 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9850 this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9856 * Adds a button to the footer section of the dialog.
9857 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9858 * object or a valid Roo.DomHelper element config
9859 * @param {Function} handler The function called when the button is clicked
9860 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9861 * @return {Roo.Button} The new button
9863 addButton : function(config, handler, scope){
9864 var dh = Roo.DomHelper;
9866 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9868 if(!this.btnContainer){
9869 var tb = this.footer.createChild({
9871 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9872 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9874 this.btnContainer = tb.firstChild.firstChild.firstChild;
9879 minWidth: this.minButtonWidth,
9882 if(typeof config == "string"){
9883 bconfig.text = config;
9886 bconfig.dhconfig = config;
9888 Roo.apply(bconfig, config);
9892 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9893 bconfig.position = Math.max(0, bconfig.position);
9894 fc = this.btnContainer.childNodes[bconfig.position];
9897 var btn = new Roo.Button(
9899 this.btnContainer.insertBefore(document.createElement("td"),fc)
9900 : this.btnContainer.appendChild(document.createElement("td")),
9901 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
9904 this.syncBodyHeight();
9907 * Array of all the buttons that have been added to this dialog via addButton
9912 this.buttons.push(btn);
9917 * Sets the default button to be focused when the dialog is displayed.
9918 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9919 * @return {Roo.BasicDialog} this
9921 setDefaultButton : function(btn){
9922 this.defaultButton = btn;
9927 getHeaderFooterHeight : function(safe){
9930 height += this.header.getHeight();
9933 var fm = this.footer.getMargins();
9934 height += (this.footer.getHeight()+fm.top+fm.bottom);
9936 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9937 height += this.centerBg.getPadding("tb");
9942 syncBodyHeight : function()
9944 var bd = this.body, // the text
9945 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9947 var height = this.size.height - this.getHeaderFooterHeight(false);
9948 bd.setHeight(height-bd.getMargins("tb"));
9949 var hh = this.header.getHeight();
9950 var h = this.size.height-hh;
9953 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9954 bw.setHeight(h-cb.getPadding("tb"));
9956 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9957 bd.setWidth(bw.getWidth(true));
9959 this.tabs.syncHeight();
9961 this.tabs.el.repaint();
9967 * Restores the previous state of the dialog if Roo.state is configured.
9968 * @return {Roo.BasicDialog} this
9970 restoreState : function(){
9971 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9972 if(box && box.width){
9973 this.xy = [box.x, box.y];
9974 this.resizeTo(box.width, box.height);
9980 beforeShow : function(){
9982 if(this.fixedcenter){
9983 this.xy = this.el.getCenterXY(true);
9986 Roo.get(document.body).addClass("x-body-masked");
9987 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9994 animShow : function(){
9995 var b = Roo.get(this.animateTarget).getBox();
9996 this.proxy.setSize(b.width, b.height);
9997 this.proxy.setLocation(b.x, b.y);
9999 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
10000 true, .35, this.showEl.createDelegate(this));
10004 * Shows the dialog.
10005 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
10006 * @return {Roo.BasicDialog} this
10008 show : function(animateTarget){
10009 if (this.fireEvent("beforeshow", this) === false){
10012 if(this.syncHeightBeforeShow){
10013 this.syncBodyHeight();
10014 }else if(this.firstShow){
10015 this.firstShow = false;
10016 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10018 this.animateTarget = animateTarget || this.animateTarget;
10019 if(!this.el.isVisible()){
10021 if(this.animateTarget && Roo.get(this.animateTarget)){
10031 showEl : function(){
10033 this.el.setXY(this.xy);
10035 this.adjustAssets(true);
10038 // IE peekaboo bug - fix found by Dave Fenwick
10042 this.fireEvent("show", this);
10046 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
10047 * dialog itself will receive focus.
10049 focus : function(){
10050 if(this.defaultButton){
10051 this.defaultButton.focus();
10053 this.focusEl.focus();
10058 constrainXY : function(){
10059 if(this.constraintoviewport !== false){
10060 if(!this.viewSize){
10061 if(this.container){
10062 var s = this.container.getSize();
10063 this.viewSize = [s.width, s.height];
10065 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10068 var s = Roo.get(this.container||document).getScroll();
10070 var x = this.xy[0], y = this.xy[1];
10071 var w = this.size.width, h = this.size.height;
10072 var vw = this.viewSize[0], vh = this.viewSize[1];
10073 // only move it if it needs it
10075 // first validate right/bottom
10076 if(x + w > vw+s.left){
10080 if(y + h > vh+s.top){
10084 // then make sure top/left isn't negative
10096 if(this.isVisible()){
10097 this.el.setLocation(x, y);
10098 this.adjustAssets();
10105 onDrag : function(){
10106 if(!this.proxyDrag){
10107 this.xy = this.el.getXY();
10108 this.adjustAssets();
10113 adjustAssets : function(doShow){
10114 var x = this.xy[0], y = this.xy[1];
10115 var w = this.size.width, h = this.size.height;
10116 if(doShow === true){
10118 this.shadow.show(this.el);
10124 if(this.shadow && this.shadow.isVisible()){
10125 this.shadow.show(this.el);
10127 if(this.shim && this.shim.isVisible()){
10128 this.shim.setBounds(x, y, w, h);
10133 adjustViewport : function(w, h){
10135 w = Roo.lib.Dom.getViewWidth();
10136 h = Roo.lib.Dom.getViewHeight();
10139 this.viewSize = [w, h];
10140 if(this.modal && this.mask.isVisible()){
10141 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10142 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10144 if(this.isVisible()){
10145 this.constrainXY();
10150 * Destroys this dialog and all its supporting elements (including any tabs, shim,
10151 * shadow, proxy, mask, etc.) Also removes all event listeners.
10152 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10154 destroy : function(removeEl){
10155 if(this.isVisible()){
10156 this.animateTarget = null;
10159 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10161 this.tabs.destroy(removeEl);
10174 for(var i = 0, len = this.buttons.length; i < len; i++){
10175 this.buttons[i].destroy();
10178 this.el.removeAllListeners();
10179 if(removeEl === true){
10180 this.el.update("");
10183 Roo.DialogManager.unregister(this);
10187 startMove : function(){
10188 if(this.proxyDrag){
10191 if(this.constraintoviewport !== false){
10192 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10197 endMove : function(){
10198 if(!this.proxyDrag){
10199 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10201 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10204 this.refreshSize();
10205 this.adjustAssets();
10207 this.fireEvent("move", this, this.xy[0], this.xy[1]);
10211 * Brings this dialog to the front of any other visible dialogs
10212 * @return {Roo.BasicDialog} this
10214 toFront : function(){
10215 Roo.DialogManager.bringToFront(this);
10220 * Sends this dialog to the back (under) of any other visible dialogs
10221 * @return {Roo.BasicDialog} this
10223 toBack : function(){
10224 Roo.DialogManager.sendToBack(this);
10229 * Centers this dialog in the viewport
10230 * @return {Roo.BasicDialog} this
10232 center : function(){
10233 var xy = this.el.getCenterXY(true);
10234 this.moveTo(xy[0], xy[1]);
10239 * Moves the dialog's top-left corner to the specified point
10240 * @param {Number} x
10241 * @param {Number} y
10242 * @return {Roo.BasicDialog} this
10244 moveTo : function(x, y){
10246 if(this.isVisible()){
10247 this.el.setXY(this.xy);
10248 this.adjustAssets();
10254 * Aligns the dialog to the specified element
10255 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10256 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10257 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10258 * @return {Roo.BasicDialog} this
10260 alignTo : function(element, position, offsets){
10261 this.xy = this.el.getAlignToXY(element, position, offsets);
10262 if(this.isVisible()){
10263 this.el.setXY(this.xy);
10264 this.adjustAssets();
10270 * Anchors an element to another element and realigns it when the window is resized.
10271 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10272 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10273 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10274 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10275 * is a number, it is used as the buffer delay (defaults to 50ms).
10276 * @return {Roo.BasicDialog} this
10278 anchorTo : function(el, alignment, offsets, monitorScroll){
10279 var action = function(){
10280 this.alignTo(el, alignment, offsets);
10282 Roo.EventManager.onWindowResize(action, this);
10283 var tm = typeof monitorScroll;
10284 if(tm != 'undefined'){
10285 Roo.EventManager.on(window, 'scroll', action, this,
10286 {buffer: tm == 'number' ? monitorScroll : 50});
10293 * Returns true if the dialog is visible
10294 * @return {Boolean}
10296 isVisible : function(){
10297 return this.el.isVisible();
10301 animHide : function(callback){
10302 var b = Roo.get(this.animateTarget).getBox();
10304 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10306 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10307 this.hideEl.createDelegate(this, [callback]));
10311 * Hides the dialog.
10312 * @param {Function} callback (optional) Function to call when the dialog is hidden
10313 * @return {Roo.BasicDialog} this
10315 hide : function(callback){
10316 if (this.fireEvent("beforehide", this) === false){
10320 this.shadow.hide();
10325 // sometimes animateTarget seems to get set.. causing problems...
10326 // this just double checks..
10327 if(this.animateTarget && Roo.get(this.animateTarget)) {
10328 this.animHide(callback);
10331 this.hideEl(callback);
10337 hideEl : function(callback){
10341 Roo.get(document.body).removeClass("x-body-masked");
10343 this.fireEvent("hide", this);
10344 if(typeof callback == "function"){
10350 hideAction : function(){
10351 this.setLeft("-10000px");
10352 this.setTop("-10000px");
10353 this.setStyle("visibility", "hidden");
10357 refreshSize : function(){
10358 this.size = this.el.getSize();
10359 this.xy = this.el.getXY();
10360 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10364 // z-index is managed by the DialogManager and may be overwritten at any time
10365 setZIndex : function(index){
10367 this.mask.setStyle("z-index", index);
10370 this.shim.setStyle("z-index", ++index);
10373 this.shadow.setZIndex(++index);
10375 this.el.setStyle("z-index", ++index);
10377 this.proxy.setStyle("z-index", ++index);
10380 this.resizer.proxy.setStyle("z-index", ++index);
10383 this.lastZIndex = index;
10387 * Returns the element for this dialog
10388 * @return {Roo.Element} The underlying dialog Element
10390 getEl : function(){
10396 * @class Roo.DialogManager
10397 * Provides global access to BasicDialogs that have been created and
10398 * support for z-indexing (layering) multiple open dialogs.
10400 Roo.DialogManager = function(){
10402 var accessList = [];
10406 var sortDialogs = function(d1, d2){
10407 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10411 var orderDialogs = function(){
10412 accessList.sort(sortDialogs);
10413 var seed = Roo.DialogManager.zseed;
10414 for(var i = 0, len = accessList.length; i < len; i++){
10415 var dlg = accessList[i];
10417 dlg.setZIndex(seed + (i*10));
10424 * The starting z-index for BasicDialogs (defaults to 9000)
10425 * @type Number The z-index value
10430 register : function(dlg){
10431 list[dlg.id] = dlg;
10432 accessList.push(dlg);
10436 unregister : function(dlg){
10437 delete list[dlg.id];
10440 if(!accessList.indexOf){
10441 for( i = 0, len = accessList.length; i < len; i++){
10442 if(accessList[i] == dlg){
10443 accessList.splice(i, 1);
10448 i = accessList.indexOf(dlg);
10450 accessList.splice(i, 1);
10456 * Gets a registered dialog by id
10457 * @param {String/Object} id The id of the dialog or a dialog
10458 * @return {Roo.BasicDialog} this
10460 get : function(id){
10461 return typeof id == "object" ? id : list[id];
10465 * Brings the specified dialog to the front
10466 * @param {String/Object} dlg The id of the dialog or a dialog
10467 * @return {Roo.BasicDialog} this
10469 bringToFront : function(dlg){
10470 dlg = this.get(dlg);
10473 dlg._lastAccess = new Date().getTime();
10480 * Sends the specified dialog to the back
10481 * @param {String/Object} dlg The id of the dialog or a dialog
10482 * @return {Roo.BasicDialog} this
10484 sendToBack : function(dlg){
10485 dlg = this.get(dlg);
10486 dlg._lastAccess = -(new Date().getTime());
10492 * Hides all dialogs
10494 hideAll : function(){
10495 for(var id in list){
10496 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10505 * @class Roo.LayoutDialog
10506 * @extends Roo.BasicDialog
10507 * Dialog which provides adjustments for working with a layout in a Dialog.
10508 * Add your necessary layout config options to the dialog's config.<br>
10509 * Example usage (including a nested layout):
10512 dialog = new Roo.LayoutDialog("download-dlg", {
10521 // layout config merges with the dialog config
10523 tabPosition: "top",
10524 alwaysShowTabs: true
10527 dialog.addKeyListener(27, dialog.hide, dialog);
10528 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10529 dialog.addButton("Build It!", this.getDownload, this);
10531 // we can even add nested layouts
10532 var innerLayout = new Roo.BorderLayout("dl-inner", {
10542 innerLayout.beginUpdate();
10543 innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10544 innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10545 innerLayout.endUpdate(true);
10547 var layout = dialog.getLayout();
10548 layout.beginUpdate();
10549 layout.add("center", new Roo.ContentPanel("standard-panel",
10550 {title: "Download the Source", fitToFrame:true}));
10551 layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10552 {title: "Build your own roo.js"}));
10553 layout.getRegion("center").showPanel(sp);
10554 layout.endUpdate();
10558 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10559 * @param {Object} config configuration options
10561 Roo.LayoutDialog = function(el, cfg){
10564 if (typeof(cfg) == 'undefined') {
10565 config = Roo.apply({}, el);
10566 // not sure why we use documentElement here.. - it should always be body.
10567 // IE7 borks horribly if we use documentElement.
10568 // webkit also does not like documentElement - it creates a body element...
10569 el = Roo.get( document.body || document.documentElement ).createChild();
10570 //config.autoCreate = true;
10574 config.autoTabs = false;
10575 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10576 this.body.setStyle({overflow:"hidden", position:"relative"});
10577 this.layout = new Roo.BorderLayout(this.body.dom, config);
10578 this.layout.monitorWindowResize = false;
10579 this.el.addClass("x-dlg-auto-layout");
10580 // fix case when center region overwrites center function
10581 this.center = Roo.BasicDialog.prototype.center;
10582 this.on("show", this.layout.layout, this.layout, true);
10583 if (config.items) {
10584 var xitems = config.items;
10585 delete config.items;
10586 Roo.each(xitems, this.addxtype, this);
10591 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10593 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10596 endUpdate : function(){
10597 this.layout.endUpdate();
10601 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10604 beginUpdate : function(){
10605 this.layout.beginUpdate();
10609 * Get the BorderLayout for this dialog
10610 * @return {Roo.BorderLayout}
10612 getLayout : function(){
10613 return this.layout;
10616 showEl : function(){
10617 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10619 this.layout.layout();
10624 // Use the syncHeightBeforeShow config option to control this automatically
10625 syncBodyHeight : function(){
10626 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10627 if(this.layout){this.layout.layout();}
10631 * Add an xtype element (actually adds to the layout.)
10632 * @return {Object} xdata xtype object data.
10635 addxtype : function(c) {
10636 return this.layout.addxtype(c);
10640 * Ext JS Library 1.1.1
10641 * Copyright(c) 2006-2007, Ext JS, LLC.
10643 * Originally Released Under LGPL - original licence link has changed is not relivant.
10646 * <script type="text/javascript">
10650 * @class Roo.MessageBox
10651 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
10655 Roo.Msg.alert('Status', 'Changes saved successfully.');
10657 // Prompt for user data:
10658 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10660 // process text value...
10664 // Show a dialog using config options:
10666 title:'Save Changes?',
10667 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10668 buttons: Roo.Msg.YESNOCANCEL,
10675 Roo.MessageBox = function(){
10676 var dlg, opt, mask, waitTimer;
10677 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10678 var buttons, activeTextEl, bwidth;
10681 var handleButton = function(button){
10683 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10687 var handleHide = function(){
10688 if(opt && opt.cls){
10689 dlg.el.removeClass(opt.cls);
10692 Roo.TaskMgr.stop(waitTimer);
10698 var updateButtons = function(b){
10701 buttons["ok"].hide();
10702 buttons["cancel"].hide();
10703 buttons["yes"].hide();
10704 buttons["no"].hide();
10705 dlg.footer.dom.style.display = 'none';
10708 dlg.footer.dom.style.display = '';
10709 for(var k in buttons){
10710 if(typeof buttons[k] != "function"){
10713 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10714 width += buttons[k].el.getWidth()+15;
10724 var handleEsc = function(d, k, e){
10725 if(opt && opt.closable !== false){
10735 * Returns a reference to the underlying {@link Roo.BasicDialog} element
10736 * @return {Roo.BasicDialog} The BasicDialog element
10738 getDialog : function(){
10740 dlg = new Roo.BasicDialog("x-msg-box", {
10745 constraintoviewport:false,
10747 collapsible : false,
10750 width:400, height:100,
10751 buttonAlign:"center",
10752 closeClick : function(){
10753 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10754 handleButton("no");
10756 handleButton("cancel");
10760 dlg.on("hide", handleHide);
10762 dlg.addKeyListener(27, handleEsc);
10764 var bt = this.buttonText;
10765 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10766 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10767 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10768 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10769 bodyEl = dlg.body.createChild({
10771 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>'
10773 msgEl = bodyEl.dom.firstChild;
10774 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10775 textboxEl.enableDisplayMode();
10776 textboxEl.addKeyListener([10,13], function(){
10777 if(dlg.isVisible() && opt && opt.buttons){
10778 if(opt.buttons.ok){
10779 handleButton("ok");
10780 }else if(opt.buttons.yes){
10781 handleButton("yes");
10785 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10786 textareaEl.enableDisplayMode();
10787 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10788 progressEl.enableDisplayMode();
10789 var pf = progressEl.dom.firstChild;
10791 pp = Roo.get(pf.firstChild);
10792 pp.setHeight(pf.offsetHeight);
10800 * Updates the message box body text
10801 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10802 * the XHTML-compliant non-breaking space character '&#160;')
10803 * @return {Roo.MessageBox} This message box
10805 updateText : function(text){
10806 if(!dlg.isVisible() && !opt.width){
10807 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10809 msgEl.innerHTML = text || ' ';
10811 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10812 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10814 Math.min(opt.width || cw , this.maxWidth),
10815 Math.max(opt.minWidth || this.minWidth, bwidth)
10818 activeTextEl.setWidth(w);
10820 if(dlg.isVisible()){
10821 dlg.fixedcenter = false;
10823 // to big, make it scroll. = But as usual stupid IE does not support
10826 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10827 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10828 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10830 bodyEl.dom.style.height = '';
10831 bodyEl.dom.style.overflowY = '';
10834 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10836 bodyEl.dom.style.overflowX = '';
10839 dlg.setContentSize(w, bodyEl.getHeight());
10840 if(dlg.isVisible()){
10841 dlg.fixedcenter = true;
10847 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
10848 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10849 * @param {Number} value Any number between 0 and 1 (e.g., .5)
10850 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10851 * @return {Roo.MessageBox} This message box
10853 updateProgress : function(value, text){
10855 this.updateText(text);
10857 if (pp) { // weird bug on my firefox - for some reason this is not defined
10858 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10864 * Returns true if the message box is currently displayed
10865 * @return {Boolean} True if the message box is visible, else false
10867 isVisible : function(){
10868 return dlg && dlg.isVisible();
10872 * Hides the message box if it is displayed
10875 if(this.isVisible()){
10881 * Displays a new message box, or reinitializes an existing message box, based on the config options
10882 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10883 * The following config object properties are supported:
10885 Property Type Description
10886 ---------- --------------- ------------------------------------------------------------------------------------
10887 animEl String/Element An id or Element from which the message box should animate as it opens and
10888 closes (defaults to undefined)
10889 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10890 cancel:'Bar'}), or false to not show any buttons (defaults to false)
10891 closable Boolean False to hide the top-right close button (defaults to true). Note that
10892 progress and wait dialogs will ignore this property and always hide the
10893 close button as they can only be closed programmatically.
10894 cls String A custom CSS class to apply to the message box element
10895 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
10896 displayed (defaults to 75)
10897 fn Function A callback function to execute after closing the dialog. The arguments to the
10898 function will be btn (the name of the button that was clicked, if applicable,
10899 e.g. "ok"), and text (the value of the active text field, if applicable).
10900 Progress and wait dialogs will ignore this option since they do not respond to
10901 user actions and can only be closed programmatically, so any required function
10902 should be called by the same code after it closes the dialog.
10903 icon String A CSS class that provides a background image to be used as an icon for
10904 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10905 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
10906 minWidth Number The minimum width in pixels of the message box (defaults to 100)
10907 modal Boolean False to allow user interaction with the page while the message box is
10908 displayed (defaults to true)
10909 msg String A string that will replace the existing message box body text (defaults
10910 to the XHTML-compliant non-breaking space character ' ')
10911 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
10912 progress Boolean True to display a progress bar (defaults to false)
10913 progressText String The text to display inside the progress bar if progress = true (defaults to '')
10914 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
10915 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
10916 title String The title text
10917 value String The string value to set into the active textbox element if displayed
10918 wait Boolean True to display a progress bar (defaults to false)
10919 width Number The width of the dialog in pixels
10926 msg: 'Please enter your address:',
10928 buttons: Roo.MessageBox.OKCANCEL,
10931 animEl: 'addAddressBtn'
10934 * @param {Object} config Configuration options
10935 * @return {Roo.MessageBox} This message box
10937 show : function(options)
10940 // this causes nightmares if you show one dialog after another
10941 // especially on callbacks..
10943 if(this.isVisible()){
10946 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10947 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
10948 Roo.log("New Dialog Message:" + options.msg )
10949 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10950 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10953 var d = this.getDialog();
10955 d.setTitle(opt.title || " ");
10956 d.close.setDisplayed(opt.closable !== false);
10957 activeTextEl = textboxEl;
10958 opt.prompt = opt.prompt || (opt.multiline ? true : false);
10963 textareaEl.setHeight(typeof opt.multiline == "number" ?
10964 opt.multiline : this.defaultTextHeight);
10965 activeTextEl = textareaEl;
10974 progressEl.setDisplayed(opt.progress === true);
10975 this.updateProgress(0);
10976 activeTextEl.dom.value = opt.value || "";
10978 dlg.setDefaultButton(activeTextEl);
10980 var bs = opt.buttons;
10983 db = buttons["ok"];
10984 }else if(bs && bs.yes){
10985 db = buttons["yes"];
10987 dlg.setDefaultButton(db);
10989 bwidth = updateButtons(opt.buttons);
10990 this.updateText(opt.msg);
10992 d.el.addClass(opt.cls);
10994 d.proxyDrag = opt.proxyDrag === true;
10995 d.modal = opt.modal !== false;
10996 d.mask = opt.modal !== false ? mask : false;
10997 if(!d.isVisible()){
10998 // force it to the end of the z-index stack so it gets a cursor in FF
10999 document.body.appendChild(dlg.el.dom);
11000 d.animateTarget = null;
11001 d.show(options.animEl);
11007 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
11008 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
11009 * and closing the message box when the process is complete.
11010 * @param {String} title The title bar text
11011 * @param {String} msg The message box body text
11012 * @return {Roo.MessageBox} This message box
11014 progress : function(title, msg){
11021 minWidth: this.minProgressWidth,
11028 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11029 * If a callback function is passed it will be called after the user clicks the button, and the
11030 * id of the button that was clicked will be passed as the only parameter to the callback
11031 * (could also be the top-right close button).
11032 * @param {String} title The title bar text
11033 * @param {String} msg The message box body text
11034 * @param {Function} fn (optional) The callback function invoked after the message box is closed
11035 * @param {Object} scope (optional) The scope of the callback function
11036 * @return {Roo.MessageBox} This message box
11038 alert : function(title, msg, fn, scope){
11051 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
11052 * interaction while waiting for a long-running process to complete that does not have defined intervals.
11053 * You are responsible for closing the message box when the process is complete.
11054 * @param {String} msg The message box body text
11055 * @param {String} title (optional) The title bar text
11056 * @return {Roo.MessageBox} This message box
11058 wait : function(msg, title){
11069 waitTimer = Roo.TaskMgr.start({
11071 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11079 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11080 * If a callback function is passed it will be called after the user clicks either button, and the id of the
11081 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11082 * @param {String} title The title bar text
11083 * @param {String} msg The message box body text
11084 * @param {Function} fn (optional) The callback function invoked after the message box is closed
11085 * @param {Object} scope (optional) The scope of the callback function
11086 * @return {Roo.MessageBox} This message box
11088 confirm : function(title, msg, fn, scope){
11092 buttons: this.YESNO,
11101 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11102 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
11103 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11104 * (could also be the top-right close button) and the text that was entered will be passed as the two
11105 * parameters to the callback.
11106 * @param {String} title The title bar text
11107 * @param {String} msg The message box body text
11108 * @param {Function} fn (optional) The callback function invoked after the message box is closed
11109 * @param {Object} scope (optional) The scope of the callback function
11110 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11111 * property, or the height in pixels to create the textbox (defaults to false / single-line)
11112 * @return {Roo.MessageBox} This message box
11114 prompt : function(title, msg, fn, scope, multiline){
11118 buttons: this.OKCANCEL,
11123 multiline: multiline,
11130 * Button config that displays a single OK button
11135 * Button config that displays Yes and No buttons
11138 YESNO : {yes:true, no:true},
11140 * Button config that displays OK and Cancel buttons
11143 OKCANCEL : {ok:true, cancel:true},
11145 * Button config that displays Yes, No and Cancel buttons
11148 YESNOCANCEL : {yes:true, no:true, cancel:true},
11151 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11154 defaultTextHeight : 75,
11156 * The maximum width in pixels of the message box (defaults to 600)
11161 * The minimum width in pixels of the message box (defaults to 100)
11166 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
11167 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11170 minProgressWidth : 250,
11172 * An object containing the default button text strings that can be overriden for localized language support.
11173 * Supported properties are: ok, cancel, yes and no.
11174 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11187 * Shorthand for {@link Roo.MessageBox}
11189 Roo.Msg = Roo.MessageBox;/*
11191 * Ext JS Library 1.1.1
11192 * Copyright(c) 2006-2007, Ext JS, LLC.
11194 * Originally Released Under LGPL - original licence link has changed is not relivant.
11197 * <script type="text/javascript">
11200 * @class Roo.QuickTips
11201 * Provides attractive and customizable tooltips for any element.
11204 Roo.QuickTips = function(){
11205 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11206 var ce, bd, xy, dd;
11207 var visible = false, disabled = true, inited = false;
11208 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11210 var onOver = function(e){
11214 var t = e.getTarget();
11215 if(!t || t.nodeType !== 1 || t == document || t == document.body){
11218 if(ce && t == ce.el){
11219 clearTimeout(hideProc);
11222 if(t && tagEls[t.id]){
11223 tagEls[t.id].el = t;
11224 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11227 var ttp, et = Roo.fly(t);
11228 var ns = cfg.namespace;
11229 if(tm.interceptTitles && t.title){
11232 t.removeAttribute("title");
11233 e.preventDefault();
11235 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11238 showProc = show.defer(tm.showDelay, tm, [{
11240 text: ttp.replace(/\\n/g,'<br/>'),
11241 width: et.getAttributeNS(ns, cfg.width),
11242 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11243 title: et.getAttributeNS(ns, cfg.title),
11244 cls: et.getAttributeNS(ns, cfg.cls)
11249 var onOut = function(e){
11250 clearTimeout(showProc);
11251 var t = e.getTarget();
11252 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11253 hideProc = setTimeout(hide, tm.hideDelay);
11257 var onMove = function(e){
11263 if(tm.trackMouse && ce){
11268 var onDown = function(e){
11269 clearTimeout(showProc);
11270 clearTimeout(hideProc);
11272 if(tm.hideOnClick){
11275 tm.enable.defer(100, tm);
11280 var getPad = function(){
11281 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11284 var show = function(o){
11288 clearTimeout(dismissProc);
11290 if(removeCls){ // in case manually hidden
11291 el.removeClass(removeCls);
11295 el.addClass(ce.cls);
11296 removeCls = ce.cls;
11299 tipTitle.update(ce.title);
11302 tipTitle.update('');
11305 el.dom.style.width = tm.maxWidth+'px';
11306 //tipBody.dom.style.width = '';
11307 tipBodyText.update(o.text);
11308 var p = getPad(), w = ce.width;
11310 var td = tipBodyText.dom;
11311 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11312 if(aw > tm.maxWidth){
11314 }else if(aw < tm.minWidth){
11320 //tipBody.setWidth(w);
11321 el.setWidth(parseInt(w, 10) + p);
11322 if(ce.autoHide === false){
11323 close.setDisplayed(true);
11328 close.setDisplayed(false);
11334 el.avoidY = xy[1]-18;
11339 el.setStyle("visibility", "visible");
11340 el.fadeIn({callback: afterShow});
11346 var afterShow = function(){
11350 if(tm.autoDismiss && ce.autoHide !== false){
11351 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11356 var hide = function(noanim){
11357 clearTimeout(dismissProc);
11358 clearTimeout(hideProc);
11360 if(el.isVisible()){
11362 if(noanim !== true && tm.animate){
11363 el.fadeOut({callback: afterHide});
11370 var afterHide = function(){
11373 el.removeClass(removeCls);
11380 * @cfg {Number} minWidth
11381 * The minimum width of the quick tip (defaults to 40)
11385 * @cfg {Number} maxWidth
11386 * The maximum width of the quick tip (defaults to 300)
11390 * @cfg {Boolean} interceptTitles
11391 * True to automatically use the element's DOM title value if available (defaults to false)
11393 interceptTitles : false,
11395 * @cfg {Boolean} trackMouse
11396 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11398 trackMouse : false,
11400 * @cfg {Boolean} hideOnClick
11401 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11403 hideOnClick : true,
11405 * @cfg {Number} showDelay
11406 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11410 * @cfg {Number} hideDelay
11411 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11415 * @cfg {Boolean} autoHide
11416 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11417 * Used in conjunction with hideDelay.
11422 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11423 * (defaults to true). Used in conjunction with autoDismissDelay.
11425 autoDismiss : true,
11428 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11430 autoDismissDelay : 5000,
11432 * @cfg {Boolean} animate
11433 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11438 * @cfg {String} title
11439 * Title text to display (defaults to ''). This can be any valid HTML markup.
11443 * @cfg {String} text
11444 * Body text to display (defaults to ''). This can be any valid HTML markup.
11448 * @cfg {String} cls
11449 * A CSS class to apply to the base quick tip element (defaults to '').
11453 * @cfg {Number} width
11454 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
11455 * minWidth or maxWidth.
11460 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
11461 * or display QuickTips in a page.
11464 tm = Roo.QuickTips;
11465 cfg = tm.tagConfig;
11467 if(!Roo.isReady){ // allow calling of init() before onReady
11468 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11471 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11472 el.fxDefaults = {stopFx: true};
11473 // maximum custom styling
11474 //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>');
11475 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>');
11476 tipTitle = el.child('h3');
11477 tipTitle.enableDisplayMode("block");
11478 tipBody = el.child('div.x-tip-bd');
11479 tipBodyText = el.child('div.x-tip-bd-inner');
11480 //bdLeft = el.child('div.x-tip-bd-left');
11481 //bdRight = el.child('div.x-tip-bd-right');
11482 close = el.child('div.x-tip-close');
11483 close.enableDisplayMode("block");
11484 close.on("click", hide);
11485 var d = Roo.get(document);
11486 d.on("mousedown", onDown);
11487 d.on("mouseover", onOver);
11488 d.on("mouseout", onOut);
11489 d.on("mousemove", onMove);
11490 esc = d.addKeyListener(27, hide);
11493 dd = el.initDD("default", null, {
11494 onDrag : function(){
11498 dd.setHandleElId(tipTitle.id);
11507 * Configures a new quick tip instance and assigns it to a target element. The following config options
11510 Property Type Description
11511 ---------- --------------------- ------------------------------------------------------------------------
11512 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
11514 * @param {Object} config The config object
11516 register : function(config){
11517 var cs = config instanceof Array ? config : arguments;
11518 for(var i = 0, len = cs.length; i < len; i++) {
11520 var target = c.target;
11522 if(target instanceof Array){
11523 for(var j = 0, jlen = target.length; j < jlen; j++){
11524 tagEls[target[j]] = c;
11527 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11534 * Removes this quick tip from its element and destroys it.
11535 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11537 unregister : function(el){
11538 delete tagEls[Roo.id(el)];
11542 * Enable this quick tip.
11544 enable : function(){
11545 if(inited && disabled){
11547 if(locks.length < 1){
11554 * Disable this quick tip.
11556 disable : function(){
11558 clearTimeout(showProc);
11559 clearTimeout(hideProc);
11560 clearTimeout(dismissProc);
11568 * Returns true if the quick tip is enabled, else false.
11570 isEnabled : function(){
11576 namespace : "roo", // was ext?? this may break..
11577 alt_namespace : "ext",
11578 attribute : "qtip",
11588 // backwards compat
11589 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11591 * Ext JS Library 1.1.1
11592 * Copyright(c) 2006-2007, Ext JS, LLC.
11594 * Originally Released Under LGPL - original licence link has changed is not relivant.
11597 * <script type="text/javascript">
11602 * @class Roo.tree.TreePanel
11603 * @extends Roo.data.Tree
11605 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11606 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11607 * @cfg {Boolean} enableDD true to enable drag and drop
11608 * @cfg {Boolean} enableDrag true to enable just drag
11609 * @cfg {Boolean} enableDrop true to enable just drop
11610 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11611 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11612 * @cfg {String} ddGroup The DD group this TreePanel belongs to
11613 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11614 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11615 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11616 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11617 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11618 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11619 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11620 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11621 * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11622 * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11623 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11624 * @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>
11625 * @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>
11628 * @param {String/HTMLElement/Element} el The container element
11629 * @param {Object} config
11631 Roo.tree.TreePanel = function(el, config){
11633 var loader = false;
11635 root = config.root;
11636 delete config.root;
11638 if (config.loader) {
11639 loader = config.loader;
11640 delete config.loader;
11643 Roo.apply(this, config);
11644 Roo.tree.TreePanel.superclass.constructor.call(this);
11645 this.el = Roo.get(el);
11646 this.el.addClass('x-tree');
11647 //console.log(root);
11649 this.setRootNode( Roo.factory(root, Roo.tree));
11652 this.loader = Roo.factory(loader, Roo.tree);
11655 * Read-only. The id of the container element becomes this TreePanel's id.
11657 this.id = this.el.id;
11660 * @event beforeload
11661 * Fires before a node is loaded, return false to cancel
11662 * @param {Node} node The node being loaded
11664 "beforeload" : true,
11667 * Fires when a node is loaded
11668 * @param {Node} node The node that was loaded
11672 * @event textchange
11673 * Fires when the text for a node is changed
11674 * @param {Node} node The node
11675 * @param {String} text The new text
11676 * @param {String} oldText The old text
11678 "textchange" : true,
11680 * @event beforeexpand
11681 * Fires before a node is expanded, return false to cancel.
11682 * @param {Node} node The node
11683 * @param {Boolean} deep
11684 * @param {Boolean} anim
11686 "beforeexpand" : true,
11688 * @event beforecollapse
11689 * Fires before a node is collapsed, return false to cancel.
11690 * @param {Node} node The node
11691 * @param {Boolean} deep
11692 * @param {Boolean} anim
11694 "beforecollapse" : true,
11697 * Fires when a node is expanded
11698 * @param {Node} node The node
11702 * @event disabledchange
11703 * Fires when the disabled status of a node changes
11704 * @param {Node} node The node
11705 * @param {Boolean} disabled
11707 "disabledchange" : true,
11710 * Fires when a node is collapsed
11711 * @param {Node} node The node
11715 * @event beforeclick
11716 * Fires before click processing on a node. Return false to cancel the default action.
11717 * @param {Node} node The node
11718 * @param {Roo.EventObject} e The event object
11720 "beforeclick":true,
11722 * @event checkchange
11723 * Fires when a node with a checkbox's checked property changes
11724 * @param {Node} this This node
11725 * @param {Boolean} checked
11727 "checkchange":true,
11730 * Fires when a node is clicked
11731 * @param {Node} node The node
11732 * @param {Roo.EventObject} e The event object
11737 * Fires when a node is double clicked
11738 * @param {Node} node The node
11739 * @param {Roo.EventObject} e The event object
11743 * @event contextmenu
11744 * Fires when a node is right clicked
11745 * @param {Node} node The node
11746 * @param {Roo.EventObject} e The event object
11748 "contextmenu":true,
11750 * @event beforechildrenrendered
11751 * Fires right before the child nodes for a node are rendered
11752 * @param {Node} node The node
11754 "beforechildrenrendered":true,
11757 * Fires when a node starts being dragged
11758 * @param {Roo.tree.TreePanel} this
11759 * @param {Roo.tree.TreeNode} node
11760 * @param {event} e The raw browser event
11762 "startdrag" : true,
11765 * Fires when a drag operation is complete
11766 * @param {Roo.tree.TreePanel} this
11767 * @param {Roo.tree.TreeNode} node
11768 * @param {event} e The raw browser event
11773 * Fires when a dragged node is dropped on a valid DD target
11774 * @param {Roo.tree.TreePanel} this
11775 * @param {Roo.tree.TreeNode} node
11776 * @param {DD} dd The dd it was dropped on
11777 * @param {event} e The raw browser event
11781 * @event beforenodedrop
11782 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11783 * passed to handlers has the following properties:<br />
11784 * <ul style="padding:5px;padding-left:16px;">
11785 * <li>tree - The TreePanel</li>
11786 * <li>target - The node being targeted for the drop</li>
11787 * <li>data - The drag data from the drag source</li>
11788 * <li>point - The point of the drop - append, above or below</li>
11789 * <li>source - The drag source</li>
11790 * <li>rawEvent - Raw mouse event</li>
11791 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11792 * to be inserted by setting them on this object.</li>
11793 * <li>cancel - Set this to true to cancel the drop.</li>
11795 * @param {Object} dropEvent
11797 "beforenodedrop" : true,
11800 * Fires after a DD object is dropped on a node in this tree. The dropEvent
11801 * passed to handlers has the following properties:<br />
11802 * <ul style="padding:5px;padding-left:16px;">
11803 * <li>tree - The TreePanel</li>
11804 * <li>target - The node being targeted for the drop</li>
11805 * <li>data - The drag data from the drag source</li>
11806 * <li>point - The point of the drop - append, above or below</li>
11807 * <li>source - The drag source</li>
11808 * <li>rawEvent - Raw mouse event</li>
11809 * <li>dropNode - Dropped node(s).</li>
11811 * @param {Object} dropEvent
11815 * @event nodedragover
11816 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11817 * passed to handlers has the following properties:<br />
11818 * <ul style="padding:5px;padding-left:16px;">
11819 * <li>tree - The TreePanel</li>
11820 * <li>target - The node being targeted for the drop</li>
11821 * <li>data - The drag data from the drag source</li>
11822 * <li>point - The point of the drop - append, above or below</li>
11823 * <li>source - The drag source</li>
11824 * <li>rawEvent - Raw mouse event</li>
11825 * <li>dropNode - Drop node(s) provided by the source.</li>
11826 * <li>cancel - Set this to true to signal drop not allowed.</li>
11828 * @param {Object} dragOverEvent
11830 "nodedragover" : true
11833 if(this.singleExpand){
11834 this.on("beforeexpand", this.restrictExpand, this);
11837 this.editor.tree = this;
11838 this.editor = Roo.factory(this.editor, Roo.tree);
11841 if (this.selModel) {
11842 this.selModel = Roo.factory(this.selModel, Roo.tree);
11846 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11847 rootVisible : true,
11848 animate: Roo.enableFx,
11851 hlDrop : Roo.enableFx,
11855 rendererTip: false,
11857 restrictExpand : function(node){
11858 var p = node.parentNode;
11860 if(p.expandedChild && p.expandedChild.parentNode == p){
11861 p.expandedChild.collapse();
11863 p.expandedChild = node;
11867 // private override
11868 setRootNode : function(node){
11869 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11870 if(!this.rootVisible){
11871 node.ui = new Roo.tree.RootTreeNodeUI(node);
11877 * Returns the container element for this TreePanel
11879 getEl : function(){
11884 * Returns the default TreeLoader for this TreePanel
11886 getLoader : function(){
11887 return this.loader;
11893 expandAll : function(){
11894 this.root.expand(true);
11898 * Collapse all nodes
11900 collapseAll : function(){
11901 this.root.collapse(true);
11905 * Returns the selection model used by this TreePanel
11907 getSelectionModel : function(){
11908 if(!this.selModel){
11909 this.selModel = new Roo.tree.DefaultSelectionModel();
11911 return this.selModel;
11915 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11916 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11917 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11920 getChecked : function(a, startNode){
11921 startNode = startNode || this.root;
11923 var f = function(){
11924 if(this.attributes.checked){
11925 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11928 startNode.cascade(f);
11933 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11934 * @param {String} path
11935 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11936 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11937 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11939 expandPath : function(path, attr, callback){
11940 attr = attr || "id";
11941 var keys = path.split(this.pathSeparator);
11942 var curNode = this.root;
11943 if(curNode.attributes[attr] != keys[1]){ // invalid root
11945 callback(false, null);
11950 var f = function(){
11951 if(++index == keys.length){
11953 callback(true, curNode);
11957 var c = curNode.findChild(attr, keys[index]);
11960 callback(false, curNode);
11965 c.expand(false, false, f);
11967 curNode.expand(false, false, f);
11971 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11972 * @param {String} path
11973 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11974 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11975 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11977 selectPath : function(path, attr, callback){
11978 attr = attr || "id";
11979 var keys = path.split(this.pathSeparator);
11980 var v = keys.pop();
11981 if(keys.length > 0){
11982 var f = function(success, node){
11983 if(success && node){
11984 var n = node.findChild(attr, v);
11990 }else if(callback){
11991 callback(false, n);
11995 callback(false, n);
11999 this.expandPath(keys.join(this.pathSeparator), attr, f);
12001 this.root.select();
12003 callback(true, this.root);
12008 getTreeEl : function(){
12013 * Trigger rendering of this TreePanel
12015 render : function(){
12016 if (this.innerCt) {
12017 return this; // stop it rendering more than once!!
12020 this.innerCt = this.el.createChild({tag:"ul",
12021 cls:"x-tree-root-ct " +
12022 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12024 if(this.containerScroll){
12025 Roo.dd.ScrollManager.register(this.el);
12027 if((this.enableDD || this.enableDrop) && !this.dropZone){
12029 * The dropZone used by this tree if drop is enabled
12030 * @type Roo.tree.TreeDropZone
12032 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12033 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12036 if((this.enableDD || this.enableDrag) && !this.dragZone){
12038 * The dragZone used by this tree if drag is enabled
12039 * @type Roo.tree.TreeDragZone
12041 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12042 ddGroup: this.ddGroup || "TreeDD",
12043 scroll: this.ddScroll
12046 this.getSelectionModel().init(this);
12048 Roo.log("ROOT not set in tree");
12051 this.root.render();
12052 if(!this.rootVisible){
12053 this.root.renderChildren();
12059 * Ext JS Library 1.1.1
12060 * Copyright(c) 2006-2007, Ext JS, LLC.
12062 * Originally Released Under LGPL - original licence link has changed is not relivant.
12065 * <script type="text/javascript">
12070 * @class Roo.tree.DefaultSelectionModel
12071 * @extends Roo.util.Observable
12072 * The default single selection for a TreePanel.
12073 * @param {Object} cfg Configuration
12075 Roo.tree.DefaultSelectionModel = function(cfg){
12076 this.selNode = null;
12082 * @event selectionchange
12083 * Fires when the selected node changes
12084 * @param {DefaultSelectionModel} this
12085 * @param {TreeNode} node the new selection
12087 "selectionchange" : true,
12090 * @event beforeselect
12091 * Fires before the selected node changes, return false to cancel the change
12092 * @param {DefaultSelectionModel} this
12093 * @param {TreeNode} node the new selection
12094 * @param {TreeNode} node the old selection
12096 "beforeselect" : true
12099 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12102 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12103 init : function(tree){
12105 tree.getTreeEl().on("keydown", this.onKeyDown, this);
12106 tree.on("click", this.onNodeClick, this);
12109 onNodeClick : function(node, e){
12110 if (e.ctrlKey && this.selNode == node) {
12111 this.unselect(node);
12119 * @param {TreeNode} node The node to select
12120 * @return {TreeNode} The selected node
12122 select : function(node){
12123 var last = this.selNode;
12124 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12126 last.ui.onSelectedChange(false);
12128 this.selNode = node;
12129 node.ui.onSelectedChange(true);
12130 this.fireEvent("selectionchange", this, node, last);
12137 * @param {TreeNode} node The node to unselect
12139 unselect : function(node){
12140 if(this.selNode == node){
12141 this.clearSelections();
12146 * Clear all selections
12148 clearSelections : function(){
12149 var n = this.selNode;
12151 n.ui.onSelectedChange(false);
12152 this.selNode = null;
12153 this.fireEvent("selectionchange", this, null);
12159 * Get the selected node
12160 * @return {TreeNode} The selected node
12162 getSelectedNode : function(){
12163 return this.selNode;
12167 * Returns true if the node is selected
12168 * @param {TreeNode} node The node to check
12169 * @return {Boolean}
12171 isSelected : function(node){
12172 return this.selNode == node;
12176 * Selects the node above the selected node in the tree, intelligently walking the nodes
12177 * @return TreeNode The new selection
12179 selectPrevious : function(){
12180 var s = this.selNode || this.lastSelNode;
12184 var ps = s.previousSibling;
12186 if(!ps.isExpanded() || ps.childNodes.length < 1){
12187 return this.select(ps);
12189 var lc = ps.lastChild;
12190 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12193 return this.select(lc);
12195 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12196 return this.select(s.parentNode);
12202 * Selects the node above the selected node in the tree, intelligently walking the nodes
12203 * @return TreeNode The new selection
12205 selectNext : function(){
12206 var s = this.selNode || this.lastSelNode;
12210 if(s.firstChild && s.isExpanded()){
12211 return this.select(s.firstChild);
12212 }else if(s.nextSibling){
12213 return this.select(s.nextSibling);
12214 }else if(s.parentNode){
12216 s.parentNode.bubble(function(){
12217 if(this.nextSibling){
12218 newS = this.getOwnerTree().selModel.select(this.nextSibling);
12227 onKeyDown : function(e){
12228 var s = this.selNode || this.lastSelNode;
12229 // undesirable, but required
12234 var k = e.getKey();
12242 this.selectPrevious();
12245 e.preventDefault();
12246 if(s.hasChildNodes()){
12247 if(!s.isExpanded()){
12249 }else if(s.firstChild){
12250 this.select(s.firstChild, e);
12255 e.preventDefault();
12256 if(s.hasChildNodes() && s.isExpanded()){
12258 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12259 this.select(s.parentNode, e);
12267 * @class Roo.tree.MultiSelectionModel
12268 * @extends Roo.util.Observable
12269 * Multi selection for a TreePanel.
12270 * @param {Object} cfg Configuration
12272 Roo.tree.MultiSelectionModel = function(){
12273 this.selNodes = [];
12277 * @event selectionchange
12278 * Fires when the selected nodes change
12279 * @param {MultiSelectionModel} this
12280 * @param {Array} nodes Array of the selected nodes
12282 "selectionchange" : true
12284 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12288 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12289 init : function(tree){
12291 tree.getTreeEl().on("keydown", this.onKeyDown, this);
12292 tree.on("click", this.onNodeClick, this);
12295 onNodeClick : function(node, e){
12296 this.select(node, e, e.ctrlKey);
12301 * @param {TreeNode} node The node to select
12302 * @param {EventObject} e (optional) An event associated with the selection
12303 * @param {Boolean} keepExisting True to retain existing selections
12304 * @return {TreeNode} The selected node
12306 select : function(node, e, keepExisting){
12307 if(keepExisting !== true){
12308 this.clearSelections(true);
12310 if(this.isSelected(node)){
12311 this.lastSelNode = node;
12314 this.selNodes.push(node);
12315 this.selMap[node.id] = node;
12316 this.lastSelNode = node;
12317 node.ui.onSelectedChange(true);
12318 this.fireEvent("selectionchange", this, this.selNodes);
12324 * @param {TreeNode} node The node to unselect
12326 unselect : function(node){
12327 if(this.selMap[node.id]){
12328 node.ui.onSelectedChange(false);
12329 var sn = this.selNodes;
12332 index = sn.indexOf(node);
12334 for(var i = 0, len = sn.length; i < len; i++){
12342 this.selNodes.splice(index, 1);
12344 delete this.selMap[node.id];
12345 this.fireEvent("selectionchange", this, this.selNodes);
12350 * Clear all selections
12352 clearSelections : function(suppressEvent){
12353 var sn = this.selNodes;
12355 for(var i = 0, len = sn.length; i < len; i++){
12356 sn[i].ui.onSelectedChange(false);
12358 this.selNodes = [];
12360 if(suppressEvent !== true){
12361 this.fireEvent("selectionchange", this, this.selNodes);
12367 * Returns true if the node is selected
12368 * @param {TreeNode} node The node to check
12369 * @return {Boolean}
12371 isSelected : function(node){
12372 return this.selMap[node.id] ? true : false;
12376 * Returns an array of the selected nodes
12379 getSelectedNodes : function(){
12380 return this.selNodes;
12383 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12385 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12387 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12390 * Ext JS Library 1.1.1
12391 * Copyright(c) 2006-2007, Ext JS, LLC.
12393 * Originally Released Under LGPL - original licence link has changed is not relivant.
12396 * <script type="text/javascript">
12400 * @class Roo.tree.TreeNode
12401 * @extends Roo.data.Node
12402 * @cfg {String} text The text for this node
12403 * @cfg {Boolean} expanded true to start the node expanded
12404 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12405 * @cfg {Boolean} allowDrop false if this node cannot be drop on
12406 * @cfg {Boolean} disabled true to start the node disabled
12407 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12408 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
12409 * @cfg {String} cls A css class to be added to the node
12410 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12411 * @cfg {String} href URL of the link used for the node (defaults to #)
12412 * @cfg {String} hrefTarget target frame for the link
12413 * @cfg {String} qtip An Ext QuickTip for the node
12414 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12415 * @cfg {Boolean} singleClickExpand True for single click expand on this node
12416 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12417 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12418 * (defaults to undefined with no checkbox rendered)
12420 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12422 Roo.tree.TreeNode = function(attributes){
12423 attributes = attributes || {};
12424 if(typeof attributes == "string"){
12425 attributes = {text: attributes};
12427 this.childrenRendered = false;
12428 this.rendered = false;
12429 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12430 this.expanded = attributes.expanded === true;
12431 this.isTarget = attributes.isTarget !== false;
12432 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12433 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12436 * Read-only. The text for this node. To change it use setText().
12439 this.text = attributes.text;
12441 * True if this node is disabled.
12444 this.disabled = attributes.disabled === true;
12448 * @event textchange
12449 * Fires when the text for this node is changed
12450 * @param {Node} this This node
12451 * @param {String} text The new text
12452 * @param {String} oldText The old text
12454 "textchange" : true,
12456 * @event beforeexpand
12457 * Fires before this node is expanded, return false to cancel.
12458 * @param {Node} this This node
12459 * @param {Boolean} deep
12460 * @param {Boolean} anim
12462 "beforeexpand" : true,
12464 * @event beforecollapse
12465 * Fires before this node is collapsed, return false to cancel.
12466 * @param {Node} this This node
12467 * @param {Boolean} deep
12468 * @param {Boolean} anim
12470 "beforecollapse" : true,
12473 * Fires when this node is expanded
12474 * @param {Node} this This node
12478 * @event disabledchange
12479 * Fires when the disabled status of this node changes
12480 * @param {Node} this This node
12481 * @param {Boolean} disabled
12483 "disabledchange" : true,
12486 * Fires when this node is collapsed
12487 * @param {Node} this This node
12491 * @event beforeclick
12492 * Fires before click processing. Return false to cancel the default action.
12493 * @param {Node} this This node
12494 * @param {Roo.EventObject} e The event object
12496 "beforeclick":true,
12498 * @event checkchange
12499 * Fires when a node with a checkbox's checked property changes
12500 * @param {Node} this This node
12501 * @param {Boolean} checked
12503 "checkchange":true,
12506 * Fires when this node is clicked
12507 * @param {Node} this This node
12508 * @param {Roo.EventObject} e The event object
12513 * Fires when this node is double clicked
12514 * @param {Node} this This node
12515 * @param {Roo.EventObject} e The event object
12519 * @event contextmenu
12520 * Fires when this node is right clicked
12521 * @param {Node} this This node
12522 * @param {Roo.EventObject} e The event object
12524 "contextmenu":true,
12526 * @event beforechildrenrendered
12527 * Fires right before the child nodes for this node are rendered
12528 * @param {Node} this This node
12530 "beforechildrenrendered":true
12533 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12536 * Read-only. The UI for this node
12539 this.ui = new uiClass(this);
12541 // finally support items[]
12542 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12547 Roo.each(this.attributes.items, function(c) {
12548 this.appendChild(Roo.factory(c,Roo.Tree));
12550 delete this.attributes.items;
12555 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12556 preventHScroll: true,
12558 * Returns true if this node is expanded
12559 * @return {Boolean}
12561 isExpanded : function(){
12562 return this.expanded;
12566 * Returns the UI object for this node
12567 * @return {TreeNodeUI}
12569 getUI : function(){
12573 // private override
12574 setFirstChild : function(node){
12575 var of = this.firstChild;
12576 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12577 if(this.childrenRendered && of && node != of){
12578 of.renderIndent(true, true);
12581 this.renderIndent(true, true);
12585 // private override
12586 setLastChild : function(node){
12587 var ol = this.lastChild;
12588 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12589 if(this.childrenRendered && ol && node != ol){
12590 ol.renderIndent(true, true);
12593 this.renderIndent(true, true);
12597 // these methods are overridden to provide lazy rendering support
12598 // private override
12599 appendChild : function()
12601 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12602 if(node && this.childrenRendered){
12605 this.ui.updateExpandIcon();
12609 // private override
12610 removeChild : function(node){
12611 this.ownerTree.getSelectionModel().unselect(node);
12612 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12613 // if it's been rendered remove dom node
12614 if(this.childrenRendered){
12617 if(this.childNodes.length < 1){
12618 this.collapse(false, false);
12620 this.ui.updateExpandIcon();
12622 if(!this.firstChild) {
12623 this.childrenRendered = false;
12628 // private override
12629 insertBefore : function(node, refNode){
12630 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12631 if(newNode && refNode && this.childrenRendered){
12634 this.ui.updateExpandIcon();
12639 * Sets the text for this node
12640 * @param {String} text
12642 setText : function(text){
12643 var oldText = this.text;
12645 this.attributes.text = text;
12646 if(this.rendered){ // event without subscribing
12647 this.ui.onTextChange(this, text, oldText);
12649 this.fireEvent("textchange", this, text, oldText);
12653 * Triggers selection of this node
12655 select : function(){
12656 this.getOwnerTree().getSelectionModel().select(this);
12660 * Triggers deselection of this node
12662 unselect : function(){
12663 this.getOwnerTree().getSelectionModel().unselect(this);
12667 * Returns true if this node is selected
12668 * @return {Boolean}
12670 isSelected : function(){
12671 return this.getOwnerTree().getSelectionModel().isSelected(this);
12675 * Expand this node.
12676 * @param {Boolean} deep (optional) True to expand all children as well
12677 * @param {Boolean} anim (optional) false to cancel the default animation
12678 * @param {Function} callback (optional) A callback to be called when
12679 * expanding this node completes (does not wait for deep expand to complete).
12680 * Called with 1 parameter, this node.
12682 expand : function(deep, anim, callback){
12683 if(!this.expanded){
12684 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12687 if(!this.childrenRendered){
12688 this.renderChildren();
12690 this.expanded = true;
12692 if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12693 this.ui.animExpand(function(){
12694 this.fireEvent("expand", this);
12695 if(typeof callback == "function"){
12699 this.expandChildNodes(true);
12701 }.createDelegate(this));
12705 this.fireEvent("expand", this);
12706 if(typeof callback == "function"){
12711 if(typeof callback == "function"){
12716 this.expandChildNodes(true);
12720 isHiddenRoot : function(){
12721 return this.isRoot && !this.getOwnerTree().rootVisible;
12725 * Collapse this node.
12726 * @param {Boolean} deep (optional) True to collapse all children as well
12727 * @param {Boolean} anim (optional) false to cancel the default animation
12729 collapse : function(deep, anim){
12730 if(this.expanded && !this.isHiddenRoot()){
12731 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12734 this.expanded = false;
12735 if((this.getOwnerTree().animate && anim !== false) || anim){
12736 this.ui.animCollapse(function(){
12737 this.fireEvent("collapse", this);
12739 this.collapseChildNodes(true);
12741 }.createDelegate(this));
12744 this.ui.collapse();
12745 this.fireEvent("collapse", this);
12749 var cs = this.childNodes;
12750 for(var i = 0, len = cs.length; i < len; i++) {
12751 cs[i].collapse(true, false);
12757 delayedExpand : function(delay){
12758 if(!this.expandProcId){
12759 this.expandProcId = this.expand.defer(delay, this);
12764 cancelExpand : function(){
12765 if(this.expandProcId){
12766 clearTimeout(this.expandProcId);
12768 this.expandProcId = false;
12772 * Toggles expanded/collapsed state of the node
12774 toggle : function(){
12783 * Ensures all parent nodes are expanded
12785 ensureVisible : function(callback){
12786 var tree = this.getOwnerTree();
12787 tree.expandPath(this.parentNode.getPath(), false, function(){
12788 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12789 Roo.callback(callback);
12790 }.createDelegate(this));
12794 * Expand all child nodes
12795 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12797 expandChildNodes : function(deep){
12798 var cs = this.childNodes;
12799 for(var i = 0, len = cs.length; i < len; i++) {
12800 cs[i].expand(deep);
12805 * Collapse all child nodes
12806 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12808 collapseChildNodes : function(deep){
12809 var cs = this.childNodes;
12810 for(var i = 0, len = cs.length; i < len; i++) {
12811 cs[i].collapse(deep);
12816 * Disables this node
12818 disable : function(){
12819 this.disabled = true;
12821 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12822 this.ui.onDisableChange(this, true);
12824 this.fireEvent("disabledchange", this, true);
12828 * Enables this node
12830 enable : function(){
12831 this.disabled = false;
12832 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12833 this.ui.onDisableChange(this, false);
12835 this.fireEvent("disabledchange", this, false);
12839 renderChildren : function(suppressEvent){
12840 if(suppressEvent !== false){
12841 this.fireEvent("beforechildrenrendered", this);
12843 var cs = this.childNodes;
12844 for(var i = 0, len = cs.length; i < len; i++){
12845 cs[i].render(true);
12847 this.childrenRendered = true;
12851 sort : function(fn, scope){
12852 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12853 if(this.childrenRendered){
12854 var cs = this.childNodes;
12855 for(var i = 0, len = cs.length; i < len; i++){
12856 cs[i].render(true);
12862 render : function(bulkRender){
12863 this.ui.render(bulkRender);
12864 if(!this.rendered){
12865 this.rendered = true;
12867 this.expanded = false;
12868 this.expand(false, false);
12874 renderIndent : function(deep, refresh){
12876 this.ui.childIndent = null;
12878 this.ui.renderIndent();
12879 if(deep === true && this.childrenRendered){
12880 var cs = this.childNodes;
12881 for(var i = 0, len = cs.length; i < len; i++){
12882 cs[i].renderIndent(true, refresh);
12888 * Ext JS Library 1.1.1
12889 * Copyright(c) 2006-2007, Ext JS, LLC.
12891 * Originally Released Under LGPL - original licence link has changed is not relivant.
12894 * <script type="text/javascript">
12898 * @class Roo.tree.AsyncTreeNode
12899 * @extends Roo.tree.TreeNode
12900 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12902 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12904 Roo.tree.AsyncTreeNode = function(config){
12905 this.loaded = false;
12906 this.loading = false;
12907 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12909 * @event beforeload
12910 * Fires before this node is loaded, return false to cancel
12911 * @param {Node} this This node
12913 this.addEvents({'beforeload':true, 'load': true});
12916 * Fires when this node is loaded
12917 * @param {Node} this This node
12920 * The loader used by this node (defaults to using the tree's defined loader)
12925 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12926 expand : function(deep, anim, callback){
12927 if(this.loading){ // if an async load is already running, waiting til it's done
12929 var f = function(){
12930 if(!this.loading){ // done loading
12931 clearInterval(timer);
12932 this.expand(deep, anim, callback);
12934 }.createDelegate(this);
12935 timer = setInterval(f, 200);
12939 if(this.fireEvent("beforeload", this) === false){
12942 this.loading = true;
12943 this.ui.beforeLoad(this);
12944 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12946 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12950 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12954 * Returns true if this node is currently loading
12955 * @return {Boolean}
12957 isLoading : function(){
12958 return this.loading;
12961 loadComplete : function(deep, anim, callback){
12962 this.loading = false;
12963 this.loaded = true;
12964 this.ui.afterLoad(this);
12965 this.fireEvent("load", this);
12966 this.expand(deep, anim, callback);
12970 * Returns true if this node has been loaded
12971 * @return {Boolean}
12973 isLoaded : function(){
12974 return this.loaded;
12977 hasChildNodes : function(){
12978 if(!this.isLeaf() && !this.loaded){
12981 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12986 * Trigger a reload for this node
12987 * @param {Function} callback
12989 reload : function(callback){
12990 this.collapse(false, false);
12991 while(this.firstChild){
12992 this.removeChild(this.firstChild);
12994 this.childrenRendered = false;
12995 this.loaded = false;
12996 if(this.isHiddenRoot()){
12997 this.expanded = false;
12999 this.expand(false, false, callback);
13003 * Ext JS Library 1.1.1
13004 * Copyright(c) 2006-2007, Ext JS, LLC.
13006 * Originally Released Under LGPL - original licence link has changed is not relivant.
13009 * <script type="text/javascript">
13013 * @class Roo.tree.TreeNodeUI
13015 * @param {Object} node The node to render
13016 * The TreeNode UI implementation is separate from the
13017 * tree implementation. Unless you are customizing the tree UI,
13018 * you should never have to use this directly.
13020 Roo.tree.TreeNodeUI = function(node){
13022 this.rendered = false;
13023 this.animating = false;
13024 this.emptyIcon = Roo.BLANK_IMAGE_URL;
13027 Roo.tree.TreeNodeUI.prototype = {
13028 removeChild : function(node){
13030 this.ctNode.removeChild(node.ui.getEl());
13034 beforeLoad : function(){
13035 this.addClass("x-tree-node-loading");
13038 afterLoad : function(){
13039 this.removeClass("x-tree-node-loading");
13042 onTextChange : function(node, text, oldText){
13044 this.textNode.innerHTML = text;
13048 onDisableChange : function(node, state){
13049 this.disabled = state;
13051 this.addClass("x-tree-node-disabled");
13053 this.removeClass("x-tree-node-disabled");
13057 onSelectedChange : function(state){
13060 this.addClass("x-tree-selected");
13063 this.removeClass("x-tree-selected");
13067 onMove : function(tree, node, oldParent, newParent, index, refNode){
13068 this.childIndent = null;
13070 var targetNode = newParent.ui.getContainer();
13071 if(!targetNode){//target not rendered
13072 this.holder = document.createElement("div");
13073 this.holder.appendChild(this.wrap);
13076 var insertBefore = refNode ? refNode.ui.getEl() : null;
13078 targetNode.insertBefore(this.wrap, insertBefore);
13080 targetNode.appendChild(this.wrap);
13082 this.node.renderIndent(true);
13086 addClass : function(cls){
13088 Roo.fly(this.elNode).addClass(cls);
13092 removeClass : function(cls){
13094 Roo.fly(this.elNode).removeClass(cls);
13098 remove : function(){
13100 this.holder = document.createElement("div");
13101 this.holder.appendChild(this.wrap);
13105 fireEvent : function(){
13106 return this.node.fireEvent.apply(this.node, arguments);
13109 initEvents : function(){
13110 this.node.on("move", this.onMove, this);
13111 var E = Roo.EventManager;
13112 var a = this.anchor;
13114 var el = Roo.fly(a, '_treeui');
13116 if(Roo.isOpera){ // opera render bug ignores the CSS
13117 el.setStyle("text-decoration", "none");
13120 el.on("click", this.onClick, this);
13121 el.on("dblclick", this.onDblClick, this);
13124 Roo.EventManager.on(this.checkbox,
13125 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13128 el.on("contextmenu", this.onContextMenu, this);
13130 var icon = Roo.fly(this.iconNode);
13131 icon.on("click", this.onClick, this);
13132 icon.on("dblclick", this.onDblClick, this);
13133 icon.on("contextmenu", this.onContextMenu, this);
13134 E.on(this.ecNode, "click", this.ecClick, this, true);
13136 if(this.node.disabled){
13137 this.addClass("x-tree-node-disabled");
13139 if(this.node.hidden){
13140 this.addClass("x-tree-node-disabled");
13142 var ot = this.node.getOwnerTree();
13143 var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
13144 if(dd && (!this.node.isRoot || ot.rootVisible)){
13145 Roo.dd.Registry.register(this.elNode, {
13147 handles: this.getDDHandles(),
13153 getDDHandles : function(){
13154 return [this.iconNode, this.textNode];
13159 this.wrap.style.display = "none";
13165 this.wrap.style.display = "";
13169 onContextMenu : function(e){
13170 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13171 e.preventDefault();
13173 this.fireEvent("contextmenu", this.node, e);
13177 onClick : function(e){
13182 if(this.fireEvent("beforeclick", this.node, e) !== false){
13183 if(!this.disabled && this.node.attributes.href){
13184 this.fireEvent("click", this.node, e);
13187 e.preventDefault();
13192 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13193 this.node.toggle();
13196 this.fireEvent("click", this.node, e);
13202 onDblClick : function(e){
13203 e.preventDefault();
13208 this.toggleCheck();
13210 if(!this.animating && this.node.hasChildNodes()){
13211 this.node.toggle();
13213 this.fireEvent("dblclick", this.node, e);
13216 onCheckChange : function(){
13217 var checked = this.checkbox.checked;
13218 this.node.attributes.checked = checked;
13219 this.fireEvent('checkchange', this.node, checked);
13222 ecClick : function(e){
13223 if(!this.animating && this.node.hasChildNodes()){
13224 this.node.toggle();
13228 startDrop : function(){
13229 this.dropping = true;
13232 // delayed drop so the click event doesn't get fired on a drop
13233 endDrop : function(){
13234 setTimeout(function(){
13235 this.dropping = false;
13236 }.createDelegate(this), 50);
13239 expand : function(){
13240 this.updateExpandIcon();
13241 this.ctNode.style.display = "";
13244 focus : function(){
13245 if(!this.node.preventHScroll){
13246 try{this.anchor.focus();
13248 }else if(!Roo.isIE){
13250 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13251 var l = noscroll.scrollLeft;
13252 this.anchor.focus();
13253 noscroll.scrollLeft = l;
13258 toggleCheck : function(value){
13259 var cb = this.checkbox;
13261 cb.checked = (value === undefined ? !cb.checked : value);
13267 this.anchor.blur();
13271 animExpand : function(callback){
13272 var ct = Roo.get(this.ctNode);
13274 if(!this.node.hasChildNodes()){
13275 this.updateExpandIcon();
13276 this.ctNode.style.display = "";
13277 Roo.callback(callback);
13280 this.animating = true;
13281 this.updateExpandIcon();
13284 callback : function(){
13285 this.animating = false;
13286 Roo.callback(callback);
13289 duration: this.node.ownerTree.duration || .25
13293 highlight : function(){
13294 var tree = this.node.getOwnerTree();
13295 Roo.fly(this.wrap).highlight(
13296 tree.hlColor || "C3DAF9",
13297 {endColor: tree.hlBaseColor}
13301 collapse : function(){
13302 this.updateExpandIcon();
13303 this.ctNode.style.display = "none";
13306 animCollapse : function(callback){
13307 var ct = Roo.get(this.ctNode);
13308 ct.enableDisplayMode('block');
13311 this.animating = true;
13312 this.updateExpandIcon();
13315 callback : function(){
13316 this.animating = false;
13317 Roo.callback(callback);
13320 duration: this.node.ownerTree.duration || .25
13324 getContainer : function(){
13325 return this.ctNode;
13328 getEl : function(){
13332 appendDDGhost : function(ghostNode){
13333 ghostNode.appendChild(this.elNode.cloneNode(true));
13336 getDDRepairXY : function(){
13337 return Roo.lib.Dom.getXY(this.iconNode);
13340 onRender : function(){
13344 render : function(bulkRender){
13345 var n = this.node, a = n.attributes;
13346 var targetNode = n.parentNode ?
13347 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13349 if(!this.rendered){
13350 this.rendered = true;
13352 this.renderElements(n, a, targetNode, bulkRender);
13355 if(this.textNode.setAttributeNS){
13356 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13358 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13361 this.textNode.setAttribute("ext:qtip", a.qtip);
13363 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13366 }else if(a.qtipCfg){
13367 a.qtipCfg.target = Roo.id(this.textNode);
13368 Roo.QuickTips.register(a.qtipCfg);
13371 if(!this.node.expanded){
13372 this.updateExpandIcon();
13375 if(bulkRender === true) {
13376 targetNode.appendChild(this.wrap);
13381 renderElements : function(n, a, targetNode, bulkRender)
13383 // add some indent caching, this helps performance when rendering a large tree
13384 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13385 var t = n.getOwnerTree();
13386 var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13387 if (typeof(n.attributes.html) != 'undefined') {
13388 txt = n.attributes.html;
13390 var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
13391 var cb = typeof a.checked == 'boolean';
13392 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13393 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13394 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13395 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13396 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13397 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13398 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13399 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
13400 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13401 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13404 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13405 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13406 n.nextSibling.ui.getEl(), buf.join(""));
13408 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13411 this.elNode = this.wrap.childNodes[0];
13412 this.ctNode = this.wrap.childNodes[1];
13413 var cs = this.elNode.childNodes;
13414 this.indentNode = cs[0];
13415 this.ecNode = cs[1];
13416 this.iconNode = cs[2];
13419 this.checkbox = cs[3];
13422 this.anchor = cs[index];
13423 this.textNode = cs[index].firstChild;
13426 getAnchor : function(){
13427 return this.anchor;
13430 getTextEl : function(){
13431 return this.textNode;
13434 getIconEl : function(){
13435 return this.iconNode;
13438 isChecked : function(){
13439 return this.checkbox ? this.checkbox.checked : false;
13442 updateExpandIcon : function(){
13444 var n = this.node, c1, c2;
13445 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13446 var hasChild = n.hasChildNodes();
13450 c1 = "x-tree-node-collapsed";
13451 c2 = "x-tree-node-expanded";
13454 c1 = "x-tree-node-expanded";
13455 c2 = "x-tree-node-collapsed";
13458 this.removeClass("x-tree-node-leaf");
13459 this.wasLeaf = false;
13461 if(this.c1 != c1 || this.c2 != c2){
13462 Roo.fly(this.elNode).replaceClass(c1, c2);
13463 this.c1 = c1; this.c2 = c2;
13466 // this changes non-leafs into leafs if they have no children.
13467 // it's not very rational behaviour..
13469 if(!this.wasLeaf && this.node.leaf){
13470 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13473 this.wasLeaf = true;
13476 var ecc = "x-tree-ec-icon "+cls;
13477 if(this.ecc != ecc){
13478 this.ecNode.className = ecc;
13484 getChildIndent : function(){
13485 if(!this.childIndent){
13489 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13491 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13493 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13498 this.childIndent = buf.join("");
13500 return this.childIndent;
13503 renderIndent : function(){
13506 var p = this.node.parentNode;
13508 indent = p.ui.getChildIndent();
13510 if(this.indentMarkup != indent){ // don't rerender if not required
13511 this.indentNode.innerHTML = indent;
13512 this.indentMarkup = indent;
13514 this.updateExpandIcon();
13519 Roo.tree.RootTreeNodeUI = function(){
13520 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13522 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13523 render : function(){
13524 if(!this.rendered){
13525 var targetNode = this.node.ownerTree.innerCt.dom;
13526 this.node.expanded = true;
13527 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13528 this.wrap = this.ctNode = targetNode.firstChild;
13531 collapse : function(){
13533 expand : function(){
13537 * Ext JS Library 1.1.1
13538 * Copyright(c) 2006-2007, Ext JS, LLC.
13540 * Originally Released Under LGPL - original licence link has changed is not relivant.
13543 * <script type="text/javascript">
13546 * @class Roo.tree.TreeLoader
13547 * @extends Roo.util.Observable
13548 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13549 * nodes from a specified URL. The response must be a javascript Array definition
13550 * who's elements are node definition objects. eg:
13555 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13556 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13563 * The old style respose with just an array is still supported, but not recommended.
13566 * A server request is sent, and child nodes are loaded only when a node is expanded.
13567 * The loading node's id is passed to the server under the parameter name "node" to
13568 * enable the server to produce the correct child nodes.
13570 * To pass extra parameters, an event handler may be attached to the "beforeload"
13571 * event, and the parameters specified in the TreeLoader's baseParams property:
13573 myTreeLoader.on("beforeload", function(treeLoader, node) {
13574 this.baseParams.category = node.attributes.category;
13579 * This would pass an HTTP parameter called "category" to the server containing
13580 * the value of the Node's "category" attribute.
13582 * Creates a new Treeloader.
13583 * @param {Object} config A config object containing config properties.
13585 Roo.tree.TreeLoader = function(config){
13586 this.baseParams = {};
13587 this.requestMethod = "POST";
13588 Roo.apply(this, config);
13593 * @event beforeload
13594 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13595 * @param {Object} This TreeLoader object.
13596 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13597 * @param {Object} callback The callback function specified in the {@link #load} call.
13602 * Fires when the node has been successfuly loaded.
13603 * @param {Object} This TreeLoader object.
13604 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13605 * @param {Object} response The response object containing the data from the server.
13609 * @event loadexception
13610 * Fires if the network request failed.
13611 * @param {Object} This TreeLoader object.
13612 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13613 * @param {Object} response The response object containing the data from the server.
13615 loadexception : true,
13618 * Fires before a node is created, enabling you to return custom Node types
13619 * @param {Object} This TreeLoader object.
13620 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13625 Roo.tree.TreeLoader.superclass.constructor.call(this);
13628 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13630 * @cfg {String} dataUrl The URL from which to request a Json string which
13631 * specifies an array of node definition object representing the child nodes
13635 * @cfg {String} requestMethod either GET or POST
13636 * defaults to POST (due to BC)
13640 * @cfg {Object} baseParams (optional) An object containing properties which
13641 * specify HTTP parameters to be passed to each request for child nodes.
13644 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13645 * created by this loader. If the attributes sent by the server have an attribute in this object,
13646 * they take priority.
13649 * @cfg {Object} uiProviders (optional) An object containing properties which
13651 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13652 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13653 * <i>uiProvider</i> attribute of a returned child node is a string rather
13654 * than a reference to a TreeNodeUI implementation, this that string value
13655 * is used as a property name in the uiProviders object. You can define the provider named
13656 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13661 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13662 * child nodes before loading.
13664 clearOnLoad : true,
13667 * @cfg {String} root (optional) Default to false. Use this to read data from an object
13668 * property on loading, rather than expecting an array. (eg. more compatible to a standard
13669 * Grid query { data : [ .....] }
13674 * @cfg {String} queryParam (optional)
13675 * Name of the query as it will be passed on the querystring (defaults to 'node')
13676 * eg. the request will be ?node=[id]
13683 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13684 * This is called automatically when a node is expanded, but may be used to reload
13685 * a node (or append new children if the {@link #clearOnLoad} option is false.)
13686 * @param {Roo.tree.TreeNode} node
13687 * @param {Function} callback
13689 load : function(node, callback){
13690 if(this.clearOnLoad){
13691 while(node.firstChild){
13692 node.removeChild(node.firstChild);
13695 if(node.attributes.children){ // preloaded json children
13696 var cs = node.attributes.children;
13697 for(var i = 0, len = cs.length; i < len; i++){
13698 Roo.log('appendchild');
13700 node.appendChild(this.createNode(cs[i]));
13702 if(typeof callback == "function"){
13705 }else if(this.dataUrl){
13706 this.requestData(node, callback);
13710 getParams: function(node){
13711 var buf = [], bp = this.baseParams;
13712 for(var key in bp){
13713 if(typeof bp[key] != "function"){
13714 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13717 var n = this.queryParam === false ? 'node' : this.queryParam;
13718 buf.push(n + "=", encodeURIComponent(node.id));
13719 return buf.join("");
13722 requestData : function(node, callback){
13723 if(this.fireEvent("beforeload", this, node, callback) !== false){
13724 this.transId = Roo.Ajax.request({
13725 method:this.requestMethod,
13726 url: this.dataUrl||this.url,
13727 success: this.handleResponse,
13728 failure: this.handleFailure,
13730 argument: {callback: callback, node: node},
13731 params: this.getParams(node)
13734 // if the load is cancelled, make sure we notify
13735 // the node that we are done
13736 if(typeof callback == "function"){
13742 isLoading : function(){
13743 return this.transId ? true : false;
13746 abort : function(){
13747 if(this.isLoading()){
13748 Roo.Ajax.abort(this.transId);
13753 createNode : function(attr)
13755 // apply baseAttrs, nice idea Corey!
13756 if(this.baseAttrs){
13757 Roo.applyIf(attr, this.baseAttrs);
13759 if(this.applyLoader !== false){
13760 attr.loader = this;
13762 // uiProvider = depreciated..
13764 if(typeof(attr.uiProvider) == 'string'){
13765 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
13766 /** eval:var:attr */ eval(attr.uiProvider);
13768 if(typeof(this.uiProviders['default']) != 'undefined') {
13769 attr.uiProvider = this.uiProviders['default'];
13772 this.fireEvent('create', this, attr);
13774 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13776 new Roo.tree.TreeNode(attr) :
13777 new Roo.tree.AsyncTreeNode(attr));
13780 processResponse : function(response, node, callback)
13782 var json = response.responseText;
13785 var o = Roo.decode(json);
13787 if (this.root === false && typeof(o.success) != undefined) {
13788 this.root = 'data'; // the default behaviour for list like data..
13791 if (this.root !== false && !o.success) {
13792 // it's a failure condition.
13793 var a = response.argument;
13794 this.fireEvent("loadexception", this, a.node, response);
13795 Roo.log("Load failed - should have a handler really");
13801 if (this.root !== false) {
13805 for(var i = 0, len = o.length; i < len; i++){
13806 var n = this.createNode(o[i]);
13808 node.appendChild(n);
13811 if(typeof callback == "function"){
13812 callback(this, node);
13815 this.handleFailure(response);
13819 handleResponse : function(response){
13820 this.transId = false;
13821 var a = response.argument;
13822 this.processResponse(response, a.node, a.callback);
13823 this.fireEvent("load", this, a.node, response);
13826 handleFailure : function(response)
13828 // should handle failure better..
13829 this.transId = false;
13830 var a = response.argument;
13831 this.fireEvent("loadexception", this, a.node, response);
13832 if(typeof a.callback == "function"){
13833 a.callback(this, a.node);
13838 * Ext JS Library 1.1.1
13839 * Copyright(c) 2006-2007, Ext JS, LLC.
13841 * Originally Released Under LGPL - original licence link has changed is not relivant.
13844 * <script type="text/javascript">
13848 * @class Roo.tree.TreeFilter
13849 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13850 * @param {TreePanel} tree
13851 * @param {Object} config (optional)
13853 Roo.tree.TreeFilter = function(tree, config){
13855 this.filtered = {};
13856 Roo.apply(this, config);
13859 Roo.tree.TreeFilter.prototype = {
13866 * Filter the data by a specific attribute.
13867 * @param {String/RegExp} value Either string that the attribute value
13868 * should start with or a RegExp to test against the attribute
13869 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13870 * @param {TreeNode} startNode (optional) The node to start the filter at.
13872 filter : function(value, attr, startNode){
13873 attr = attr || "text";
13875 if(typeof value == "string"){
13876 var vlen = value.length;
13877 // auto clear empty filter
13878 if(vlen == 0 && this.clearBlank){
13882 value = value.toLowerCase();
13884 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13886 }else if(value.exec){ // regex?
13888 return value.test(n.attributes[attr]);
13891 throw 'Illegal filter type, must be string or regex';
13893 this.filterBy(f, null, startNode);
13897 * Filter by a function. The passed function will be called with each
13898 * node in the tree (or from the startNode). If the function returns true, the node is kept
13899 * otherwise it is filtered. If a node is filtered, its children are also filtered.
13900 * @param {Function} fn The filter function
13901 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13903 filterBy : function(fn, scope, startNode){
13904 startNode = startNode || this.tree.root;
13905 if(this.autoClear){
13908 var af = this.filtered, rv = this.reverse;
13909 var f = function(n){
13910 if(n == startNode){
13916 var m = fn.call(scope || n, n);
13924 startNode.cascade(f);
13927 if(typeof id != "function"){
13929 if(n && n.parentNode){
13930 n.parentNode.removeChild(n);
13938 * Clears the current filter. Note: with the "remove" option
13939 * set a filter cannot be cleared.
13941 clear : function(){
13943 var af = this.filtered;
13945 if(typeof id != "function"){
13952 this.filtered = {};
13957 * Ext JS Library 1.1.1
13958 * Copyright(c) 2006-2007, Ext JS, LLC.
13960 * Originally Released Under LGPL - original licence link has changed is not relivant.
13963 * <script type="text/javascript">
13968 * @class Roo.tree.TreeSorter
13969 * Provides sorting of nodes in a TreePanel
13971 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13972 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13973 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13974 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13975 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13976 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13978 * @param {TreePanel} tree
13979 * @param {Object} config
13981 Roo.tree.TreeSorter = function(tree, config){
13982 Roo.apply(this, config);
13983 tree.on("beforechildrenrendered", this.doSort, this);
13984 tree.on("append", this.updateSort, this);
13985 tree.on("insert", this.updateSort, this);
13987 var dsc = this.dir && this.dir.toLowerCase() == "desc";
13988 var p = this.property || "text";
13989 var sortType = this.sortType;
13990 var fs = this.folderSort;
13991 var cs = this.caseSensitive === true;
13992 var leafAttr = this.leafAttr || 'leaf';
13994 this.sortFn = function(n1, n2){
13996 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13999 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
14003 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
14004 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
14006 return dsc ? +1 : -1;
14008 return dsc ? -1 : +1;
14015 Roo.tree.TreeSorter.prototype = {
14016 doSort : function(node){
14017 node.sort(this.sortFn);
14020 compareNodes : function(n1, n2){
14021 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14024 updateSort : function(tree, node){
14025 if(node.childrenRendered){
14026 this.doSort.defer(1, this, [node]);
14031 * Ext JS Library 1.1.1
14032 * Copyright(c) 2006-2007, Ext JS, LLC.
14034 * Originally Released Under LGPL - original licence link has changed is not relivant.
14037 * <script type="text/javascript">
14040 if(Roo.dd.DropZone){
14042 Roo.tree.TreeDropZone = function(tree, config){
14043 this.allowParentInsert = false;
14044 this.allowContainerDrop = false;
14045 this.appendOnly = false;
14046 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14048 this.lastInsertClass = "x-tree-no-status";
14049 this.dragOverData = {};
14052 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14053 ddGroup : "TreeDD",
14056 expandDelay : 1000,
14058 expandNode : function(node){
14059 if(node.hasChildNodes() && !node.isExpanded()){
14060 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14064 queueExpand : function(node){
14065 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14068 cancelExpand : function(){
14069 if(this.expandProcId){
14070 clearTimeout(this.expandProcId);
14071 this.expandProcId = false;
14075 isValidDropPoint : function(n, pt, dd, e, data){
14076 if(!n || !data){ return false; }
14077 var targetNode = n.node;
14078 var dropNode = data.node;
14079 // default drop rules
14080 if(!(targetNode && targetNode.isTarget && pt)){
14083 if(pt == "append" && targetNode.allowChildren === false){
14086 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14089 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14092 // reuse the object
14093 var overEvent = this.dragOverData;
14094 overEvent.tree = this.tree;
14095 overEvent.target = targetNode;
14096 overEvent.data = data;
14097 overEvent.point = pt;
14098 overEvent.source = dd;
14099 overEvent.rawEvent = e;
14100 overEvent.dropNode = dropNode;
14101 overEvent.cancel = false;
14102 var result = this.tree.fireEvent("nodedragover", overEvent);
14103 return overEvent.cancel === false && result !== false;
14106 getDropPoint : function(e, n, dd)
14110 return tn.allowChildren !== false ? "append" : false; // always append for root
14112 var dragEl = n.ddel;
14113 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14114 var y = Roo.lib.Event.getPageY(e);
14115 //var noAppend = tn.allowChildren === false || tn.isLeaf();
14117 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14118 var noAppend = tn.allowChildren === false;
14119 if(this.appendOnly || tn.parentNode.allowChildren === false){
14120 return noAppend ? false : "append";
14122 var noBelow = false;
14123 if(!this.allowParentInsert){
14124 noBelow = tn.hasChildNodes() && tn.isExpanded();
14126 var q = (b - t) / (noAppend ? 2 : 3);
14127 if(y >= t && y < (t + q)){
14129 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14136 onNodeEnter : function(n, dd, e, data)
14138 this.cancelExpand();
14141 onNodeOver : function(n, dd, e, data)
14144 var pt = this.getDropPoint(e, n, dd);
14147 // auto node expand check
14148 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14149 this.queueExpand(node);
14150 }else if(pt != "append"){
14151 this.cancelExpand();
14154 // set the insert point style on the target node
14155 var returnCls = this.dropNotAllowed;
14156 if(this.isValidDropPoint(n, pt, dd, e, data)){
14161 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14162 cls = "x-tree-drag-insert-above";
14163 }else if(pt == "below"){
14164 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14165 cls = "x-tree-drag-insert-below";
14167 returnCls = "x-tree-drop-ok-append";
14168 cls = "x-tree-drag-append";
14170 if(this.lastInsertClass != cls){
14171 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14172 this.lastInsertClass = cls;
14179 onNodeOut : function(n, dd, e, data){
14181 this.cancelExpand();
14182 this.removeDropIndicators(n);
14185 onNodeDrop : function(n, dd, e, data){
14186 var point = this.getDropPoint(e, n, dd);
14187 var targetNode = n.node;
14188 targetNode.ui.startDrop();
14189 if(!this.isValidDropPoint(n, point, dd, e, data)){
14190 targetNode.ui.endDrop();
14193 // first try to find the drop node
14194 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14197 target: targetNode,
14202 dropNode: dropNode,
14205 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14206 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14207 targetNode.ui.endDrop();
14210 // allow target changing
14211 targetNode = dropEvent.target;
14212 if(point == "append" && !targetNode.isExpanded()){
14213 targetNode.expand(false, null, function(){
14214 this.completeDrop(dropEvent);
14215 }.createDelegate(this));
14217 this.completeDrop(dropEvent);
14222 completeDrop : function(de){
14223 var ns = de.dropNode, p = de.point, t = de.target;
14224 if(!(ns instanceof Array)){
14228 for(var i = 0, len = ns.length; i < len; i++){
14231 t.parentNode.insertBefore(n, t);
14232 }else if(p == "below"){
14233 t.parentNode.insertBefore(n, t.nextSibling);
14239 if(this.tree.hlDrop){
14243 this.tree.fireEvent("nodedrop", de);
14246 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14247 if(this.tree.hlDrop){
14248 dropNode.ui.focus();
14249 dropNode.ui.highlight();
14251 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14254 getTree : function(){
14258 removeDropIndicators : function(n){
14261 Roo.fly(el).removeClass([
14262 "x-tree-drag-insert-above",
14263 "x-tree-drag-insert-below",
14264 "x-tree-drag-append"]);
14265 this.lastInsertClass = "_noclass";
14269 beforeDragDrop : function(target, e, id){
14270 this.cancelExpand();
14274 afterRepair : function(data){
14275 if(data && Roo.enableFx){
14276 data.node.ui.highlight();
14286 * Ext JS Library 1.1.1
14287 * Copyright(c) 2006-2007, Ext JS, LLC.
14289 * Originally Released Under LGPL - original licence link has changed is not relivant.
14292 * <script type="text/javascript">
14296 if(Roo.dd.DragZone){
14297 Roo.tree.TreeDragZone = function(tree, config){
14298 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14302 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14303 ddGroup : "TreeDD",
14305 onBeforeDrag : function(data, e){
14307 return n && n.draggable && !n.disabled;
14311 onInitDrag : function(e){
14312 var data = this.dragData;
14313 this.tree.getSelectionModel().select(data.node);
14314 this.proxy.update("");
14315 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14316 this.tree.fireEvent("startdrag", this.tree, data.node, e);
14319 getRepairXY : function(e, data){
14320 return data.node.ui.getDDRepairXY();
14323 onEndDrag : function(data, e){
14324 this.tree.fireEvent("enddrag", this.tree, data.node, e);
14329 onValidDrop : function(dd, e, id){
14330 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14334 beforeInvalidDrop : function(e, id){
14335 // this scrolls the original position back into view
14336 var sm = this.tree.getSelectionModel();
14337 sm.clearSelections();
14338 sm.select(this.dragData.node);
14343 * Ext JS Library 1.1.1
14344 * Copyright(c) 2006-2007, Ext JS, LLC.
14346 * Originally Released Under LGPL - original licence link has changed is not relivant.
14349 * <script type="text/javascript">
14352 * @class Roo.tree.TreeEditor
14353 * @extends Roo.Editor
14354 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
14355 * as the editor field.
14357 * @param {Object} config (used to be the tree panel.)
14358 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14360 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14361 * @cfg {Roo.form.TextField|Object} field The field configuration
14365 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14368 if (oldconfig) { // old style..
14369 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14372 tree = config.tree;
14373 config.field = config.field || {};
14374 config.field.xtype = 'TextField';
14375 field = Roo.factory(config.field, Roo.form);
14377 config = config || {};
14382 * @event beforenodeedit
14383 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
14384 * false from the handler of this event.
14385 * @param {Editor} this
14386 * @param {Roo.tree.Node} node
14388 "beforenodeedit" : true
14392 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14396 tree.on('beforeclick', this.beforeNodeClick, this);
14397 tree.getTreeEl().on('mousedown', this.hide, this);
14398 this.on('complete', this.updateNode, this);
14399 this.on('beforestartedit', this.fitToTree, this);
14400 this.on('startedit', this.bindScroll, this, {delay:10});
14401 this.on('specialkey', this.onSpecialKey, this);
14404 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14406 * @cfg {String} alignment
14407 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14413 * @cfg {Boolean} hideEl
14414 * True to hide the bound element while the editor is displayed (defaults to false)
14418 * @cfg {String} cls
14419 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14421 cls: "x-small-editor x-tree-editor",
14423 * @cfg {Boolean} shim
14424 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14430 * @cfg {Number} maxWidth
14431 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
14432 * the containing tree element's size, it will be automatically limited for you to the container width, taking
14433 * scroll and client offsets into account prior to each edit.
14440 fitToTree : function(ed, el){
14441 var td = this.tree.getTreeEl().dom, nd = el.dom;
14442 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
14443 td.scrollLeft = nd.offsetLeft;
14447 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14448 this.setSize(w, '');
14450 return this.fireEvent('beforenodeedit', this, this.editNode);
14455 triggerEdit : function(node){
14456 this.completeEdit();
14457 this.editNode = node;
14458 this.startEdit(node.ui.textNode, node.text);
14462 bindScroll : function(){
14463 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14467 beforeNodeClick : function(node, e){
14468 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14469 this.lastClick = new Date();
14470 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14472 this.triggerEdit(node);
14479 updateNode : function(ed, value){
14480 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14481 this.editNode.setText(value);
14485 onHide : function(){
14486 Roo.tree.TreeEditor.superclass.onHide.call(this);
14488 this.editNode.ui.focus();
14493 onSpecialKey : function(field, e){
14494 var k = e.getKey();
14498 }else if(k == e.ENTER && !e.hasModifier()){
14500 this.completeEdit();
14503 });//<Script type="text/javascript">
14506 * Ext JS Library 1.1.1
14507 * Copyright(c) 2006-2007, Ext JS, LLC.
14509 * Originally Released Under LGPL - original licence link has changed is not relivant.
14512 * <script type="text/javascript">
14516 * Not documented??? - probably should be...
14519 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14520 //focus: Roo.emptyFn, // prevent odd scrolling behavior
14522 renderElements : function(n, a, targetNode, bulkRender){
14523 //consel.log("renderElements?");
14524 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14526 var t = n.getOwnerTree();
14527 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14529 var cols = t.columns;
14530 var bw = t.borderWidth;
14532 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14533 var cb = typeof a.checked == "boolean";
14534 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14535 var colcls = 'x-t-' + tid + '-c0';
14537 '<li class="x-tree-node">',
14540 '<div class="x-tree-node-el ', a.cls,'">',
14542 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14545 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14546 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
14547 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14548 (a.icon ? ' x-tree-node-inline-icon' : ''),
14549 (a.iconCls ? ' '+a.iconCls : ''),
14550 '" unselectable="on" />',
14551 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
14552 (a.checked ? 'checked="checked" />' : ' />')) : ''),
14554 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14555 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14556 '<span unselectable="on" qtip="' + tx + '">',
14560 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14561 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14563 for(var i = 1, len = cols.length; i < len; i++){
14565 colcls = 'x-t-' + tid + '-c' +i;
14566 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14567 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14568 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14574 '<div class="x-clear"></div></div>',
14575 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14578 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14579 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14580 n.nextSibling.ui.getEl(), buf.join(""));
14582 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14584 var el = this.wrap.firstChild;
14586 this.elNode = el.firstChild;
14587 this.ranchor = el.childNodes[1];
14588 this.ctNode = this.wrap.childNodes[1];
14589 var cs = el.firstChild.childNodes;
14590 this.indentNode = cs[0];
14591 this.ecNode = cs[1];
14592 this.iconNode = cs[2];
14595 this.checkbox = cs[3];
14598 this.anchor = cs[index];
14600 this.textNode = cs[index].firstChild;
14602 //el.on("click", this.onClick, this);
14603 //el.on("dblclick", this.onDblClick, this);
14606 // console.log(this);
14608 initEvents : function(){
14609 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14612 var a = this.ranchor;
14614 var el = Roo.get(a);
14616 if(Roo.isOpera){ // opera render bug ignores the CSS
14617 el.setStyle("text-decoration", "none");
14620 el.on("click", this.onClick, this);
14621 el.on("dblclick", this.onDblClick, this);
14622 el.on("contextmenu", this.onContextMenu, this);
14626 /*onSelectedChange : function(state){
14629 this.addClass("x-tree-selected");
14632 this.removeClass("x-tree-selected");
14635 addClass : function(cls){
14637 Roo.fly(this.elRow).addClass(cls);
14643 removeClass : function(cls){
14645 Roo.fly(this.elRow).removeClass(cls);
14651 });//<Script type="text/javascript">
14655 * Ext JS Library 1.1.1
14656 * Copyright(c) 2006-2007, Ext JS, LLC.
14658 * Originally Released Under LGPL - original licence link has changed is not relivant.
14661 * <script type="text/javascript">
14666 * @class Roo.tree.ColumnTree
14667 * @extends Roo.data.TreePanel
14668 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
14669 * @cfg {int} borderWidth compined right/left border allowance
14671 * @param {String/HTMLElement/Element} el The container element
14672 * @param {Object} config
14674 Roo.tree.ColumnTree = function(el, config)
14676 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14680 * Fire this event on a container when it resizes
14681 * @param {int} w Width
14682 * @param {int} h Height
14686 this.on('resize', this.onResize, this);
14689 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14693 borderWidth: Roo.isBorderBox ? 0 : 2,
14696 render : function(){
14697 // add the header.....
14699 Roo.tree.ColumnTree.superclass.render.apply(this);
14701 this.el.addClass('x-column-tree');
14703 this.headers = this.el.createChild(
14704 {cls:'x-tree-headers'},this.innerCt.dom);
14706 var cols = this.columns, c;
14707 var totalWidth = 0;
14709 var len = cols.length;
14710 for(var i = 0; i < len; i++){
14712 totalWidth += c.width;
14713 this.headEls.push(this.headers.createChild({
14714 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14716 cls:'x-tree-hd-text',
14719 style:'width:'+(c.width-this.borderWidth)+'px;'
14722 this.headers.createChild({cls:'x-clear'});
14723 // prevent floats from wrapping when clipped
14724 this.headers.setWidth(totalWidth);
14725 //this.innerCt.setWidth(totalWidth);
14726 this.innerCt.setStyle({ overflow: 'auto' });
14727 this.onResize(this.width, this.height);
14731 onResize : function(w,h)
14736 this.innerCt.setWidth(this.width);
14737 this.innerCt.setHeight(this.height-20);
14740 var cols = this.columns, c;
14741 var totalWidth = 0;
14743 var len = cols.length;
14744 for(var i = 0; i < len; i++){
14746 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14747 // it's the expander..
14748 expEl = this.headEls[i];
14751 totalWidth += c.width;
14755 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
14757 this.headers.setWidth(w-20);
14766 * Ext JS Library 1.1.1
14767 * Copyright(c) 2006-2007, Ext JS, LLC.
14769 * Originally Released Under LGPL - original licence link has changed is not relivant.
14772 * <script type="text/javascript">
14776 * @class Roo.menu.Menu
14777 * @extends Roo.util.Observable
14778 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
14779 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14781 * Creates a new Menu
14782 * @param {Object} config Configuration options
14784 Roo.menu.Menu = function(config){
14786 Roo.menu.Menu.superclass.constructor.call(this, config);
14788 this.id = this.id || Roo.id();
14791 * @event beforeshow
14792 * Fires before this menu is displayed
14793 * @param {Roo.menu.Menu} this
14797 * @event beforehide
14798 * Fires before this menu is hidden
14799 * @param {Roo.menu.Menu} this
14804 * Fires after this menu is displayed
14805 * @param {Roo.menu.Menu} this
14810 * Fires after this menu is hidden
14811 * @param {Roo.menu.Menu} this
14816 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14817 * @param {Roo.menu.Menu} this
14818 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14819 * @param {Roo.EventObject} e
14824 * Fires when the mouse is hovering over this menu
14825 * @param {Roo.menu.Menu} this
14826 * @param {Roo.EventObject} e
14827 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14832 * Fires when the mouse exits this menu
14833 * @param {Roo.menu.Menu} this
14834 * @param {Roo.EventObject} e
14835 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14840 * Fires when a menu item contained in this menu is clicked
14841 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14842 * @param {Roo.EventObject} e
14846 if (this.registerMenu) {
14847 Roo.menu.MenuMgr.register(this);
14850 var mis = this.items;
14851 this.items = new Roo.util.MixedCollection();
14853 this.add.apply(this, mis);
14857 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14859 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14863 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14864 * for bottom-right shadow (defaults to "sides")
14868 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14869 * this menu (defaults to "tl-tr?")
14871 subMenuAlign : "tl-tr?",
14873 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14874 * relative to its element of origin (defaults to "tl-bl?")
14876 defaultAlign : "tl-bl?",
14878 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14880 allowOtherMenus : false,
14882 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14884 registerMenu : true,
14889 render : function(){
14893 var el = this.el = new Roo.Layer({
14895 shadow:this.shadow,
14897 parentEl: this.parentEl || document.body,
14901 this.keyNav = new Roo.menu.MenuNav(this);
14904 el.addClass("x-menu-plain");
14907 el.addClass(this.cls);
14909 // generic focus element
14910 this.focusEl = el.createChild({
14911 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14913 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14914 //disabling touch- as it's causing issues ..
14915 //ul.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
14916 ul.on('click' , this.onClick, this);
14919 ul.on("mouseover", this.onMouseOver, this);
14920 ul.on("mouseout", this.onMouseOut, this);
14921 this.items.each(function(item){
14926 var li = document.createElement("li");
14927 li.className = "x-menu-list-item";
14928 ul.dom.appendChild(li);
14929 item.render(li, this);
14936 autoWidth : function(){
14937 var el = this.el, ul = this.ul;
14941 var w = this.width;
14944 }else if(Roo.isIE){
14945 el.setWidth(this.minWidth);
14946 var t = el.dom.offsetWidth; // force recalc
14947 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14952 delayAutoWidth : function(){
14955 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14957 this.awTask.delay(20);
14962 findTargetItem : function(e){
14963 var t = e.getTarget(".x-menu-list-item", this.ul, true);
14964 if(t && t.menuItemId){
14965 return this.items.get(t.menuItemId);
14970 onClick : function(e){
14971 Roo.log("menu.onClick");
14972 var t = this.findTargetItem(e);
14977 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
14978 if(t == this.activeItem && t.shouldDeactivate(e)){
14979 this.activeItem.deactivate();
14980 delete this.activeItem;
14984 this.setActiveItem(t, true);
14992 this.fireEvent("click", this, t, e);
14996 setActiveItem : function(item, autoExpand){
14997 if(item != this.activeItem){
14998 if(this.activeItem){
14999 this.activeItem.deactivate();
15001 this.activeItem = item;
15002 item.activate(autoExpand);
15003 }else if(autoExpand){
15009 tryActivate : function(start, step){
15010 var items = this.items;
15011 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
15012 var item = items.get(i);
15013 if(!item.disabled && item.canActivate){
15014 this.setActiveItem(item, false);
15022 onMouseOver : function(e){
15024 if(t = this.findTargetItem(e)){
15025 if(t.canActivate && !t.disabled){
15026 this.setActiveItem(t, true);
15029 this.fireEvent("mouseover", this, e, t);
15033 onMouseOut : function(e){
15035 if(t = this.findTargetItem(e)){
15036 if(t == this.activeItem && t.shouldDeactivate(e)){
15037 this.activeItem.deactivate();
15038 delete this.activeItem;
15041 this.fireEvent("mouseout", this, e, t);
15045 * Read-only. Returns true if the menu is currently displayed, else false.
15048 isVisible : function(){
15049 return this.el && !this.hidden;
15053 * Displays this menu relative to another element
15054 * @param {String/HTMLElement/Roo.Element} element The element to align to
15055 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15056 * the element (defaults to this.defaultAlign)
15057 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15059 show : function(el, pos, parentMenu){
15060 this.parentMenu = parentMenu;
15064 this.fireEvent("beforeshow", this);
15065 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15069 * Displays this menu at a specific xy position
15070 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15071 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15073 showAt : function(xy, parentMenu, /* private: */_e){
15074 this.parentMenu = parentMenu;
15079 this.fireEvent("beforeshow", this);
15080 xy = this.el.adjustForConstraints(xy);
15084 this.hidden = false;
15086 this.fireEvent("show", this);
15089 focus : function(){
15091 this.doFocus.defer(50, this);
15095 doFocus : function(){
15097 this.focusEl.focus();
15102 * Hides this menu and optionally all parent menus
15103 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15105 hide : function(deep){
15106 if(this.el && this.isVisible()){
15107 this.fireEvent("beforehide", this);
15108 if(this.activeItem){
15109 this.activeItem.deactivate();
15110 this.activeItem = null;
15113 this.hidden = true;
15114 this.fireEvent("hide", this);
15116 if(deep === true && this.parentMenu){
15117 this.parentMenu.hide(true);
15122 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15123 * Any of the following are valid:
15125 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15126 * <li>An HTMLElement object which will be converted to a menu item</li>
15127 * <li>A menu item config object that will be created as a new menu item</li>
15128 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15129 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15134 var menu = new Roo.menu.Menu();
15136 // Create a menu item to add by reference
15137 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15139 // Add a bunch of items at once using different methods.
15140 // Only the last item added will be returned.
15141 var item = menu.add(
15142 menuItem, // add existing item by ref
15143 'Dynamic Item', // new TextItem
15144 '-', // new separator
15145 { text: 'Config Item' } // new item by config
15148 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15149 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15152 var a = arguments, l = a.length, item;
15153 for(var i = 0; i < l; i++){
15155 if ((typeof(el) == "object") && el.xtype && el.xns) {
15156 el = Roo.factory(el, Roo.menu);
15159 if(el.render){ // some kind of Item
15160 item = this.addItem(el);
15161 }else if(typeof el == "string"){ // string
15162 if(el == "separator" || el == "-"){
15163 item = this.addSeparator();
15165 item = this.addText(el);
15167 }else if(el.tagName || el.el){ // element
15168 item = this.addElement(el);
15169 }else if(typeof el == "object"){ // must be menu item config?
15170 item = this.addMenuItem(el);
15177 * Returns this menu's underlying {@link Roo.Element} object
15178 * @return {Roo.Element} The element
15180 getEl : function(){
15188 * Adds a separator bar to the menu
15189 * @return {Roo.menu.Item} The menu item that was added
15191 addSeparator : function(){
15192 return this.addItem(new Roo.menu.Separator());
15196 * Adds an {@link Roo.Element} object to the menu
15197 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15198 * @return {Roo.menu.Item} The menu item that was added
15200 addElement : function(el){
15201 return this.addItem(new Roo.menu.BaseItem(el));
15205 * Adds an existing object based on {@link Roo.menu.Item} to the menu
15206 * @param {Roo.menu.Item} item The menu item to add
15207 * @return {Roo.menu.Item} The menu item that was added
15209 addItem : function(item){
15210 this.items.add(item);
15212 var li = document.createElement("li");
15213 li.className = "x-menu-list-item";
15214 this.ul.dom.appendChild(li);
15215 item.render(li, this);
15216 this.delayAutoWidth();
15222 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15223 * @param {Object} config A MenuItem config object
15224 * @return {Roo.menu.Item} The menu item that was added
15226 addMenuItem : function(config){
15227 if(!(config instanceof Roo.menu.Item)){
15228 if(typeof config.checked == "boolean"){ // must be check menu item config?
15229 config = new Roo.menu.CheckItem(config);
15231 config = new Roo.menu.Item(config);
15234 return this.addItem(config);
15238 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15239 * @param {String} text The text to display in the menu item
15240 * @return {Roo.menu.Item} The menu item that was added
15242 addText : function(text){
15243 return this.addItem(new Roo.menu.TextItem({ text : text }));
15247 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15248 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15249 * @param {Roo.menu.Item} item The menu item to add
15250 * @return {Roo.menu.Item} The menu item that was added
15252 insert : function(index, item){
15253 this.items.insert(index, item);
15255 var li = document.createElement("li");
15256 li.className = "x-menu-list-item";
15257 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15258 item.render(li, this);
15259 this.delayAutoWidth();
15265 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15266 * @param {Roo.menu.Item} item The menu item to remove
15268 remove : function(item){
15269 this.items.removeKey(item.id);
15274 * Removes and destroys all items in the menu
15276 removeAll : function(){
15278 while(f = this.items.first()){
15284 // MenuNav is a private utility class used internally by the Menu
15285 Roo.menu.MenuNav = function(menu){
15286 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15287 this.scope = this.menu = menu;
15290 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15291 doRelay : function(e, h){
15292 var k = e.getKey();
15293 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15294 this.menu.tryActivate(0, 1);
15297 return h.call(this.scope || this, e, this.menu);
15300 up : function(e, m){
15301 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15302 m.tryActivate(m.items.length-1, -1);
15306 down : function(e, m){
15307 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15308 m.tryActivate(0, 1);
15312 right : function(e, m){
15314 m.activeItem.expandMenu(true);
15318 left : function(e, m){
15320 if(m.parentMenu && m.parentMenu.activeItem){
15321 m.parentMenu.activeItem.activate();
15325 enter : function(e, m){
15327 e.stopPropagation();
15328 m.activeItem.onClick(e);
15329 m.fireEvent("click", this, m.activeItem);
15335 * Ext JS Library 1.1.1
15336 * Copyright(c) 2006-2007, Ext JS, LLC.
15338 * Originally Released Under LGPL - original licence link has changed is not relivant.
15341 * <script type="text/javascript">
15345 * @class Roo.menu.MenuMgr
15346 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15349 Roo.menu.MenuMgr = function(){
15350 var menus, active, groups = {}, attached = false, lastShow = new Date();
15352 // private - called when first menu is created
15355 active = new Roo.util.MixedCollection();
15356 Roo.get(document).addKeyListener(27, function(){
15357 if(active.length > 0){
15364 function hideAll(){
15365 if(active && active.length > 0){
15366 var c = active.clone();
15367 c.each(function(m){
15374 function onHide(m){
15376 if(active.length < 1){
15377 Roo.get(document).un("mousedown", onMouseDown);
15383 function onShow(m){
15384 var last = active.last();
15385 lastShow = new Date();
15388 Roo.get(document).on("mousedown", onMouseDown);
15392 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15393 m.parentMenu.activeChild = m;
15394 }else if(last && last.isVisible()){
15395 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15400 function onBeforeHide(m){
15402 m.activeChild.hide();
15404 if(m.autoHideTimer){
15405 clearTimeout(m.autoHideTimer);
15406 delete m.autoHideTimer;
15411 function onBeforeShow(m){
15412 var pm = m.parentMenu;
15413 if(!pm && !m.allowOtherMenus){
15415 }else if(pm && pm.activeChild && active != m){
15416 pm.activeChild.hide();
15421 function onMouseDown(e){
15422 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15428 function onBeforeCheck(mi, state){
15430 var g = groups[mi.group];
15431 for(var i = 0, l = g.length; i < l; i++){
15433 g[i].setChecked(false);
15442 * Hides all menus that are currently visible
15444 hideAll : function(){
15449 register : function(menu){
15453 menus[menu.id] = menu;
15454 menu.on("beforehide", onBeforeHide);
15455 menu.on("hide", onHide);
15456 menu.on("beforeshow", onBeforeShow);
15457 menu.on("show", onShow);
15458 var g = menu.group;
15459 if(g && menu.events["checkchange"]){
15463 groups[g].push(menu);
15464 menu.on("checkchange", onCheck);
15469 * Returns a {@link Roo.menu.Menu} object
15470 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15471 * be used to generate and return a new Menu instance.
15473 get : function(menu){
15474 if(typeof menu == "string"){ // menu id
15475 return menus[menu];
15476 }else if(menu.events){ // menu instance
15478 }else if(typeof menu.length == 'number'){ // array of menu items?
15479 return new Roo.menu.Menu({items:menu});
15480 }else{ // otherwise, must be a config
15481 return new Roo.menu.Menu(menu);
15486 unregister : function(menu){
15487 delete menus[menu.id];
15488 menu.un("beforehide", onBeforeHide);
15489 menu.un("hide", onHide);
15490 menu.un("beforeshow", onBeforeShow);
15491 menu.un("show", onShow);
15492 var g = menu.group;
15493 if(g && menu.events["checkchange"]){
15494 groups[g].remove(menu);
15495 menu.un("checkchange", onCheck);
15500 registerCheckable : function(menuItem){
15501 var g = menuItem.group;
15506 groups[g].push(menuItem);
15507 menuItem.on("beforecheckchange", onBeforeCheck);
15512 unregisterCheckable : function(menuItem){
15513 var g = menuItem.group;
15515 groups[g].remove(menuItem);
15516 menuItem.un("beforecheckchange", onBeforeCheck);
15522 * Ext JS Library 1.1.1
15523 * Copyright(c) 2006-2007, Ext JS, LLC.
15525 * Originally Released Under LGPL - original licence link has changed is not relivant.
15528 * <script type="text/javascript">
15533 * @class Roo.menu.BaseItem
15534 * @extends Roo.Component
15535 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
15536 * management and base configuration options shared by all menu components.
15538 * Creates a new BaseItem
15539 * @param {Object} config Configuration options
15541 Roo.menu.BaseItem = function(config){
15542 Roo.menu.BaseItem.superclass.constructor.call(this, config);
15547 * Fires when this item is clicked
15548 * @param {Roo.menu.BaseItem} this
15549 * @param {Roo.EventObject} e
15554 * Fires when this item is activated
15555 * @param {Roo.menu.BaseItem} this
15559 * @event deactivate
15560 * Fires when this item is deactivated
15561 * @param {Roo.menu.BaseItem} this
15567 this.on("click", this.handler, this.scope, true);
15571 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15573 * @cfg {Function} handler
15574 * A function that will handle the click event of this menu item (defaults to undefined)
15577 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15579 canActivate : false,
15582 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15587 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15589 activeClass : "x-menu-item-active",
15591 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15593 hideOnClick : true,
15595 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15600 ctype: "Roo.menu.BaseItem",
15603 actionMode : "container",
15606 render : function(container, parentMenu){
15607 this.parentMenu = parentMenu;
15608 Roo.menu.BaseItem.superclass.render.call(this, container);
15609 this.container.menuItemId = this.id;
15613 onRender : function(container, position){
15614 this.el = Roo.get(this.el);
15615 container.dom.appendChild(this.el.dom);
15619 onClick : function(e){
15620 if(!this.disabled && this.fireEvent("click", this, e) !== false
15621 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15622 this.handleClick(e);
15629 activate : function(){
15633 var li = this.container;
15634 li.addClass(this.activeClass);
15635 this.region = li.getRegion().adjust(2, 2, -2, -2);
15636 this.fireEvent("activate", this);
15641 deactivate : function(){
15642 this.container.removeClass(this.activeClass);
15643 this.fireEvent("deactivate", this);
15647 shouldDeactivate : function(e){
15648 return !this.region || !this.region.contains(e.getPoint());
15652 handleClick : function(e){
15653 if(this.hideOnClick){
15654 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15659 expandMenu : function(autoActivate){
15664 hideMenu : function(){
15669 * Ext JS Library 1.1.1
15670 * Copyright(c) 2006-2007, Ext JS, LLC.
15672 * Originally Released Under LGPL - original licence link has changed is not relivant.
15675 * <script type="text/javascript">
15679 * @class Roo.menu.Adapter
15680 * @extends Roo.menu.BaseItem
15681 * 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.
15682 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15684 * Creates a new Adapter
15685 * @param {Object} config Configuration options
15687 Roo.menu.Adapter = function(component, config){
15688 Roo.menu.Adapter.superclass.constructor.call(this, config);
15689 this.component = component;
15691 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15693 canActivate : true,
15696 onRender : function(container, position){
15697 this.component.render(container);
15698 this.el = this.component.getEl();
15702 activate : function(){
15706 this.component.focus();
15707 this.fireEvent("activate", this);
15712 deactivate : function(){
15713 this.fireEvent("deactivate", this);
15717 disable : function(){
15718 this.component.disable();
15719 Roo.menu.Adapter.superclass.disable.call(this);
15723 enable : function(){
15724 this.component.enable();
15725 Roo.menu.Adapter.superclass.enable.call(this);
15729 * Ext JS Library 1.1.1
15730 * Copyright(c) 2006-2007, Ext JS, LLC.
15732 * Originally Released Under LGPL - original licence link has changed is not relivant.
15735 * <script type="text/javascript">
15739 * @class Roo.menu.TextItem
15740 * @extends Roo.menu.BaseItem
15741 * Adds a static text string to a menu, usually used as either a heading or group separator.
15742 * Note: old style constructor with text is still supported.
15745 * Creates a new TextItem
15746 * @param {Object} cfg Configuration
15748 Roo.menu.TextItem = function(cfg){
15749 if (typeof(cfg) == 'string') {
15752 Roo.apply(this,cfg);
15755 Roo.menu.TextItem.superclass.constructor.call(this);
15758 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15760 * @cfg {Boolean} text Text to show on item.
15765 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15767 hideOnClick : false,
15769 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15771 itemCls : "x-menu-text",
15774 onRender : function(){
15775 var s = document.createElement("span");
15776 s.className = this.itemCls;
15777 s.innerHTML = this.text;
15779 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15783 * Ext JS Library 1.1.1
15784 * Copyright(c) 2006-2007, Ext JS, LLC.
15786 * Originally Released Under LGPL - original licence link has changed is not relivant.
15789 * <script type="text/javascript">
15793 * @class Roo.menu.Separator
15794 * @extends Roo.menu.BaseItem
15795 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15796 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15798 * @param {Object} config Configuration options
15800 Roo.menu.Separator = function(config){
15801 Roo.menu.Separator.superclass.constructor.call(this, config);
15804 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15806 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15808 itemCls : "x-menu-sep",
15810 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15812 hideOnClick : false,
15815 onRender : function(li){
15816 var s = document.createElement("span");
15817 s.className = this.itemCls;
15818 s.innerHTML = " ";
15820 li.addClass("x-menu-sep-li");
15821 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15825 * Ext JS Library 1.1.1
15826 * Copyright(c) 2006-2007, Ext JS, LLC.
15828 * Originally Released Under LGPL - original licence link has changed is not relivant.
15831 * <script type="text/javascript">
15834 * @class Roo.menu.Item
15835 * @extends Roo.menu.BaseItem
15836 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15837 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15838 * activation and click handling.
15840 * Creates a new Item
15841 * @param {Object} config Configuration options
15843 Roo.menu.Item = function(config){
15844 Roo.menu.Item.superclass.constructor.call(this, config);
15846 this.menu = Roo.menu.MenuMgr.get(this.menu);
15849 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15852 * @cfg {String} text
15853 * The text to show on the menu item.
15857 * @cfg {String} HTML to render in menu
15858 * The text to show on the menu item (HTML version).
15862 * @cfg {String} icon
15863 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15867 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15869 itemCls : "x-menu-item",
15871 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15873 canActivate : true,
15875 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15878 // doc'd in BaseItem
15882 ctype: "Roo.menu.Item",
15885 onRender : function(container, position){
15886 var el = document.createElement("a");
15887 el.hideFocus = true;
15888 el.unselectable = "on";
15889 el.href = this.href || "#";
15890 if(this.hrefTarget){
15891 el.target = this.hrefTarget;
15893 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
15895 var html = this.html.length ? this.html : String.format('{0}',this.text);
15897 el.innerHTML = String.format(
15898 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15899 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15901 Roo.menu.Item.superclass.onRender.call(this, container, position);
15905 * Sets the text to display in this menu item
15906 * @param {String} text The text to display
15907 * @param {Boolean} isHTML true to indicate text is pure html.
15909 setText : function(text, isHTML){
15917 var html = this.html.length ? this.html : String.format('{0}',this.text);
15919 this.el.update(String.format(
15920 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15921 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15922 this.parentMenu.autoWidth();
15927 handleClick : function(e){
15928 if(!this.href){ // if no link defined, stop the event automatically
15931 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15935 activate : function(autoExpand){
15936 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15946 shouldDeactivate : function(e){
15947 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15948 if(this.menu && this.menu.isVisible()){
15949 return !this.menu.getEl().getRegion().contains(e.getPoint());
15957 deactivate : function(){
15958 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15963 expandMenu : function(autoActivate){
15964 if(!this.disabled && this.menu){
15965 clearTimeout(this.hideTimer);
15966 delete this.hideTimer;
15967 if(!this.menu.isVisible() && !this.showTimer){
15968 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15969 }else if (this.menu.isVisible() && autoActivate){
15970 this.menu.tryActivate(0, 1);
15976 deferExpand : function(autoActivate){
15977 delete this.showTimer;
15978 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15980 this.menu.tryActivate(0, 1);
15985 hideMenu : function(){
15986 clearTimeout(this.showTimer);
15987 delete this.showTimer;
15988 if(!this.hideTimer && this.menu && this.menu.isVisible()){
15989 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15994 deferHide : function(){
15995 delete this.hideTimer;
16000 * Ext JS Library 1.1.1
16001 * Copyright(c) 2006-2007, Ext JS, LLC.
16003 * Originally Released Under LGPL - original licence link has changed is not relivant.
16006 * <script type="text/javascript">
16010 * @class Roo.menu.CheckItem
16011 * @extends Roo.menu.Item
16012 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
16014 * Creates a new CheckItem
16015 * @param {Object} config Configuration options
16017 Roo.menu.CheckItem = function(config){
16018 Roo.menu.CheckItem.superclass.constructor.call(this, config);
16021 * @event beforecheckchange
16022 * Fires before the checked value is set, providing an opportunity to cancel if needed
16023 * @param {Roo.menu.CheckItem} this
16024 * @param {Boolean} checked The new checked value that will be set
16026 "beforecheckchange" : true,
16028 * @event checkchange
16029 * Fires after the checked value has been set
16030 * @param {Roo.menu.CheckItem} this
16031 * @param {Boolean} checked The checked value that was set
16033 "checkchange" : true
16035 if(this.checkHandler){
16036 this.on('checkchange', this.checkHandler, this.scope);
16039 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16041 * @cfg {String} group
16042 * All check items with the same group name will automatically be grouped into a single-select
16043 * radio button group (defaults to '')
16046 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16048 itemCls : "x-menu-item x-menu-check-item",
16050 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16052 groupClass : "x-menu-group-item",
16055 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
16056 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16057 * initialized with checked = true will be rendered as checked.
16062 ctype: "Roo.menu.CheckItem",
16065 onRender : function(c){
16066 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16068 this.el.addClass(this.groupClass);
16070 Roo.menu.MenuMgr.registerCheckable(this);
16072 this.checked = false;
16073 this.setChecked(true, true);
16078 destroy : function(){
16080 Roo.menu.MenuMgr.unregisterCheckable(this);
16082 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16086 * Set the checked state of this item
16087 * @param {Boolean} checked The new checked value
16088 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16090 setChecked : function(state, suppressEvent){
16091 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16092 if(this.container){
16093 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16095 this.checked = state;
16096 if(suppressEvent !== true){
16097 this.fireEvent("checkchange", this, state);
16103 handleClick : function(e){
16104 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16105 this.setChecked(!this.checked);
16107 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16111 * Ext JS Library 1.1.1
16112 * Copyright(c) 2006-2007, Ext JS, LLC.
16114 * Originally Released Under LGPL - original licence link has changed is not relivant.
16117 * <script type="text/javascript">
16121 * @class Roo.menu.DateItem
16122 * @extends Roo.menu.Adapter
16123 * A menu item that wraps the {@link Roo.DatPicker} component.
16125 * Creates a new DateItem
16126 * @param {Object} config Configuration options
16128 Roo.menu.DateItem = function(config){
16129 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16130 /** The Roo.DatePicker object @type Roo.DatePicker */
16131 this.picker = this.component;
16132 this.addEvents({select: true});
16134 this.picker.on("render", function(picker){
16135 picker.getEl().swallowEvent("click");
16136 picker.container.addClass("x-menu-date-item");
16139 this.picker.on("select", this.onSelect, this);
16142 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16144 onSelect : function(picker, date){
16145 this.fireEvent("select", this, date, picker);
16146 Roo.menu.DateItem.superclass.handleClick.call(this);
16150 * Ext JS Library 1.1.1
16151 * Copyright(c) 2006-2007, Ext JS, LLC.
16153 * Originally Released Under LGPL - original licence link has changed is not relivant.
16156 * <script type="text/javascript">
16160 * @class Roo.menu.ColorItem
16161 * @extends Roo.menu.Adapter
16162 * A menu item that wraps the {@link Roo.ColorPalette} component.
16164 * Creates a new ColorItem
16165 * @param {Object} config Configuration options
16167 Roo.menu.ColorItem = function(config){
16168 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16169 /** The Roo.ColorPalette object @type Roo.ColorPalette */
16170 this.palette = this.component;
16171 this.relayEvents(this.palette, ["select"]);
16172 if(this.selectHandler){
16173 this.on('select', this.selectHandler, this.scope);
16176 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16178 * Ext JS Library 1.1.1
16179 * Copyright(c) 2006-2007, Ext JS, LLC.
16181 * Originally Released Under LGPL - original licence link has changed is not relivant.
16184 * <script type="text/javascript">
16189 * @class Roo.menu.DateMenu
16190 * @extends Roo.menu.Menu
16191 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16193 * Creates a new DateMenu
16194 * @param {Object} config Configuration options
16196 Roo.menu.DateMenu = function(config){
16197 Roo.menu.DateMenu.superclass.constructor.call(this, config);
16199 var di = new Roo.menu.DateItem(config);
16202 * The {@link Roo.DatePicker} instance for this DateMenu
16205 this.picker = di.picker;
16208 * @param {DatePicker} picker
16209 * @param {Date} date
16211 this.relayEvents(di, ["select"]);
16212 this.on('beforeshow', function(){
16214 this.picker.hideMonthPicker(false);
16218 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16222 * Ext JS Library 1.1.1
16223 * Copyright(c) 2006-2007, Ext JS, LLC.
16225 * Originally Released Under LGPL - original licence link has changed is not relivant.
16228 * <script type="text/javascript">
16233 * @class Roo.menu.ColorMenu
16234 * @extends Roo.menu.Menu
16235 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16237 * Creates a new ColorMenu
16238 * @param {Object} config Configuration options
16240 Roo.menu.ColorMenu = function(config){
16241 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16243 var ci = new Roo.menu.ColorItem(config);
16246 * The {@link Roo.ColorPalette} instance for this ColorMenu
16247 * @type ColorPalette
16249 this.palette = ci.palette;
16252 * @param {ColorPalette} palette
16253 * @param {String} color
16255 this.relayEvents(ci, ["select"]);
16257 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16259 * Ext JS Library 1.1.1
16260 * Copyright(c) 2006-2007, Ext JS, LLC.
16262 * Originally Released Under LGPL - original licence link has changed is not relivant.
16265 * <script type="text/javascript">
16269 * @class Roo.form.TextItem
16270 * @extends Roo.BoxComponent
16271 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16273 * Creates a new TextItem
16274 * @param {Object} config Configuration options
16276 Roo.form.TextItem = function(config){
16277 Roo.form.TextItem.superclass.constructor.call(this, config);
16280 Roo.extend(Roo.form.TextItem, Roo.BoxComponent, {
16283 * @cfg {String} tag the tag for this item (default div)
16287 * @cfg {String} html the content for this item
16291 getAutoCreate : function()
16304 onRender : function(ct, position)
16306 Roo.form.TextItem.superclass.onRender.call(this, ct, position);
16309 var cfg = this.getAutoCreate();
16311 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16313 if (!cfg.name.length) {
16316 this.el = ct.createChild(cfg, position);
16322 * Ext JS Library 1.1.1
16323 * Copyright(c) 2006-2007, Ext JS, LLC.
16325 * Originally Released Under LGPL - original licence link has changed is not relivant.
16328 * <script type="text/javascript">
16332 * @class Roo.form.Field
16333 * @extends Roo.BoxComponent
16334 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16336 * Creates a new Field
16337 * @param {Object} config Configuration options
16339 Roo.form.Field = function(config){
16340 Roo.form.Field.superclass.constructor.call(this, config);
16343 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
16345 * @cfg {String} fieldLabel Label to use when rendering a form.
16348 * @cfg {String} qtip Mouse over tip
16352 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16354 invalidClass : "x-form-invalid",
16356 * @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")
16358 invalidText : "The value in this field is invalid",
16360 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16362 focusClass : "x-form-focus",
16364 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16365 automatic validation (defaults to "keyup").
16367 validationEvent : "keyup",
16369 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16371 validateOnBlur : true,
16373 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16375 validationDelay : 250,
16377 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16378 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16380 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16382 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16384 fieldClass : "x-form-field",
16386 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
16389 ----------- ----------------------------------------------------------------------
16390 qtip Display a quick tip when the user hovers over the field
16391 title Display a default browser title attribute popup
16392 under Add a block div beneath the field containing the error text
16393 side Add an error icon to the right of the field with a popup on hover
16394 [element id] Add the error text directly to the innerHTML of the specified element
16397 msgTarget : 'qtip',
16399 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16404 * @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.
16409 * @cfg {Boolean} disabled True to disable the field (defaults to false).
16414 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16416 inputType : undefined,
16419 * @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).
16421 tabIndex : undefined,
16424 isFormField : true,
16429 * @property {Roo.Element} fieldEl
16430 * Element Containing the rendered Field (with label etc.)
16433 * @cfg {Mixed} value A value to initialize this field with.
16438 * @cfg {String} name The field's HTML name attribute.
16441 * @cfg {String} cls A CSS class to apply to the field's underlying element.
16444 loadedValue : false,
16448 initComponent : function(){
16449 Roo.form.Field.superclass.initComponent.call(this);
16453 * Fires when this field receives input focus.
16454 * @param {Roo.form.Field} this
16459 * Fires when this field loses input focus.
16460 * @param {Roo.form.Field} this
16464 * @event specialkey
16465 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
16466 * {@link Roo.EventObject#getKey} to determine which key was pressed.
16467 * @param {Roo.form.Field} this
16468 * @param {Roo.EventObject} e The event object
16473 * Fires just before the field blurs if the field value has changed.
16474 * @param {Roo.form.Field} this
16475 * @param {Mixed} newValue The new value
16476 * @param {Mixed} oldValue The original value
16481 * Fires after the field has been marked as invalid.
16482 * @param {Roo.form.Field} this
16483 * @param {String} msg The validation message
16488 * Fires after the field has been validated with no errors.
16489 * @param {Roo.form.Field} this
16494 * Fires after the key up
16495 * @param {Roo.form.Field} this
16496 * @param {Roo.EventObject} e The event Object
16503 * Returns the name attribute of the field if available
16504 * @return {String} name The field name
16506 getName: function(){
16507 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16511 onRender : function(ct, position){
16512 Roo.form.Field.superclass.onRender.call(this, ct, position);
16514 var cfg = this.getAutoCreate();
16516 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16518 if (!cfg.name.length) {
16521 if(this.inputType){
16522 cfg.type = this.inputType;
16524 this.el = ct.createChild(cfg, position);
16526 var type = this.el.dom.type;
16528 if(type == 'password'){
16531 this.el.addClass('x-form-'+type);
16534 this.el.dom.readOnly = true;
16536 if(this.tabIndex !== undefined){
16537 this.el.dom.setAttribute('tabIndex', this.tabIndex);
16540 this.el.addClass([this.fieldClass, this.cls]);
16545 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16546 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16547 * @return {Roo.form.Field} this
16549 applyTo : function(target){
16550 this.allowDomMove = false;
16551 this.el = Roo.get(target);
16552 this.render(this.el.dom.parentNode);
16557 initValue : function(){
16558 if(this.value !== undefined){
16559 this.setValue(this.value);
16560 }else if(this.el.dom.value.length > 0){
16561 this.setValue(this.el.dom.value);
16566 * Returns true if this field has been changed since it was originally loaded and is not disabled.
16567 * DEPRICATED - it never worked well - use hasChanged/resetHasChanged.
16569 isDirty : function() {
16570 if(this.disabled) {
16573 return String(this.getValue()) !== String(this.originalValue);
16577 * stores the current value in loadedValue
16579 resetHasChanged : function()
16581 this.loadedValue = String(this.getValue());
16584 * checks the current value against the 'loaded' value.
16585 * Note - will return false if 'resetHasChanged' has not been called first.
16587 hasChanged : function()
16589 if(this.disabled || this.readOnly) {
16592 return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16598 afterRender : function(){
16599 Roo.form.Field.superclass.afterRender.call(this);
16604 fireKey : function(e){
16605 //Roo.log('field ' + e.getKey());
16606 if(e.isNavKeyPress()){
16607 this.fireEvent("specialkey", this, e);
16612 * Resets the current field value to the originally loaded value and clears any validation messages
16614 reset : function(){
16615 this.setValue(this.resetValue);
16616 this.originalValue = this.getValue();
16617 this.clearInvalid();
16621 initEvents : function(){
16622 // safari killled keypress - so keydown is now used..
16623 this.el.on("keydown" , this.fireKey, this);
16624 this.el.on("focus", this.onFocus, this);
16625 this.el.on("blur", this.onBlur, this);
16626 this.el.relayEvent('keyup', this);
16628 // reference to original value for reset
16629 this.originalValue = this.getValue();
16630 this.resetValue = this.getValue();
16634 onFocus : function(){
16635 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16636 this.el.addClass(this.focusClass);
16638 if(!this.hasFocus){
16639 this.hasFocus = true;
16640 this.startValue = this.getValue();
16641 this.fireEvent("focus", this);
16645 beforeBlur : Roo.emptyFn,
16648 onBlur : function(){
16650 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16651 this.el.removeClass(this.focusClass);
16653 this.hasFocus = false;
16654 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16657 var v = this.getValue();
16658 if(String(v) !== String(this.startValue)){
16659 this.fireEvent('change', this, v, this.startValue);
16661 this.fireEvent("blur", this);
16665 * Returns whether or not the field value is currently valid
16666 * @param {Boolean} preventMark True to disable marking the field invalid
16667 * @return {Boolean} True if the value is valid, else false
16669 isValid : function(preventMark){
16673 var restore = this.preventMark;
16674 this.preventMark = preventMark === true;
16675 var v = this.validateValue(this.processValue(this.getRawValue()));
16676 this.preventMark = restore;
16681 * Validates the field value
16682 * @return {Boolean} True if the value is valid, else false
16684 validate : function(){
16685 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16686 this.clearInvalid();
16692 processValue : function(value){
16697 // Subclasses should provide the validation implementation by overriding this
16698 validateValue : function(value){
16703 * Mark this field as invalid
16704 * @param {String} msg The validation message
16706 markInvalid : function(msg){
16707 if(!this.rendered || this.preventMark){ // not rendered
16711 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16713 obj.el.addClass(this.invalidClass);
16714 msg = msg || this.invalidText;
16715 switch(this.msgTarget){
16717 obj.el.dom.qtip = msg;
16718 obj.el.dom.qclass = 'x-form-invalid-tip';
16719 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16720 Roo.QuickTips.enable();
16724 this.el.dom.title = msg;
16728 var elp = this.el.findParent('.x-form-element', 5, true);
16729 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16730 this.errorEl.setWidth(elp.getWidth(true)-20);
16732 this.errorEl.update(msg);
16733 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16736 if(!this.errorIcon){
16737 var elp = this.el.findParent('.x-form-element', 5, true);
16738 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16740 this.alignErrorIcon();
16741 this.errorIcon.dom.qtip = msg;
16742 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16743 this.errorIcon.show();
16744 this.on('resize', this.alignErrorIcon, this);
16747 var t = Roo.getDom(this.msgTarget);
16749 t.style.display = this.msgDisplay;
16752 this.fireEvent('invalid', this, msg);
16756 alignErrorIcon : function(){
16757 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16761 * Clear any invalid styles/messages for this field
16763 clearInvalid : function(){
16764 if(!this.rendered || this.preventMark){ // not rendered
16767 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16769 obj.el.removeClass(this.invalidClass);
16770 switch(this.msgTarget){
16772 obj.el.dom.qtip = '';
16775 this.el.dom.title = '';
16779 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16783 if(this.errorIcon){
16784 this.errorIcon.dom.qtip = '';
16785 this.errorIcon.hide();
16786 this.un('resize', this.alignErrorIcon, this);
16790 var t = Roo.getDom(this.msgTarget);
16792 t.style.display = 'none';
16795 this.fireEvent('valid', this);
16799 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
16800 * @return {Mixed} value The field value
16802 getRawValue : function(){
16803 var v = this.el.getValue();
16809 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16810 * @return {Mixed} value The field value
16812 getValue : function(){
16813 var v = this.el.getValue();
16819 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
16820 * @param {Mixed} value The value to set
16822 setRawValue : function(v){
16823 return this.el.dom.value = (v === null || v === undefined ? '' : v);
16827 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
16828 * @param {Mixed} value The value to set
16830 setValue : function(v){
16833 this.el.dom.value = (v === null || v === undefined ? '' : v);
16838 adjustSize : function(w, h){
16839 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16840 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16844 adjustWidth : function(tag, w){
16845 tag = tag.toLowerCase();
16846 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16847 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16848 if(tag == 'input'){
16851 if(tag == 'textarea'){
16854 }else if(Roo.isOpera){
16855 if(tag == 'input'){
16858 if(tag == 'textarea'){
16868 // anything other than normal should be considered experimental
16869 Roo.form.Field.msgFx = {
16871 show: function(msgEl, f){
16872 msgEl.setDisplayed('block');
16875 hide : function(msgEl, f){
16876 msgEl.setDisplayed(false).update('');
16881 show: function(msgEl, f){
16882 msgEl.slideIn('t', {stopFx:true});
16885 hide : function(msgEl, f){
16886 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16891 show: function(msgEl, f){
16892 msgEl.fixDisplay();
16893 msgEl.alignTo(f.el, 'tl-tr');
16894 msgEl.slideIn('l', {stopFx:true});
16897 hide : function(msgEl, f){
16898 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16903 * Ext JS Library 1.1.1
16904 * Copyright(c) 2006-2007, Ext JS, LLC.
16906 * Originally Released Under LGPL - original licence link has changed is not relivant.
16909 * <script type="text/javascript">
16914 * @class Roo.form.TextField
16915 * @extends Roo.form.Field
16916 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
16917 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16919 * Creates a new TextField
16920 * @param {Object} config Configuration options
16922 Roo.form.TextField = function(config){
16923 Roo.form.TextField.superclass.constructor.call(this, config);
16927 * Fires when the autosize function is triggered. The field may or may not have actually changed size
16928 * according to the default logic, but this event provides a hook for the developer to apply additional
16929 * logic at runtime to resize the field if needed.
16930 * @param {Roo.form.Field} this This text field
16931 * @param {Number} width The new field width
16937 Roo.extend(Roo.form.TextField, Roo.form.Field, {
16939 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16943 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16947 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16951 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16955 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16959 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16961 disableKeyFilter : false,
16963 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16967 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16971 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16973 maxLength : Number.MAX_VALUE,
16975 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16977 minLengthText : "The minimum length for this field is {0}",
16979 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16981 maxLengthText : "The maximum length for this field is {0}",
16983 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16985 selectOnFocus : false,
16987 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16989 blankText : "This field is required",
16991 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16992 * If available, this function will be called only after the basic validators all return true, and will be passed the
16993 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16997 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16998 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16999 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
17003 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
17007 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
17013 initEvents : function()
17015 if (this.emptyText) {
17016 this.el.attr('placeholder', this.emptyText);
17019 Roo.form.TextField.superclass.initEvents.call(this);
17020 if(this.validationEvent == 'keyup'){
17021 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
17022 this.el.on('keyup', this.filterValidation, this);
17024 else if(this.validationEvent !== false){
17025 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
17028 if(this.selectOnFocus){
17029 this.on("focus", this.preFocus, this);
17032 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
17033 this.el.on("keypress", this.filterKeys, this);
17036 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
17037 this.el.on("click", this.autoSize, this);
17039 if(this.el.is('input[type=password]') && Roo.isSafari){
17040 this.el.on('keydown', this.SafariOnKeyDown, this);
17044 processValue : function(value){
17045 if(this.stripCharsRe){
17046 var newValue = value.replace(this.stripCharsRe, '');
17047 if(newValue !== value){
17048 this.setRawValue(newValue);
17055 filterValidation : function(e){
17056 if(!e.isNavKeyPress()){
17057 this.validationTask.delay(this.validationDelay);
17062 onKeyUp : function(e){
17063 if(!e.isNavKeyPress()){
17069 * Resets the current field value to the originally-loaded value and clears any validation messages.
17072 reset : function(){
17073 Roo.form.TextField.superclass.reset.call(this);
17079 preFocus : function(){
17081 if(this.selectOnFocus){
17082 this.el.dom.select();
17088 filterKeys : function(e){
17089 var k = e.getKey();
17090 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17093 var c = e.getCharCode(), cc = String.fromCharCode(c);
17094 if(Roo.isIE && (e.isSpecialKey() || !cc)){
17097 if(!this.maskRe.test(cc)){
17102 setValue : function(v){
17104 Roo.form.TextField.superclass.setValue.apply(this, arguments);
17110 * Validates a value according to the field's validation rules and marks the field as invalid
17111 * if the validation fails
17112 * @param {Mixed} value The value to validate
17113 * @return {Boolean} True if the value is valid, else false
17115 validateValue : function(value){
17116 if(value.length < 1) { // if it's blank
17117 if(this.allowBlank){
17118 this.clearInvalid();
17121 this.markInvalid(this.blankText);
17125 if(value.length < this.minLength){
17126 this.markInvalid(String.format(this.minLengthText, this.minLength));
17129 if(value.length > this.maxLength){
17130 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17134 var vt = Roo.form.VTypes;
17135 if(!vt[this.vtype](value, this)){
17136 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17140 if(typeof this.validator == "function"){
17141 var msg = this.validator(value);
17143 this.markInvalid(msg);
17147 if(this.regex && !this.regex.test(value)){
17148 this.markInvalid(this.regexText);
17155 * Selects text in this field
17156 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17157 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17159 selectText : function(start, end){
17160 var v = this.getRawValue();
17162 start = start === undefined ? 0 : start;
17163 end = end === undefined ? v.length : end;
17164 var d = this.el.dom;
17165 if(d.setSelectionRange){
17166 d.setSelectionRange(start, end);
17167 }else if(d.createTextRange){
17168 var range = d.createTextRange();
17169 range.moveStart("character", start);
17170 range.moveEnd("character", v.length-end);
17177 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17178 * This only takes effect if grow = true, and fires the autosize event.
17180 autoSize : function(){
17181 if(!this.grow || !this.rendered){
17185 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17188 var v = el.dom.value;
17189 var d = document.createElement('div');
17190 d.appendChild(document.createTextNode(v));
17194 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17195 this.el.setWidth(w);
17196 this.fireEvent("autosize", this, w);
17200 SafariOnKeyDown : function(event)
17202 // this is a workaround for a password hang bug on chrome/ webkit.
17204 var isSelectAll = false;
17206 if(this.el.dom.selectionEnd > 0){
17207 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17209 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17210 event.preventDefault();
17215 if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17217 event.preventDefault();
17218 // this is very hacky as keydown always get's upper case.
17220 var cc = String.fromCharCode(event.getCharCode());
17223 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
17231 * Ext JS Library 1.1.1
17232 * Copyright(c) 2006-2007, Ext JS, LLC.
17234 * Originally Released Under LGPL - original licence link has changed is not relivant.
17237 * <script type="text/javascript">
17241 * @class Roo.form.Hidden
17242 * @extends Roo.form.TextField
17243 * Simple Hidden element used on forms
17245 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17248 * Creates a new Hidden form element.
17249 * @param {Object} config Configuration options
17254 // easy hidden field...
17255 Roo.form.Hidden = function(config){
17256 Roo.form.Hidden.superclass.constructor.call(this, config);
17259 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17261 inputType: 'hidden',
17264 labelSeparator: '',
17266 itemCls : 'x-form-item-display-none'
17274 * Ext JS Library 1.1.1
17275 * Copyright(c) 2006-2007, Ext JS, LLC.
17277 * Originally Released Under LGPL - original licence link has changed is not relivant.
17280 * <script type="text/javascript">
17284 * @class Roo.form.TriggerField
17285 * @extends Roo.form.TextField
17286 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17287 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17288 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17289 * for which you can provide a custom implementation. For example:
17291 var trigger = new Roo.form.TriggerField();
17292 trigger.onTriggerClick = myTriggerFn;
17293 trigger.applyTo('my-field');
17296 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17297 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17298 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
17299 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17301 * Create a new TriggerField.
17302 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17303 * to the base TextField)
17305 Roo.form.TriggerField = function(config){
17306 this.mimicing = false;
17307 Roo.form.TriggerField.superclass.constructor.call(this, config);
17310 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
17312 * @cfg {String} triggerClass A CSS class to apply to the trigger
17315 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17316 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17318 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17320 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17324 /** @cfg {Boolean} grow @hide */
17325 /** @cfg {Number} growMin @hide */
17326 /** @cfg {Number} growMax @hide */
17332 autoSize: Roo.emptyFn,
17336 deferHeight : true,
17339 actionMode : 'wrap',
17341 onResize : function(w, h){
17342 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17343 if(typeof w == 'number'){
17344 var x = w - this.trigger.getWidth();
17345 this.el.setWidth(this.adjustWidth('input', x));
17346 this.trigger.setStyle('left', x+'px');
17351 adjustSize : Roo.BoxComponent.prototype.adjustSize,
17354 getResizeEl : function(){
17359 getPositionEl : function(){
17364 alignErrorIcon : function(){
17365 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17369 onRender : function(ct, position){
17370 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17371 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17372 this.trigger = this.wrap.createChild(this.triggerConfig ||
17373 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17374 if(this.hideTrigger){
17375 this.trigger.setDisplayed(false);
17377 this.initTrigger();
17379 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17384 initTrigger : function(){
17385 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17386 this.trigger.addClassOnOver('x-form-trigger-over');
17387 this.trigger.addClassOnClick('x-form-trigger-click');
17391 onDestroy : function(){
17393 this.trigger.removeAllListeners();
17394 this.trigger.remove();
17397 this.wrap.remove();
17399 Roo.form.TriggerField.superclass.onDestroy.call(this);
17403 onFocus : function(){
17404 Roo.form.TriggerField.superclass.onFocus.call(this);
17405 if(!this.mimicing){
17406 this.wrap.addClass('x-trigger-wrap-focus');
17407 this.mimicing = true;
17408 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17409 if(this.monitorTab){
17410 this.el.on("keydown", this.checkTab, this);
17416 checkTab : function(e){
17417 if(e.getKey() == e.TAB){
17418 this.triggerBlur();
17423 onBlur : function(){
17428 mimicBlur : function(e, t){
17429 if(!this.wrap.contains(t) && this.validateBlur()){
17430 this.triggerBlur();
17435 triggerBlur : function(){
17436 this.mimicing = false;
17437 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17438 if(this.monitorTab){
17439 this.el.un("keydown", this.checkTab, this);
17441 this.wrap.removeClass('x-trigger-wrap-focus');
17442 Roo.form.TriggerField.superclass.onBlur.call(this);
17446 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17447 validateBlur : function(e, t){
17452 onDisable : function(){
17453 Roo.form.TriggerField.superclass.onDisable.call(this);
17455 this.wrap.addClass('x-item-disabled');
17460 onEnable : function(){
17461 Roo.form.TriggerField.superclass.onEnable.call(this);
17463 this.wrap.removeClass('x-item-disabled');
17468 onShow : function(){
17469 var ae = this.getActionEl();
17472 ae.dom.style.display = '';
17473 ae.dom.style.visibility = 'visible';
17479 onHide : function(){
17480 var ae = this.getActionEl();
17481 ae.dom.style.display = 'none';
17485 * The function that should handle the trigger's click event. This method does nothing by default until overridden
17486 * by an implementing function.
17488 * @param {EventObject} e
17490 onTriggerClick : Roo.emptyFn
17493 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
17494 // to be extended by an implementing class. For an example of implementing this class, see the custom
17495 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17496 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17497 initComponent : function(){
17498 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17500 this.triggerConfig = {
17501 tag:'span', cls:'x-form-twin-triggers', cn:[
17502 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17503 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17507 getTrigger : function(index){
17508 return this.triggers[index];
17511 initTrigger : function(){
17512 var ts = this.trigger.select('.x-form-trigger', true);
17513 this.wrap.setStyle('overflow', 'hidden');
17514 var triggerField = this;
17515 ts.each(function(t, all, index){
17516 t.hide = function(){
17517 var w = triggerField.wrap.getWidth();
17518 this.dom.style.display = 'none';
17519 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17521 t.show = function(){
17522 var w = triggerField.wrap.getWidth();
17523 this.dom.style.display = '';
17524 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17526 var triggerIndex = 'Trigger'+(index+1);
17528 if(this['hide'+triggerIndex]){
17529 t.dom.style.display = 'none';
17531 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17532 t.addClassOnOver('x-form-trigger-over');
17533 t.addClassOnClick('x-form-trigger-click');
17535 this.triggers = ts.elements;
17538 onTrigger1Click : Roo.emptyFn,
17539 onTrigger2Click : Roo.emptyFn
17542 * Ext JS Library 1.1.1
17543 * Copyright(c) 2006-2007, Ext JS, LLC.
17545 * Originally Released Under LGPL - original licence link has changed is not relivant.
17548 * <script type="text/javascript">
17552 * @class Roo.form.TextArea
17553 * @extends Roo.form.TextField
17554 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
17555 * support for auto-sizing.
17557 * Creates a new TextArea
17558 * @param {Object} config Configuration options
17560 Roo.form.TextArea = function(config){
17561 Roo.form.TextArea.superclass.constructor.call(this, config);
17562 // these are provided exchanges for backwards compat
17563 // minHeight/maxHeight were replaced by growMin/growMax to be
17564 // compatible with TextField growing config values
17565 if(this.minHeight !== undefined){
17566 this.growMin = this.minHeight;
17568 if(this.maxHeight !== undefined){
17569 this.growMax = this.maxHeight;
17573 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
17575 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17579 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17583 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17584 * in the field (equivalent to setting overflow: hidden, defaults to false)
17586 preventScrollbars: false,
17588 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17589 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17593 onRender : function(ct, position){
17595 this.defaultAutoCreate = {
17597 style:"width:300px;height:60px;",
17598 autocomplete: "new-password"
17601 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17603 this.textSizeEl = Roo.DomHelper.append(document.body, {
17604 tag: "pre", cls: "x-form-grow-sizer"
17606 if(this.preventScrollbars){
17607 this.el.setStyle("overflow", "hidden");
17609 this.el.setHeight(this.growMin);
17613 onDestroy : function(){
17614 if(this.textSizeEl){
17615 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17617 Roo.form.TextArea.superclass.onDestroy.call(this);
17621 onKeyUp : function(e){
17622 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17628 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17629 * This only takes effect if grow = true, and fires the autosize event if the height changes.
17631 autoSize : function(){
17632 if(!this.grow || !this.textSizeEl){
17636 var v = el.dom.value;
17637 var ts = this.textSizeEl;
17640 ts.appendChild(document.createTextNode(v));
17643 Roo.fly(ts).setWidth(this.el.getWidth());
17645 v = "  ";
17648 v = v.replace(/\n/g, '<p> </p>');
17650 v += " \n ";
17653 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17654 if(h != this.lastHeight){
17655 this.lastHeight = h;
17656 this.el.setHeight(h);
17657 this.fireEvent("autosize", this, h);
17662 * Ext JS Library 1.1.1
17663 * Copyright(c) 2006-2007, Ext JS, LLC.
17665 * Originally Released Under LGPL - original licence link has changed is not relivant.
17668 * <script type="text/javascript">
17673 * @class Roo.form.NumberField
17674 * @extends Roo.form.TextField
17675 * Numeric text field that provides automatic keystroke filtering and numeric validation.
17677 * Creates a new NumberField
17678 * @param {Object} config Configuration options
17680 Roo.form.NumberField = function(config){
17681 Roo.form.NumberField.superclass.constructor.call(this, config);
17684 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
17686 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17688 fieldClass: "x-form-field x-form-num-field",
17690 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17692 allowDecimals : true,
17694 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17696 decimalSeparator : ".",
17698 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17700 decimalPrecision : 2,
17702 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17704 allowNegative : true,
17706 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17708 minValue : Number.NEGATIVE_INFINITY,
17710 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17712 maxValue : Number.MAX_VALUE,
17714 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17716 minText : "The minimum value for this field is {0}",
17718 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17720 maxText : "The maximum value for this field is {0}",
17722 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
17723 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17725 nanText : "{0} is not a valid number",
17728 initEvents : function(){
17729 Roo.form.NumberField.superclass.initEvents.call(this);
17730 var allowed = "0123456789";
17731 if(this.allowDecimals){
17732 allowed += this.decimalSeparator;
17734 if(this.allowNegative){
17737 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17738 var keyPress = function(e){
17739 var k = e.getKey();
17740 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17743 var c = e.getCharCode();
17744 if(allowed.indexOf(String.fromCharCode(c)) === -1){
17748 this.el.on("keypress", keyPress, this);
17752 validateValue : function(value){
17753 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17756 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17759 var num = this.parseValue(value);
17761 this.markInvalid(String.format(this.nanText, value));
17764 if(num < this.minValue){
17765 this.markInvalid(String.format(this.minText, this.minValue));
17768 if(num > this.maxValue){
17769 this.markInvalid(String.format(this.maxText, this.maxValue));
17775 getValue : function(){
17776 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17780 parseValue : function(value){
17781 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17782 return isNaN(value) ? '' : value;
17786 fixPrecision : function(value){
17787 var nan = isNaN(value);
17788 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17789 return nan ? '' : value;
17791 return parseFloat(value).toFixed(this.decimalPrecision);
17794 setValue : function(v){
17795 v = this.fixPrecision(v);
17796 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17800 decimalPrecisionFcn : function(v){
17801 return Math.floor(v);
17804 beforeBlur : function(){
17805 var v = this.parseValue(this.getRawValue());
17812 * Ext JS Library 1.1.1
17813 * Copyright(c) 2006-2007, Ext JS, LLC.
17815 * Originally Released Under LGPL - original licence link has changed is not relivant.
17818 * <script type="text/javascript">
17822 * @class Roo.form.DateField
17823 * @extends Roo.form.TriggerField
17824 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17826 * Create a new DateField
17827 * @param {Object} config
17829 Roo.form.DateField = function(config){
17830 Roo.form.DateField.superclass.constructor.call(this, config);
17836 * Fires when a date is selected
17837 * @param {Roo.form.DateField} combo This combo box
17838 * @param {Date} date The date selected
17845 if(typeof this.minValue == "string") {
17846 this.minValue = this.parseDate(this.minValue);
17848 if(typeof this.maxValue == "string") {
17849 this.maxValue = this.parseDate(this.maxValue);
17851 this.ddMatch = null;
17852 if(this.disabledDates){
17853 var dd = this.disabledDates;
17855 for(var i = 0; i < dd.length; i++){
17857 if(i != dd.length-1) {
17861 this.ddMatch = new RegExp(re + ")");
17865 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
17867 * @cfg {String} format
17868 * The default date format string which can be overriden for localization support. The format must be
17869 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17873 * @cfg {String} altFormats
17874 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17875 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17877 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17879 * @cfg {Array} disabledDays
17880 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17882 disabledDays : null,
17884 * @cfg {String} disabledDaysText
17885 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17887 disabledDaysText : "Disabled",
17889 * @cfg {Array} disabledDates
17890 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17891 * expression so they are very powerful. Some examples:
17893 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17894 * <li>["03/08", "09/16"] would disable those days for every year</li>
17895 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17896 * <li>["03/../2006"] would disable every day in March 2006</li>
17897 * <li>["^03"] would disable every day in every March</li>
17899 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17900 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17902 disabledDates : null,
17904 * @cfg {String} disabledDatesText
17905 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17907 disabledDatesText : "Disabled",
17909 * @cfg {Date/String} minValue
17910 * The minimum allowed date. Can be either a Javascript date object or a string date in a
17911 * valid format (defaults to null).
17915 * @cfg {Date/String} maxValue
17916 * The maximum allowed date. Can be either a Javascript date object or a string date in a
17917 * valid format (defaults to null).
17921 * @cfg {String} minText
17922 * The error text to display when the date in the cell is before minValue (defaults to
17923 * 'The date in this field must be after {minValue}').
17925 minText : "The date in this field must be equal to or after {0}",
17927 * @cfg {String} maxText
17928 * The error text to display when the date in the cell is after maxValue (defaults to
17929 * 'The date in this field must be before {maxValue}').
17931 maxText : "The date in this field must be equal to or before {0}",
17933 * @cfg {String} invalidText
17934 * The error text to display when the date in the field is invalid (defaults to
17935 * '{value} is not a valid date - it must be in the format {format}').
17937 invalidText : "{0} is not a valid date - it must be in the format {1}",
17939 * @cfg {String} triggerClass
17940 * An additional CSS class used to style the trigger button. The trigger will always get the
17941 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17942 * which displays a calendar icon).
17944 triggerClass : 'x-form-date-trigger',
17948 * @cfg {Boolean} useIso
17949 * if enabled, then the date field will use a hidden field to store the
17950 * real value as iso formated date. default (false)
17954 * @cfg {String/Object} autoCreate
17955 * A DomHelper element spec, or true for a default element spec (defaults to
17956 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17959 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17962 hiddenField: false,
17964 onRender : function(ct, position)
17966 Roo.form.DateField.superclass.onRender.call(this, ct, position);
17968 //this.el.dom.removeAttribute('name');
17969 Roo.log("Changing name?");
17970 this.el.dom.setAttribute('name', this.name + '____hidden___' );
17971 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17973 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17974 // prevent input submission
17975 this.hiddenName = this.name;
17982 validateValue : function(value)
17984 value = this.formatDate(value);
17985 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17986 Roo.log('super failed');
17989 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17992 var svalue = value;
17993 value = this.parseDate(value);
17995 Roo.log('parse date failed' + svalue);
17996 this.markInvalid(String.format(this.invalidText, svalue, this.format));
17999 var time = value.getTime();
18000 if(this.minValue && time < this.minValue.getTime()){
18001 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18004 if(this.maxValue && time > this.maxValue.getTime()){
18005 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18008 if(this.disabledDays){
18009 var day = value.getDay();
18010 for(var i = 0; i < this.disabledDays.length; i++) {
18011 if(day === this.disabledDays[i]){
18012 this.markInvalid(this.disabledDaysText);
18017 var fvalue = this.formatDate(value);
18018 if(this.ddMatch && this.ddMatch.test(fvalue)){
18019 this.markInvalid(String.format(this.disabledDatesText, fvalue));
18026 // Provides logic to override the default TriggerField.validateBlur which just returns true
18027 validateBlur : function(){
18028 return !this.menu || !this.menu.isVisible();
18031 getName: function()
18033 // returns hidden if it's set..
18034 if (!this.rendered) {return ''};
18035 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
18040 * Returns the current date value of the date field.
18041 * @return {Date} The date value
18043 getValue : function(){
18045 return this.hiddenField ?
18046 this.hiddenField.value :
18047 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
18051 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
18052 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
18053 * (the default format used is "m/d/y").
18056 //All of these calls set the same date value (May 4, 2006)
18058 //Pass a date object:
18059 var dt = new Date('5/4/06');
18060 dateField.setValue(dt);
18062 //Pass a date string (default format):
18063 dateField.setValue('5/4/06');
18065 //Pass a date string (custom format):
18066 dateField.format = 'Y-m-d';
18067 dateField.setValue('2006-5-4');
18069 * @param {String/Date} date The date or valid date string
18071 setValue : function(date){
18072 if (this.hiddenField) {
18073 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18075 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18076 // make sure the value field is always stored as a date..
18077 this.value = this.parseDate(date);
18083 parseDate : function(value){
18084 if(!value || value instanceof Date){
18087 var v = Date.parseDate(value, this.format);
18088 if (!v && this.useIso) {
18089 v = Date.parseDate(value, 'Y-m-d');
18091 if(!v && this.altFormats){
18092 if(!this.altFormatsArray){
18093 this.altFormatsArray = this.altFormats.split("|");
18095 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18096 v = Date.parseDate(value, this.altFormatsArray[i]);
18103 formatDate : function(date, fmt){
18104 return (!date || !(date instanceof Date)) ?
18105 date : date.dateFormat(fmt || this.format);
18110 select: function(m, d){
18113 this.fireEvent('select', this, d);
18115 show : function(){ // retain focus styling
18119 this.focus.defer(10, this);
18120 var ml = this.menuListeners;
18121 this.menu.un("select", ml.select, this);
18122 this.menu.un("show", ml.show, this);
18123 this.menu.un("hide", ml.hide, this);
18128 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18129 onTriggerClick : function(){
18133 if(this.menu == null){
18134 this.menu = new Roo.menu.DateMenu();
18136 Roo.apply(this.menu.picker, {
18137 showClear: this.allowBlank,
18138 minDate : this.minValue,
18139 maxDate : this.maxValue,
18140 disabledDatesRE : this.ddMatch,
18141 disabledDatesText : this.disabledDatesText,
18142 disabledDays : this.disabledDays,
18143 disabledDaysText : this.disabledDaysText,
18144 format : this.useIso ? 'Y-m-d' : this.format,
18145 minText : String.format(this.minText, this.formatDate(this.minValue)),
18146 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18148 this.menu.on(Roo.apply({}, this.menuListeners, {
18151 this.menu.picker.setValue(this.getValue() || new Date());
18152 this.menu.show(this.el, "tl-bl?");
18155 beforeBlur : function(){
18156 var v = this.parseDate(this.getRawValue());
18166 isDirty : function() {
18167 if(this.disabled) {
18171 if(typeof(this.startValue) === 'undefined'){
18175 return String(this.getValue()) !== String(this.startValue);
18180 * Ext JS Library 1.1.1
18181 * Copyright(c) 2006-2007, Ext JS, LLC.
18183 * Originally Released Under LGPL - original licence link has changed is not relivant.
18186 * <script type="text/javascript">
18190 * @class Roo.form.MonthField
18191 * @extends Roo.form.TriggerField
18192 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18194 * Create a new MonthField
18195 * @param {Object} config
18197 Roo.form.MonthField = function(config){
18199 Roo.form.MonthField.superclass.constructor.call(this, config);
18205 * Fires when a date is selected
18206 * @param {Roo.form.MonthFieeld} combo This combo box
18207 * @param {Date} date The date selected
18214 if(typeof this.minValue == "string") {
18215 this.minValue = this.parseDate(this.minValue);
18217 if(typeof this.maxValue == "string") {
18218 this.maxValue = this.parseDate(this.maxValue);
18220 this.ddMatch = null;
18221 if(this.disabledDates){
18222 var dd = this.disabledDates;
18224 for(var i = 0; i < dd.length; i++){
18226 if(i != dd.length-1) {
18230 this.ddMatch = new RegExp(re + ")");
18234 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
18236 * @cfg {String} format
18237 * The default date format string which can be overriden for localization support. The format must be
18238 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18242 * @cfg {String} altFormats
18243 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18244 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18246 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18248 * @cfg {Array} disabledDays
18249 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18251 disabledDays : [0,1,2,3,4,5,6],
18253 * @cfg {String} disabledDaysText
18254 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18256 disabledDaysText : "Disabled",
18258 * @cfg {Array} disabledDates
18259 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18260 * expression so they are very powerful. Some examples:
18262 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18263 * <li>["03/08", "09/16"] would disable those days for every year</li>
18264 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18265 * <li>["03/../2006"] would disable every day in March 2006</li>
18266 * <li>["^03"] would disable every day in every March</li>
18268 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18269 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18271 disabledDates : null,
18273 * @cfg {String} disabledDatesText
18274 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18276 disabledDatesText : "Disabled",
18278 * @cfg {Date/String} minValue
18279 * The minimum allowed date. Can be either a Javascript date object or a string date in a
18280 * valid format (defaults to null).
18284 * @cfg {Date/String} maxValue
18285 * The maximum allowed date. Can be either a Javascript date object or a string date in a
18286 * valid format (defaults to null).
18290 * @cfg {String} minText
18291 * The error text to display when the date in the cell is before minValue (defaults to
18292 * 'The date in this field must be after {minValue}').
18294 minText : "The date in this field must be equal to or after {0}",
18296 * @cfg {String} maxTextf
18297 * The error text to display when the date in the cell is after maxValue (defaults to
18298 * 'The date in this field must be before {maxValue}').
18300 maxText : "The date in this field must be equal to or before {0}",
18302 * @cfg {String} invalidText
18303 * The error text to display when the date in the field is invalid (defaults to
18304 * '{value} is not a valid date - it must be in the format {format}').
18306 invalidText : "{0} is not a valid date - it must be in the format {1}",
18308 * @cfg {String} triggerClass
18309 * An additional CSS class used to style the trigger button. The trigger will always get the
18310 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18311 * which displays a calendar icon).
18313 triggerClass : 'x-form-date-trigger',
18317 * @cfg {Boolean} useIso
18318 * if enabled, then the date field will use a hidden field to store the
18319 * real value as iso formated date. default (true)
18323 * @cfg {String/Object} autoCreate
18324 * A DomHelper element spec, or true for a default element spec (defaults to
18325 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18328 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18331 hiddenField: false,
18333 hideMonthPicker : false,
18335 onRender : function(ct, position)
18337 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18339 this.el.dom.removeAttribute('name');
18340 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18342 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18343 // prevent input submission
18344 this.hiddenName = this.name;
18351 validateValue : function(value)
18353 value = this.formatDate(value);
18354 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18357 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18360 var svalue = value;
18361 value = this.parseDate(value);
18363 this.markInvalid(String.format(this.invalidText, svalue, this.format));
18366 var time = value.getTime();
18367 if(this.minValue && time < this.minValue.getTime()){
18368 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18371 if(this.maxValue && time > this.maxValue.getTime()){
18372 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18375 /*if(this.disabledDays){
18376 var day = value.getDay();
18377 for(var i = 0; i < this.disabledDays.length; i++) {
18378 if(day === this.disabledDays[i]){
18379 this.markInvalid(this.disabledDaysText);
18385 var fvalue = this.formatDate(value);
18386 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18387 this.markInvalid(String.format(this.disabledDatesText, fvalue));
18395 // Provides logic to override the default TriggerField.validateBlur which just returns true
18396 validateBlur : function(){
18397 return !this.menu || !this.menu.isVisible();
18401 * Returns the current date value of the date field.
18402 * @return {Date} The date value
18404 getValue : function(){
18408 return this.hiddenField ?
18409 this.hiddenField.value :
18410 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18414 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
18415 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18416 * (the default format used is "m/d/y").
18419 //All of these calls set the same date value (May 4, 2006)
18421 //Pass a date object:
18422 var dt = new Date('5/4/06');
18423 monthField.setValue(dt);
18425 //Pass a date string (default format):
18426 monthField.setValue('5/4/06');
18428 //Pass a date string (custom format):
18429 monthField.format = 'Y-m-d';
18430 monthField.setValue('2006-5-4');
18432 * @param {String/Date} date The date or valid date string
18434 setValue : function(date){
18435 Roo.log('month setValue' + date);
18436 // can only be first of month..
18438 var val = this.parseDate(date);
18440 if (this.hiddenField) {
18441 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18443 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18444 this.value = this.parseDate(date);
18448 parseDate : function(value){
18449 if(!value || value instanceof Date){
18450 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18453 var v = Date.parseDate(value, this.format);
18454 if (!v && this.useIso) {
18455 v = Date.parseDate(value, 'Y-m-d');
18459 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18463 if(!v && this.altFormats){
18464 if(!this.altFormatsArray){
18465 this.altFormatsArray = this.altFormats.split("|");
18467 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18468 v = Date.parseDate(value, this.altFormatsArray[i]);
18475 formatDate : function(date, fmt){
18476 return (!date || !(date instanceof Date)) ?
18477 date : date.dateFormat(fmt || this.format);
18482 select: function(m, d){
18484 this.fireEvent('select', this, d);
18486 show : function(){ // retain focus styling
18490 this.focus.defer(10, this);
18491 var ml = this.menuListeners;
18492 this.menu.un("select", ml.select, this);
18493 this.menu.un("show", ml.show, this);
18494 this.menu.un("hide", ml.hide, this);
18498 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18499 onTriggerClick : function(){
18503 if(this.menu == null){
18504 this.menu = new Roo.menu.DateMenu();
18508 Roo.apply(this.menu.picker, {
18510 showClear: this.allowBlank,
18511 minDate : this.minValue,
18512 maxDate : this.maxValue,
18513 disabledDatesRE : this.ddMatch,
18514 disabledDatesText : this.disabledDatesText,
18516 format : this.useIso ? 'Y-m-d' : this.format,
18517 minText : String.format(this.minText, this.formatDate(this.minValue)),
18518 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18521 this.menu.on(Roo.apply({}, this.menuListeners, {
18529 // hide month picker get's called when we called by 'before hide';
18531 var ignorehide = true;
18532 p.hideMonthPicker = function(disableAnim){
18536 if(this.monthPicker){
18537 Roo.log("hideMonthPicker called");
18538 if(disableAnim === true){
18539 this.monthPicker.hide();
18541 this.monthPicker.slideOut('t', {duration:.2});
18542 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18543 p.fireEvent("select", this, this.value);
18549 Roo.log('picker set value');
18550 Roo.log(this.getValue());
18551 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18552 m.show(this.el, 'tl-bl?');
18553 ignorehide = false;
18554 // this will trigger hideMonthPicker..
18557 // hidden the day picker
18558 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18564 p.showMonthPicker.defer(100, p);
18570 beforeBlur : function(){
18571 var v = this.parseDate(this.getRawValue());
18577 /** @cfg {Boolean} grow @hide */
18578 /** @cfg {Number} growMin @hide */
18579 /** @cfg {Number} growMax @hide */
18586 * Ext JS Library 1.1.1
18587 * Copyright(c) 2006-2007, Ext JS, LLC.
18589 * Originally Released Under LGPL - original licence link has changed is not relivant.
18592 * <script type="text/javascript">
18597 * @class Roo.form.ComboBox
18598 * @extends Roo.form.TriggerField
18599 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18601 * Create a new ComboBox.
18602 * @param {Object} config Configuration options
18604 Roo.form.ComboBox = function(config){
18605 Roo.form.ComboBox.superclass.constructor.call(this, config);
18609 * Fires when the dropdown list is expanded
18610 * @param {Roo.form.ComboBox} combo This combo box
18615 * Fires when the dropdown list is collapsed
18616 * @param {Roo.form.ComboBox} combo This combo box
18620 * @event beforeselect
18621 * Fires before a list item is selected. Return false to cancel the selection.
18622 * @param {Roo.form.ComboBox} combo This combo box
18623 * @param {Roo.data.Record} record The data record returned from the underlying store
18624 * @param {Number} index The index of the selected item in the dropdown list
18626 'beforeselect' : true,
18629 * Fires when a list item is selected
18630 * @param {Roo.form.ComboBox} combo This combo box
18631 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18632 * @param {Number} index The index of the selected item in the dropdown list
18636 * @event beforequery
18637 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18638 * The event object passed has these properties:
18639 * @param {Roo.form.ComboBox} combo This combo box
18640 * @param {String} query The query
18641 * @param {Boolean} forceAll true to force "all" query
18642 * @param {Boolean} cancel true to cancel the query
18643 * @param {Object} e The query event object
18645 'beforequery': true,
18648 * Fires when the 'add' icon is pressed (add a listener to enable add button)
18649 * @param {Roo.form.ComboBox} combo This combo box
18654 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18655 * @param {Roo.form.ComboBox} combo This combo box
18656 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18662 if(this.transform){
18663 this.allowDomMove = false;
18664 var s = Roo.getDom(this.transform);
18665 if(!this.hiddenName){
18666 this.hiddenName = s.name;
18669 this.mode = 'local';
18670 var d = [], opts = s.options;
18671 for(var i = 0, len = opts.length;i < len; i++){
18673 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18675 this.value = value;
18677 d.push([value, o.text]);
18679 this.store = new Roo.data.SimpleStore({
18681 fields: ['value', 'text'],
18684 this.valueField = 'value';
18685 this.displayField = 'text';
18687 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18688 if(!this.lazyRender){
18689 this.target = true;
18690 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18691 s.parentNode.removeChild(s); // remove it
18692 this.render(this.el.parentNode);
18694 s.parentNode.removeChild(s); // remove it
18699 this.store = Roo.factory(this.store, Roo.data);
18702 this.selectedIndex = -1;
18703 if(this.mode == 'local'){
18704 if(config.queryDelay === undefined){
18705 this.queryDelay = 10;
18707 if(config.minChars === undefined){
18713 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18715 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18718 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18719 * rendering into an Roo.Editor, defaults to false)
18722 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18723 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18726 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18729 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18730 * the dropdown list (defaults to undefined, with no header element)
18734 * @cfg {String/Roo.Template} tpl The template to use to render the output
18738 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18740 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18742 listWidth: undefined,
18744 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18745 * mode = 'remote' or 'text' if mode = 'local')
18747 displayField: undefined,
18749 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18750 * mode = 'remote' or 'value' if mode = 'local').
18751 * Note: use of a valueField requires the user make a selection
18752 * in order for a value to be mapped.
18754 valueField: undefined,
18758 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18759 * field's data value (defaults to the underlying DOM element's name)
18761 hiddenName: undefined,
18763 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18767 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18769 selectedClass: 'x-combo-selected',
18771 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
18772 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18773 * which displays a downward arrow icon).
18775 triggerClass : 'x-form-arrow-trigger',
18777 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18781 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18782 * anchor positions (defaults to 'tl-bl')
18784 listAlign: 'tl-bl?',
18786 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18790 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
18791 * query specified by the allQuery config option (defaults to 'query')
18793 triggerAction: 'query',
18795 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18796 * (defaults to 4, does not apply if editable = false)
18800 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18801 * delay (typeAheadDelay) if it matches a known value (defaults to false)
18805 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18806 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18810 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18811 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
18815 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
18816 * when editable = true (defaults to false)
18818 selectOnFocus:false,
18820 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18822 queryParam: 'query',
18824 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
18825 * when mode = 'remote' (defaults to 'Loading...')
18827 loadingText: 'Loading...',
18829 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18833 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18837 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18838 * traditional select (defaults to true)
18842 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18846 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18850 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18851 * listWidth has a higher value)
18855 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18856 * allow the user to set arbitrary text into the field (defaults to false)
18858 forceSelection:false,
18860 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18861 * if typeAhead = true (defaults to 250)
18863 typeAheadDelay : 250,
18865 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18866 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18868 valueNotFoundText : undefined,
18870 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18872 blockFocus : false,
18875 * @cfg {Boolean} disableClear Disable showing of clear button.
18877 disableClear : false,
18879 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
18881 alwaysQuery : false,
18887 // element that contains real text value.. (when hidden is used..)
18890 onRender : function(ct, position){
18891 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18892 if(this.hiddenName){
18893 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
18895 this.hiddenField.value =
18896 this.hiddenValue !== undefined ? this.hiddenValue :
18897 this.value !== undefined ? this.value : '';
18899 // prevent input submission
18900 this.el.dom.removeAttribute('name');
18905 this.el.dom.setAttribute('autocomplete', 'off');
18908 var cls = 'x-combo-list';
18910 this.list = new Roo.Layer({
18911 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18914 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18915 this.list.setWidth(lw);
18916 this.list.swallowEvent('mousewheel');
18917 this.assetHeight = 0;
18920 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18921 this.assetHeight += this.header.getHeight();
18924 this.innerList = this.list.createChild({cls:cls+'-inner'});
18925 this.innerList.on('mouseover', this.onViewOver, this);
18926 this.innerList.on('mousemove', this.onViewMove, this);
18927 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18929 if(this.allowBlank && !this.pageSize && !this.disableClear){
18930 this.footer = this.list.createChild({cls:cls+'-ft'});
18931 this.pageTb = new Roo.Toolbar(this.footer);
18935 this.footer = this.list.createChild({cls:cls+'-ft'});
18936 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18937 {pageSize: this.pageSize});
18941 if (this.pageTb && this.allowBlank && !this.disableClear) {
18943 this.pageTb.add(new Roo.Toolbar.Fill(), {
18944 cls: 'x-btn-icon x-btn-clear',
18946 handler: function()
18949 _this.clearValue();
18950 _this.onSelect(false, -1);
18955 this.assetHeight += this.footer.getHeight();
18960 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18963 this.view = new Roo.View(this.innerList, this.tpl, {
18964 singleSelect:true, store: this.store, selectedClass: this.selectedClass
18967 this.view.on('click', this.onViewClick, this);
18969 this.store.on('beforeload', this.onBeforeLoad, this);
18970 this.store.on('load', this.onLoad, this);
18971 this.store.on('loadexception', this.onLoadException, this);
18973 if(this.resizable){
18974 this.resizer = new Roo.Resizable(this.list, {
18975 pinned:true, handles:'se'
18977 this.resizer.on('resize', function(r, w, h){
18978 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18979 this.listWidth = w;
18980 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18981 this.restrictHeight();
18983 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18985 if(!this.editable){
18986 this.editable = true;
18987 this.setEditable(false);
18991 if (typeof(this.events.add.listeners) != 'undefined') {
18993 this.addicon = this.wrap.createChild(
18994 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
18996 this.addicon.on('click', function(e) {
18997 this.fireEvent('add', this);
19000 if (typeof(this.events.edit.listeners) != 'undefined') {
19002 this.editicon = this.wrap.createChild(
19003 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
19004 if (this.addicon) {
19005 this.editicon.setStyle('margin-left', '40px');
19007 this.editicon.on('click', function(e) {
19009 // we fire even if inothing is selected..
19010 this.fireEvent('edit', this, this.lastData );
19020 initEvents : function(){
19021 Roo.form.ComboBox.superclass.initEvents.call(this);
19023 this.keyNav = new Roo.KeyNav(this.el, {
19024 "up" : function(e){
19025 this.inKeyMode = true;
19029 "down" : function(e){
19030 if(!this.isExpanded()){
19031 this.onTriggerClick();
19033 this.inKeyMode = true;
19038 "enter" : function(e){
19039 this.onViewClick();
19043 "esc" : function(e){
19047 "tab" : function(e){
19048 this.onViewClick(false);
19049 this.fireEvent("specialkey", this, e);
19055 doRelay : function(foo, bar, hname){
19056 if(hname == 'down' || this.scope.isExpanded()){
19057 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
19064 this.queryDelay = Math.max(this.queryDelay || 10,
19065 this.mode == 'local' ? 10 : 250);
19066 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
19067 if(this.typeAhead){
19068 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
19070 if(this.editable !== false){
19071 this.el.on("keyup", this.onKeyUp, this);
19073 if(this.forceSelection){
19074 this.on('blur', this.doForce, this);
19078 onDestroy : function(){
19080 this.view.setStore(null);
19081 this.view.el.removeAllListeners();
19082 this.view.el.remove();
19083 this.view.purgeListeners();
19086 this.list.destroy();
19089 this.store.un('beforeload', this.onBeforeLoad, this);
19090 this.store.un('load', this.onLoad, this);
19091 this.store.un('loadexception', this.onLoadException, this);
19093 Roo.form.ComboBox.superclass.onDestroy.call(this);
19097 fireKey : function(e){
19098 if(e.isNavKeyPress() && !this.list.isVisible()){
19099 this.fireEvent("specialkey", this, e);
19104 onResize: function(w, h){
19105 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19107 if(typeof w != 'number'){
19108 // we do not handle it!?!?
19111 var tw = this.trigger.getWidth();
19112 tw += this.addicon ? this.addicon.getWidth() : 0;
19113 tw += this.editicon ? this.editicon.getWidth() : 0;
19115 this.el.setWidth( this.adjustWidth('input', x));
19117 this.trigger.setStyle('left', x+'px');
19119 if(this.list && this.listWidth === undefined){
19120 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19121 this.list.setWidth(lw);
19122 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19130 * Allow or prevent the user from directly editing the field text. If false is passed,
19131 * the user will only be able to select from the items defined in the dropdown list. This method
19132 * is the runtime equivalent of setting the 'editable' config option at config time.
19133 * @param {Boolean} value True to allow the user to directly edit the field text
19135 setEditable : function(value){
19136 if(value == this.editable){
19139 this.editable = value;
19141 this.el.dom.setAttribute('readOnly', true);
19142 this.el.on('mousedown', this.onTriggerClick, this);
19143 this.el.addClass('x-combo-noedit');
19145 this.el.dom.setAttribute('readOnly', false);
19146 this.el.un('mousedown', this.onTriggerClick, this);
19147 this.el.removeClass('x-combo-noedit');
19152 onBeforeLoad : function(){
19153 if(!this.hasFocus){
19156 this.innerList.update(this.loadingText ?
19157 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19158 this.restrictHeight();
19159 this.selectedIndex = -1;
19163 onLoad : function(){
19164 if(!this.hasFocus){
19167 if(this.store.getCount() > 0){
19169 this.restrictHeight();
19170 if(this.lastQuery == this.allQuery){
19172 this.el.dom.select();
19174 if(!this.selectByValue(this.value, true)){
19175 this.select(0, true);
19179 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19180 this.taTask.delay(this.typeAheadDelay);
19184 this.onEmptyResults();
19189 onLoadException : function()
19192 Roo.log(this.store.reader.jsonData);
19193 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19194 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19200 onTypeAhead : function(){
19201 if(this.store.getCount() > 0){
19202 var r = this.store.getAt(0);
19203 var newValue = r.data[this.displayField];
19204 var len = newValue.length;
19205 var selStart = this.getRawValue().length;
19206 if(selStart != len){
19207 this.setRawValue(newValue);
19208 this.selectText(selStart, newValue.length);
19214 onSelect : function(record, index){
19215 if(this.fireEvent('beforeselect', this, record, index) !== false){
19216 this.setFromData(index > -1 ? record.data : false);
19218 this.fireEvent('select', this, record, index);
19223 * Returns the currently selected field value or empty string if no value is set.
19224 * @return {String} value The selected value
19226 getValue : function(){
19227 if(this.valueField){
19228 return typeof this.value != 'undefined' ? this.value : '';
19230 return Roo.form.ComboBox.superclass.getValue.call(this);
19234 * Clears any text/value currently set in the field
19236 clearValue : function(){
19237 if(this.hiddenField){
19238 this.hiddenField.value = '';
19241 this.setRawValue('');
19242 this.lastSelectionText = '';
19247 * Sets the specified value into the field. If the value finds a match, the corresponding record text
19248 * will be displayed in the field. If the value does not match the data value of an existing item,
19249 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19250 * Otherwise the field will be blank (although the value will still be set).
19251 * @param {String} value The value to match
19253 setValue : function(v){
19255 if(this.valueField){
19256 var r = this.findRecord(this.valueField, v);
19258 text = r.data[this.displayField];
19259 }else if(this.valueNotFoundText !== undefined){
19260 text = this.valueNotFoundText;
19263 this.lastSelectionText = text;
19264 if(this.hiddenField){
19265 this.hiddenField.value = v;
19267 Roo.form.ComboBox.superclass.setValue.call(this, text);
19271 * @property {Object} the last set data for the element
19276 * Sets the value of the field based on a object which is related to the record format for the store.
19277 * @param {Object} value the value to set as. or false on reset?
19279 setFromData : function(o){
19280 var dv = ''; // display value
19281 var vv = ''; // value value..
19283 if (this.displayField) {
19284 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19286 // this is an error condition!!!
19287 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
19290 if(this.valueField){
19291 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19293 if(this.hiddenField){
19294 this.hiddenField.value = vv;
19296 this.lastSelectionText = dv;
19297 Roo.form.ComboBox.superclass.setValue.call(this, dv);
19301 // no hidden field.. - we store the value in 'value', but still display
19302 // display field!!!!
19303 this.lastSelectionText = dv;
19304 Roo.form.ComboBox.superclass.setValue.call(this, dv);
19310 reset : function(){
19311 // overridden so that last data is reset..
19312 this.setValue(this.resetValue);
19313 this.originalValue = this.getValue();
19314 this.clearInvalid();
19315 this.lastData = false;
19317 this.view.clearSelections();
19321 findRecord : function(prop, value){
19323 if(this.store.getCount() > 0){
19324 this.store.each(function(r){
19325 if(r.data[prop] == value){
19335 getName: function()
19337 // returns hidden if it's set..
19338 if (!this.rendered) {return ''};
19339 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
19343 onViewMove : function(e, t){
19344 this.inKeyMode = false;
19348 onViewOver : function(e, t){
19349 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19352 var item = this.view.findItemFromChild(t);
19354 var index = this.view.indexOf(item);
19355 this.select(index, false);
19360 onViewClick : function(doFocus)
19362 var index = this.view.getSelectedIndexes()[0];
19363 var r = this.store.getAt(index);
19365 this.onSelect(r, index);
19367 if(doFocus !== false && !this.blockFocus){
19373 restrictHeight : function(){
19374 this.innerList.dom.style.height = '';
19375 var inner = this.innerList.dom;
19376 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19377 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19378 this.list.beginUpdate();
19379 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19380 this.list.alignTo(this.el, this.listAlign);
19381 this.list.endUpdate();
19385 onEmptyResults : function(){
19390 * Returns true if the dropdown list is expanded, else false.
19392 isExpanded : function(){
19393 return this.list.isVisible();
19397 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19398 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19399 * @param {String} value The data value of the item to select
19400 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19401 * selected item if it is not currently in view (defaults to true)
19402 * @return {Boolean} True if the value matched an item in the list, else false
19404 selectByValue : function(v, scrollIntoView){
19405 if(v !== undefined && v !== null){
19406 var r = this.findRecord(this.valueField || this.displayField, v);
19408 this.select(this.store.indexOf(r), scrollIntoView);
19416 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19417 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19418 * @param {Number} index The zero-based index of the list item to select
19419 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19420 * selected item if it is not currently in view (defaults to true)
19422 select : function(index, scrollIntoView){
19423 this.selectedIndex = index;
19424 this.view.select(index);
19425 if(scrollIntoView !== false){
19426 var el = this.view.getNode(index);
19428 this.innerList.scrollChildIntoView(el, false);
19434 selectNext : function(){
19435 var ct = this.store.getCount();
19437 if(this.selectedIndex == -1){
19439 }else if(this.selectedIndex < ct-1){
19440 this.select(this.selectedIndex+1);
19446 selectPrev : function(){
19447 var ct = this.store.getCount();
19449 if(this.selectedIndex == -1){
19451 }else if(this.selectedIndex != 0){
19452 this.select(this.selectedIndex-1);
19458 onKeyUp : function(e){
19459 if(this.editable !== false && !e.isSpecialKey()){
19460 this.lastKey = e.getKey();
19461 this.dqTask.delay(this.queryDelay);
19466 validateBlur : function(){
19467 return !this.list || !this.list.isVisible();
19471 initQuery : function(){
19472 this.doQuery(this.getRawValue());
19476 doForce : function(){
19477 if(this.el.dom.value.length > 0){
19478 this.el.dom.value =
19479 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19485 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
19486 * query allowing the query action to be canceled if needed.
19487 * @param {String} query The SQL query to execute
19488 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19489 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
19490 * saved in the current store (defaults to false)
19492 doQuery : function(q, forceAll){
19493 if(q === undefined || q === null){
19498 forceAll: forceAll,
19502 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19506 forceAll = qe.forceAll;
19507 if(forceAll === true || (q.length >= this.minChars)){
19508 if(this.lastQuery != q || this.alwaysQuery){
19509 this.lastQuery = q;
19510 if(this.mode == 'local'){
19511 this.selectedIndex = -1;
19513 this.store.clearFilter();
19515 this.store.filter(this.displayField, q);
19519 this.store.baseParams[this.queryParam] = q;
19521 params: this.getParams(q)
19526 this.selectedIndex = -1;
19533 getParams : function(q){
19535 //p[this.queryParam] = q;
19538 p.limit = this.pageSize;
19544 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19546 collapse : function(){
19547 if(!this.isExpanded()){
19551 Roo.get(document).un('mousedown', this.collapseIf, this);
19552 Roo.get(document).un('mousewheel', this.collapseIf, this);
19553 if (!this.editable) {
19554 Roo.get(document).un('keydown', this.listKeyPress, this);
19556 this.fireEvent('collapse', this);
19560 collapseIf : function(e){
19561 if(!e.within(this.wrap) && !e.within(this.list)){
19567 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19569 expand : function(){
19570 if(this.isExpanded() || !this.hasFocus){
19573 this.list.alignTo(this.el, this.listAlign);
19575 Roo.get(document).on('mousedown', this.collapseIf, this);
19576 Roo.get(document).on('mousewheel', this.collapseIf, this);
19577 if (!this.editable) {
19578 Roo.get(document).on('keydown', this.listKeyPress, this);
19581 this.fireEvent('expand', this);
19585 // Implements the default empty TriggerField.onTriggerClick function
19586 onTriggerClick : function(){
19590 if(this.isExpanded()){
19592 if (!this.blockFocus) {
19597 this.hasFocus = true;
19598 if(this.triggerAction == 'all') {
19599 this.doQuery(this.allQuery, true);
19601 this.doQuery(this.getRawValue());
19603 if (!this.blockFocus) {
19608 listKeyPress : function(e)
19610 //Roo.log('listkeypress');
19611 // scroll to first matching element based on key pres..
19612 if (e.isSpecialKey()) {
19615 var k = String.fromCharCode(e.getKey()).toUpperCase();
19618 var csel = this.view.getSelectedNodes();
19619 var cselitem = false;
19621 var ix = this.view.indexOf(csel[0]);
19622 cselitem = this.store.getAt(ix);
19623 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19629 this.store.each(function(v) {
19631 // start at existing selection.
19632 if (cselitem.id == v.id) {
19638 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19639 match = this.store.indexOf(v);
19644 if (match === false) {
19645 return true; // no more action?
19648 this.view.select(match);
19649 var sn = Roo.get(this.view.getSelectedNodes()[0]);
19650 sn.scrollIntoView(sn.dom.parentNode, false);
19654 * @cfg {Boolean} grow
19658 * @cfg {Number} growMin
19662 * @cfg {Number} growMax
19670 * Copyright(c) 2010-2012, Roo J Solutions Limited
19677 * @class Roo.form.ComboBoxArray
19678 * @extends Roo.form.TextField
19679 * A facebook style adder... for lists of email / people / countries etc...
19680 * pick multiple items from a combo box, and shows each one.
19682 * Fred [x] Brian [x] [Pick another |v]
19685 * For this to work: it needs various extra information
19686 * - normal combo problay has
19688 * + displayField, valueField
19690 * For our purpose...
19693 * If we change from 'extends' to wrapping...
19700 * Create a new ComboBoxArray.
19701 * @param {Object} config Configuration options
19705 Roo.form.ComboBoxArray = function(config)
19709 * @event beforeremove
19710 * Fires before remove the value from the list
19711 * @param {Roo.form.ComboBoxArray} _self This combo box array
19712 * @param {Roo.form.ComboBoxArray.Item} item removed item
19714 'beforeremove' : true,
19717 * Fires when remove the value from the list
19718 * @param {Roo.form.ComboBoxArray} _self This combo box array
19719 * @param {Roo.form.ComboBoxArray.Item} item removed item
19726 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19728 this.items = new Roo.util.MixedCollection(false);
19730 // construct the child combo...
19740 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19743 * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19748 // behavies liek a hiddne field
19749 inputType: 'hidden',
19751 * @cfg {Number} width The width of the box that displays the selected element
19758 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
19762 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
19764 hiddenName : false,
19767 // private the array of items that are displayed..
19769 // private - the hidden field el.
19771 // private - the filed el..
19774 //validateValue : function() { return true; }, // all values are ok!
19775 //onAddClick: function() { },
19777 onRender : function(ct, position)
19780 // create the standard hidden element
19781 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19784 // give fake names to child combo;
19785 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19786 this.combo.name = this.name? (this.name+'-subcombo') : this.name;
19788 this.combo = Roo.factory(this.combo, Roo.form);
19789 this.combo.onRender(ct, position);
19790 if (typeof(this.combo.width) != 'undefined') {
19791 this.combo.onResize(this.combo.width,0);
19794 this.combo.initEvents();
19796 // assigned so form know we need to do this..
19797 this.store = this.combo.store;
19798 this.valueField = this.combo.valueField;
19799 this.displayField = this.combo.displayField ;
19802 this.combo.wrap.addClass('x-cbarray-grp');
19804 var cbwrap = this.combo.wrap.createChild(
19805 {tag: 'div', cls: 'x-cbarray-cb'},
19810 this.hiddenEl = this.combo.wrap.createChild({
19811 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
19813 this.el = this.combo.wrap.createChild({
19814 tag: 'input', type:'hidden' , name: this.name, value : ''
19816 // this.el.dom.removeAttribute("name");
19819 this.outerWrap = this.combo.wrap;
19820 this.wrap = cbwrap;
19822 this.outerWrap.setWidth(this.width);
19823 this.outerWrap.dom.removeChild(this.el.dom);
19825 this.wrap.dom.appendChild(this.el.dom);
19826 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19827 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19829 this.combo.trigger.setStyle('position','relative');
19830 this.combo.trigger.setStyle('left', '0px');
19831 this.combo.trigger.setStyle('top', '2px');
19833 this.combo.el.setStyle('vertical-align', 'text-bottom');
19835 //this.trigger.setStyle('vertical-align', 'top');
19837 // this should use the code from combo really... on('add' ....)
19841 this.adder = this.outerWrap.createChild(
19842 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
19844 this.adder.on('click', function(e) {
19845 _t.fireEvent('adderclick', this, e);
19849 //this.adder.on('click', this.onAddClick, _t);
19852 this.combo.on('select', function(cb, rec, ix) {
19853 this.addItem(rec.data);
19856 cb.el.dom.value = '';
19857 //cb.lastData = rec.data;
19866 getName: function()
19868 // returns hidden if it's set..
19869 if (!this.rendered) {return ''};
19870 return this.hiddenName ? this.hiddenName : this.name;
19875 onResize: function(w, h){
19878 // not sure if this is needed..
19879 //this.combo.onResize(w,h);
19881 if(typeof w != 'number'){
19882 // we do not handle it!?!?
19885 var tw = this.combo.trigger.getWidth();
19886 tw += this.addicon ? this.addicon.getWidth() : 0;
19887 tw += this.editicon ? this.editicon.getWidth() : 0;
19889 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19891 this.combo.trigger.setStyle('left', '0px');
19893 if(this.list && this.listWidth === undefined){
19894 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19895 this.list.setWidth(lw);
19896 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19903 addItem: function(rec)
19905 var valueField = this.combo.valueField;
19906 var displayField = this.combo.displayField;
19907 if (this.items.indexOfKey(rec[valueField]) > -1) {
19908 //console.log("GOT " + rec.data.id);
19912 var x = new Roo.form.ComboBoxArray.Item({
19913 //id : rec[this.idField],
19915 displayField : displayField ,
19916 tipField : displayField ,
19920 this.items.add(rec[valueField],x);
19921 // add it before the element..
19922 this.updateHiddenEl();
19923 x.render(this.outerWrap, this.wrap.dom);
19924 // add the image handler..
19927 updateHiddenEl : function()
19930 if (!this.hiddenEl) {
19934 var idField = this.combo.valueField;
19936 this.items.each(function(f) {
19937 ar.push(f.data[idField]);
19940 this.hiddenEl.dom.value = ar.join(',');
19946 this.items.clear();
19948 Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19952 this.el.dom.value = '';
19953 if (this.hiddenEl) {
19954 this.hiddenEl.dom.value = '';
19958 getValue: function()
19960 return this.hiddenEl ? this.hiddenEl.dom.value : '';
19962 setValue: function(v) // not a valid action - must use addItems..
19969 if (this.store.isLocal && (typeof(v) == 'string')) {
19970 // then we can use the store to find the values..
19971 // comma seperated at present.. this needs to allow JSON based encoding..
19972 this.hiddenEl.value = v;
19974 Roo.each(v.split(','), function(k) {
19975 Roo.log("CHECK " + this.valueField + ',' + k);
19976 var li = this.store.query(this.valueField, k);
19981 add[this.valueField] = k;
19982 add[this.displayField] = li.item(0).data[this.displayField];
19988 if (typeof(v) == 'object' ) {
19989 // then let's assume it's an array of objects..
19990 Roo.each(v, function(l) {
19998 setFromData: function(v)
20000 // this recieves an object, if setValues is called.
20002 this.el.dom.value = v[this.displayField];
20003 this.hiddenEl.dom.value = v[this.valueField];
20004 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
20007 var kv = v[this.valueField];
20008 var dv = v[this.displayField];
20009 kv = typeof(kv) != 'string' ? '' : kv;
20010 dv = typeof(dv) != 'string' ? '' : dv;
20013 var keys = kv.split(',');
20014 var display = dv.split(',');
20015 for (var i = 0 ; i < keys.length; i++) {
20018 add[this.valueField] = keys[i];
20019 add[this.displayField] = display[i];
20027 * Validates the combox array value
20028 * @return {Boolean} True if the value is valid, else false
20030 validate : function(){
20031 if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
20032 this.clearInvalid();
20038 validateValue : function(value){
20039 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
20047 isDirty : function() {
20048 if(this.disabled) {
20053 var d = Roo.decode(String(this.originalValue));
20055 return String(this.getValue()) !== String(this.originalValue);
20058 var originalValue = [];
20060 for (var i = 0; i < d.length; i++){
20061 originalValue.push(d[i][this.valueField]);
20064 return String(this.getValue()) !== String(originalValue.join(','));
20073 * @class Roo.form.ComboBoxArray.Item
20074 * @extends Roo.BoxComponent
20075 * A selected item in the list
20076 * Fred [x] Brian [x] [Pick another |v]
20079 * Create a new item.
20080 * @param {Object} config Configuration options
20083 Roo.form.ComboBoxArray.Item = function(config) {
20084 config.id = Roo.id();
20085 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20088 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20091 displayField : false,
20095 defaultAutoCreate : {
20097 cls: 'x-cbarray-item',
20104 src : Roo.BLANK_IMAGE_URL ,
20112 onRender : function(ct, position)
20114 Roo.form.Field.superclass.onRender.call(this, ct, position);
20117 var cfg = this.getAutoCreate();
20118 this.el = ct.createChild(cfg, position);
20121 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20123 this.el.child('div').dom.innerHTML = this.cb.renderer ?
20124 this.cb.renderer(this.data) :
20125 String.format('{0}',this.data[this.displayField]);
20128 this.el.child('div').dom.setAttribute('qtip',
20129 String.format('{0}',this.data[this.tipField])
20132 this.el.child('img').on('click', this.remove, this);
20136 remove : function()
20138 if(this.cb.disabled){
20142 if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20143 this.cb.items.remove(this);
20144 this.el.child('img').un('click', this.remove, this);
20146 this.cb.updateHiddenEl();
20148 this.cb.fireEvent('remove', this.cb, this);
20154 * Ext JS Library 1.1.1
20155 * Copyright(c) 2006-2007, Ext JS, LLC.
20157 * Originally Released Under LGPL - original licence link has changed is not relivant.
20160 * <script type="text/javascript">
20163 * @class Roo.form.Checkbox
20164 * @extends Roo.form.Field
20165 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
20167 * Creates a new Checkbox
20168 * @param {Object} config Configuration options
20170 Roo.form.Checkbox = function(config){
20171 Roo.form.Checkbox.superclass.constructor.call(this, config);
20175 * Fires when the checkbox is checked or unchecked.
20176 * @param {Roo.form.Checkbox} this This checkbox
20177 * @param {Boolean} checked The new checked value
20183 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
20185 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20187 focusClass : undefined,
20189 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20191 fieldClass: "x-form-field",
20193 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20197 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20198 * {tag: "input", type: "checkbox", autocomplete: "off"})
20200 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20202 * @cfg {String} boxLabel The text that appears beside the checkbox
20206 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20210 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20212 valueOff: '0', // value when not checked..
20214 actionMode : 'viewEl',
20217 itemCls : 'x-menu-check-item x-form-item',
20218 groupClass : 'x-menu-group-item',
20219 inputType : 'hidden',
20222 inSetChecked: false, // check that we are not calling self...
20224 inputElement: false, // real input element?
20225 basedOn: false, // ????
20227 isFormField: true, // not sure where this is needed!!!!
20229 onResize : function(){
20230 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20231 if(!this.boxLabel){
20232 this.el.alignTo(this.wrap, 'c-c');
20236 initEvents : function(){
20237 Roo.form.Checkbox.superclass.initEvents.call(this);
20238 this.el.on("click", this.onClick, this);
20239 this.el.on("change", this.onClick, this);
20243 getResizeEl : function(){
20247 getPositionEl : function(){
20252 onRender : function(ct, position){
20253 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20255 if(this.inputValue !== undefined){
20256 this.el.dom.value = this.inputValue;
20259 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20260 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20261 var viewEl = this.wrap.createChild({
20262 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20263 this.viewEl = viewEl;
20264 this.wrap.on('click', this.onClick, this);
20266 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20267 this.el.on('propertychange', this.setFromHidden, this); //ie
20272 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20273 // viewEl.on('click', this.onClick, this);
20275 //if(this.checked){
20276 this.setChecked(this.checked);
20278 //this.checked = this.el.dom;
20284 initValue : Roo.emptyFn,
20287 * Returns the checked state of the checkbox.
20288 * @return {Boolean} True if checked, else false
20290 getValue : function(){
20292 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20294 return this.valueOff;
20299 onClick : function(){
20300 if (this.disabled) {
20303 this.setChecked(!this.checked);
20305 //if(this.el.dom.checked != this.checked){
20306 // this.setValue(this.el.dom.checked);
20311 * Sets the checked state of the checkbox.
20312 * On is always based on a string comparison between inputValue and the param.
20313 * @param {Boolean/String} value - the value to set
20314 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20316 setValue : function(v,suppressEvent){
20319 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20320 //if(this.el && this.el.dom){
20321 // this.el.dom.checked = this.checked;
20322 // this.el.dom.defaultChecked = this.checked;
20324 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20325 //this.fireEvent("check", this, this.checked);
20328 setChecked : function(state,suppressEvent)
20330 if (this.inSetChecked) {
20331 this.checked = state;
20337 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20339 this.checked = state;
20340 if(suppressEvent !== true){
20341 this.fireEvent('check', this, state);
20343 this.inSetChecked = true;
20344 this.el.dom.value = state ? this.inputValue : this.valueOff;
20345 this.inSetChecked = false;
20348 // handle setting of hidden value by some other method!!?!?
20349 setFromHidden: function()
20354 //console.log("SET FROM HIDDEN");
20355 //alert('setFrom hidden');
20356 this.setValue(this.el.dom.value);
20359 onDestroy : function()
20362 Roo.get(this.viewEl).remove();
20365 Roo.form.Checkbox.superclass.onDestroy.call(this);
20368 setBoxLabel : function(str)
20370 this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20375 * Ext JS Library 1.1.1
20376 * Copyright(c) 2006-2007, Ext JS, LLC.
20378 * Originally Released Under LGPL - original licence link has changed is not relivant.
20381 * <script type="text/javascript">
20385 * @class Roo.form.Radio
20386 * @extends Roo.form.Checkbox
20387 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
20388 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20390 * Creates a new Radio
20391 * @param {Object} config Configuration options
20393 Roo.form.Radio = function(){
20394 Roo.form.Radio.superclass.constructor.apply(this, arguments);
20396 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20397 inputType: 'radio',
20400 * If this radio is part of a group, it will return the selected value
20403 getGroupValue : function(){
20404 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20408 onRender : function(ct, position){
20409 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20411 if(this.inputValue !== undefined){
20412 this.el.dom.value = this.inputValue;
20415 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20416 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20417 //var viewEl = this.wrap.createChild({
20418 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20419 //this.viewEl = viewEl;
20420 //this.wrap.on('click', this.onClick, this);
20422 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20423 //this.el.on('propertychange', this.setFromHidden, this); //ie
20428 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20429 // viewEl.on('click', this.onClick, this);
20432 this.el.dom.checked = 'checked' ;
20438 });//<script type="text/javascript">
20441 * Based Ext JS Library 1.1.1
20442 * Copyright(c) 2006-2007, Ext JS, LLC.
20448 * @class Roo.HtmlEditorCore
20449 * @extends Roo.Component
20450 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20452 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20455 Roo.HtmlEditorCore = function(config){
20458 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20463 * @event initialize
20464 * Fires when the editor is fully initialized (including the iframe)
20465 * @param {Roo.HtmlEditorCore} this
20470 * Fires when the editor is first receives the focus. Any insertion must wait
20471 * until after this event.
20472 * @param {Roo.HtmlEditorCore} this
20476 * @event beforesync
20477 * Fires before the textarea is updated with content from the editor iframe. Return false
20478 * to cancel the sync.
20479 * @param {Roo.HtmlEditorCore} this
20480 * @param {String} html
20484 * @event beforepush
20485 * Fires before the iframe editor is updated with content from the textarea. Return false
20486 * to cancel the push.
20487 * @param {Roo.HtmlEditorCore} this
20488 * @param {String} html
20493 * Fires when the textarea is updated with content from the editor iframe.
20494 * @param {Roo.HtmlEditorCore} this
20495 * @param {String} html
20500 * Fires when the iframe editor is updated with content from the textarea.
20501 * @param {Roo.HtmlEditorCore} this
20502 * @param {String} html
20507 * @event editorevent
20508 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20509 * @param {Roo.HtmlEditorCore} this
20515 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20517 // defaults : white / black...
20518 this.applyBlacklists();
20525 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20529 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20535 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20540 * @cfg {Number} height (in pixels)
20544 * @cfg {Number} width (in pixels)
20549 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20552 stylesheets: false,
20557 // private properties
20558 validationEvent : false,
20560 initialized : false,
20562 sourceEditMode : false,
20563 onFocus : Roo.emptyFn,
20565 hideMode:'offsets',
20569 // blacklist + whitelisted elements..
20576 * Protected method that will not generally be called directly. It
20577 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20578 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20580 getDocMarkup : function(){
20584 // inherit styels from page...??
20585 if (this.stylesheets === false) {
20587 Roo.get(document.head).select('style').each(function(node) {
20588 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20591 Roo.get(document.head).select('link').each(function(node) {
20592 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20595 } else if (!this.stylesheets.length) {
20597 st = '<style type="text/css">' +
20598 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20601 st = '<style type="text/css">' +
20606 st += '<style type="text/css">' +
20607 'IMG { cursor: pointer } ' +
20610 var cls = 'roo-htmleditor-body';
20612 if(this.bodyCls.length){
20613 cls += ' ' + this.bodyCls;
20616 return '<html><head>' + st +
20617 //<style type="text/css">' +
20618 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20620 ' </head><body class="' + cls + '"></body></html>';
20624 onRender : function(ct, position)
20627 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20628 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20631 this.el.dom.style.border = '0 none';
20632 this.el.dom.setAttribute('tabIndex', -1);
20633 this.el.addClass('x-hidden hide');
20637 if(Roo.isIE){ // fix IE 1px bogus margin
20638 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20642 this.frameId = Roo.id();
20646 var iframe = this.owner.wrap.createChild({
20648 cls: 'form-control', // bootstrap..
20650 name: this.frameId,
20651 frameBorder : 'no',
20652 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20657 this.iframe = iframe.dom;
20659 this.assignDocWin();
20661 this.doc.designMode = 'on';
20664 this.doc.write(this.getDocMarkup());
20668 var task = { // must defer to wait for browser to be ready
20670 //console.log("run task?" + this.doc.readyState);
20671 this.assignDocWin();
20672 if(this.doc.body || this.doc.readyState == 'complete'){
20674 this.doc.designMode="on";
20678 Roo.TaskMgr.stop(task);
20679 this.initEditor.defer(10, this);
20686 Roo.TaskMgr.start(task);
20691 onResize : function(w, h)
20693 Roo.log('resize: ' +w + ',' + h );
20694 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20698 if(typeof w == 'number'){
20700 this.iframe.style.width = w + 'px';
20702 if(typeof h == 'number'){
20704 this.iframe.style.height = h + 'px';
20706 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20713 * Toggles the editor between standard and source edit mode.
20714 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20716 toggleSourceEdit : function(sourceEditMode){
20718 this.sourceEditMode = sourceEditMode === true;
20720 if(this.sourceEditMode){
20722 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20725 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20726 //this.iframe.className = '';
20729 //this.setSize(this.owner.wrap.getSize());
20730 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20737 * Protected method that will not generally be called directly. If you need/want
20738 * custom HTML cleanup, this is the method you should override.
20739 * @param {String} html The HTML to be cleaned
20740 * return {String} The cleaned HTML
20742 cleanHtml : function(html){
20743 html = String(html);
20744 if(html.length > 5){
20745 if(Roo.isSafari){ // strip safari nonsense
20746 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20749 if(html == ' '){
20756 * HTML Editor -> Textarea
20757 * Protected method that will not generally be called directly. Syncs the contents
20758 * of the editor iframe with the textarea.
20760 syncValue : function(){
20761 if(this.initialized){
20762 var bd = (this.doc.body || this.doc.documentElement);
20763 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20764 var html = bd.innerHTML;
20766 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20767 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20769 html = '<div style="'+m[0]+'">' + html + '</div>';
20772 html = this.cleanHtml(html);
20773 // fix up the special chars.. normaly like back quotes in word...
20774 // however we do not want to do this with chinese..
20775 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20776 var cc = b.charCodeAt();
20778 (cc >= 0x4E00 && cc < 0xA000 ) ||
20779 (cc >= 0x3400 && cc < 0x4E00 ) ||
20780 (cc >= 0xf900 && cc < 0xfb00 )
20786 if(this.owner.fireEvent('beforesync', this, html) !== false){
20787 this.el.dom.value = html;
20788 this.owner.fireEvent('sync', this, html);
20794 * Protected method that will not generally be called directly. Pushes the value of the textarea
20795 * into the iframe editor.
20797 pushValue : function(){
20798 if(this.initialized){
20799 var v = this.el.dom.value.trim();
20801 // if(v.length < 1){
20805 if(this.owner.fireEvent('beforepush', this, v) !== false){
20806 var d = (this.doc.body || this.doc.documentElement);
20808 this.cleanUpPaste();
20809 this.el.dom.value = d.innerHTML;
20810 this.owner.fireEvent('push', this, v);
20816 deferFocus : function(){
20817 this.focus.defer(10, this);
20821 focus : function(){
20822 if(this.win && !this.sourceEditMode){
20829 assignDocWin: function()
20831 var iframe = this.iframe;
20834 this.doc = iframe.contentWindow.document;
20835 this.win = iframe.contentWindow;
20837 // if (!Roo.get(this.frameId)) {
20840 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20841 // this.win = Roo.get(this.frameId).dom.contentWindow;
20843 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20847 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20848 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20853 initEditor : function(){
20854 //console.log("INIT EDITOR");
20855 this.assignDocWin();
20859 this.doc.designMode="on";
20861 this.doc.write(this.getDocMarkup());
20864 var dbody = (this.doc.body || this.doc.documentElement);
20865 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20866 // this copies styles from the containing element into thsi one..
20867 // not sure why we need all of this..
20868 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20870 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20871 //ss['background-attachment'] = 'fixed'; // w3c
20872 dbody.bgProperties = 'fixed'; // ie
20873 //Roo.DomHelper.applyStyles(dbody, ss);
20874 Roo.EventManager.on(this.doc, {
20875 //'mousedown': this.onEditorEvent,
20876 'mouseup': this.onEditorEvent,
20877 'dblclick': this.onEditorEvent,
20878 'click': this.onEditorEvent,
20879 'keyup': this.onEditorEvent,
20884 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20886 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20887 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20889 this.initialized = true;
20891 this.owner.fireEvent('initialize', this);
20896 onDestroy : function(){
20902 //for (var i =0; i < this.toolbars.length;i++) {
20903 // // fixme - ask toolbars for heights?
20904 // this.toolbars[i].onDestroy();
20907 //this.wrap.dom.innerHTML = '';
20908 //this.wrap.remove();
20913 onFirstFocus : function(){
20915 this.assignDocWin();
20918 this.activated = true;
20921 if(Roo.isGecko){ // prevent silly gecko errors
20923 var s = this.win.getSelection();
20924 if(!s.focusNode || s.focusNode.nodeType != 3){
20925 var r = s.getRangeAt(0);
20926 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20931 this.execCmd('useCSS', true);
20932 this.execCmd('styleWithCSS', false);
20935 this.owner.fireEvent('activate', this);
20939 adjustFont: function(btn){
20940 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20941 //if(Roo.isSafari){ // safari
20944 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20945 if(Roo.isSafari){ // safari
20946 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20947 v = (v < 10) ? 10 : v;
20948 v = (v > 48) ? 48 : v;
20949 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20954 v = Math.max(1, v+adjust);
20956 this.execCmd('FontSize', v );
20959 onEditorEvent : function(e)
20961 this.owner.fireEvent('editorevent', this, e);
20962 // this.updateToolbar();
20963 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20966 insertTag : function(tg)
20968 // could be a bit smarter... -> wrap the current selected tRoo..
20969 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20971 range = this.createRange(this.getSelection());
20972 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20973 wrappingNode.appendChild(range.extractContents());
20974 range.insertNode(wrappingNode);
20981 this.execCmd("formatblock", tg);
20985 insertText : function(txt)
20989 var range = this.createRange();
20990 range.deleteContents();
20991 //alert(Sender.getAttribute('label'));
20993 range.insertNode(this.doc.createTextNode(txt));
20999 * Executes a Midas editor command on the editor document and performs necessary focus and
21000 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21001 * @param {String} cmd The Midas command
21002 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21004 relayCmd : function(cmd, value){
21006 this.execCmd(cmd, value);
21007 this.owner.fireEvent('editorevent', this);
21008 //this.updateToolbar();
21009 this.owner.deferFocus();
21013 * Executes a Midas editor command directly on the editor document.
21014 * For visual commands, you should use {@link #relayCmd} instead.
21015 * <b>This should only be called after the editor is initialized.</b>
21016 * @param {String} cmd The Midas command
21017 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21019 execCmd : function(cmd, value){
21020 this.doc.execCommand(cmd, false, value === undefined ? null : value);
21027 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21029 * @param {String} text | dom node..
21031 insertAtCursor : function(text)
21034 if(!this.activated){
21040 var r = this.doc.selection.createRange();
21051 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21055 // from jquery ui (MIT licenced)
21057 var win = this.win;
21059 if (win.getSelection && win.getSelection().getRangeAt) {
21060 range = win.getSelection().getRangeAt(0);
21061 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21062 range.insertNode(node);
21063 } else if (win.document.selection && win.document.selection.createRange) {
21064 // no firefox support
21065 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21066 win.document.selection.createRange().pasteHTML(txt);
21068 // no firefox support
21069 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21070 this.execCmd('InsertHTML', txt);
21079 mozKeyPress : function(e){
21081 var c = e.getCharCode(), cmd;
21084 c = String.fromCharCode(c).toLowerCase();
21098 this.cleanUpPaste.defer(100, this);
21106 e.preventDefault();
21114 fixKeys : function(){ // load time branching for fastest keydown performance
21116 return function(e){
21117 var k = e.getKey(), r;
21120 r = this.doc.selection.createRange();
21123 r.pasteHTML('    ');
21130 r = this.doc.selection.createRange();
21132 var target = r.parentElement();
21133 if(!target || target.tagName.toLowerCase() != 'li'){
21135 r.pasteHTML('<br />');
21141 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21142 this.cleanUpPaste.defer(100, this);
21148 }else if(Roo.isOpera){
21149 return function(e){
21150 var k = e.getKey();
21154 this.execCmd('InsertHTML','    ');
21157 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21158 this.cleanUpPaste.defer(100, this);
21163 }else if(Roo.isSafari){
21164 return function(e){
21165 var k = e.getKey();
21169 this.execCmd('InsertText','\t');
21173 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21174 this.cleanUpPaste.defer(100, this);
21182 getAllAncestors: function()
21184 var p = this.getSelectedNode();
21187 a.push(p); // push blank onto stack..
21188 p = this.getParentElement();
21192 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21196 a.push(this.doc.body);
21200 lastSelNode : false,
21203 getSelection : function()
21205 this.assignDocWin();
21206 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21209 getSelectedNode: function()
21211 // this may only work on Gecko!!!
21213 // should we cache this!!!!
21218 var range = this.createRange(this.getSelection()).cloneRange();
21221 var parent = range.parentElement();
21223 var testRange = range.duplicate();
21224 testRange.moveToElementText(parent);
21225 if (testRange.inRange(range)) {
21228 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21231 parent = parent.parentElement;
21236 // is ancestor a text element.
21237 var ac = range.commonAncestorContainer;
21238 if (ac.nodeType == 3) {
21239 ac = ac.parentNode;
21242 var ar = ac.childNodes;
21245 var other_nodes = [];
21246 var has_other_nodes = false;
21247 for (var i=0;i<ar.length;i++) {
21248 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21251 // fullly contained node.
21253 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21258 // probably selected..
21259 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21260 other_nodes.push(ar[i]);
21264 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21269 has_other_nodes = true;
21271 if (!nodes.length && other_nodes.length) {
21272 nodes= other_nodes;
21274 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21280 createRange: function(sel)
21282 // this has strange effects when using with
21283 // top toolbar - not sure if it's a great idea.
21284 //this.editor.contentWindow.focus();
21285 if (typeof sel != "undefined") {
21287 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21289 return this.doc.createRange();
21292 return this.doc.createRange();
21295 getParentElement: function()
21298 this.assignDocWin();
21299 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21301 var range = this.createRange(sel);
21304 var p = range.commonAncestorContainer;
21305 while (p.nodeType == 3) { // text node
21316 * Range intersection.. the hard stuff...
21320 * [ -- selected range --- ]
21324 * if end is before start or hits it. fail.
21325 * if start is after end or hits it fail.
21327 * if either hits (but other is outside. - then it's not
21333 // @see http://www.thismuchiknow.co.uk/?p=64.
21334 rangeIntersectsNode : function(range, node)
21336 var nodeRange = node.ownerDocument.createRange();
21338 nodeRange.selectNode(node);
21340 nodeRange.selectNodeContents(node);
21343 var rangeStartRange = range.cloneRange();
21344 rangeStartRange.collapse(true);
21346 var rangeEndRange = range.cloneRange();
21347 rangeEndRange.collapse(false);
21349 var nodeStartRange = nodeRange.cloneRange();
21350 nodeStartRange.collapse(true);
21352 var nodeEndRange = nodeRange.cloneRange();
21353 nodeEndRange.collapse(false);
21355 return rangeStartRange.compareBoundaryPoints(
21356 Range.START_TO_START, nodeEndRange) == -1 &&
21357 rangeEndRange.compareBoundaryPoints(
21358 Range.START_TO_START, nodeStartRange) == 1;
21362 rangeCompareNode : function(range, node)
21364 var nodeRange = node.ownerDocument.createRange();
21366 nodeRange.selectNode(node);
21368 nodeRange.selectNodeContents(node);
21372 range.collapse(true);
21374 nodeRange.collapse(true);
21376 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21377 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21379 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21381 var nodeIsBefore = ss == 1;
21382 var nodeIsAfter = ee == -1;
21384 if (nodeIsBefore && nodeIsAfter) {
21387 if (!nodeIsBefore && nodeIsAfter) {
21388 return 1; //right trailed.
21391 if (nodeIsBefore && !nodeIsAfter) {
21392 return 2; // left trailed.
21398 // private? - in a new class?
21399 cleanUpPaste : function()
21401 // cleans up the whole document..
21402 Roo.log('cleanuppaste');
21404 this.cleanUpChildren(this.doc.body);
21405 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21406 if (clean != this.doc.body.innerHTML) {
21407 this.doc.body.innerHTML = clean;
21412 cleanWordChars : function(input) {// change the chars to hex code
21413 var he = Roo.HtmlEditorCore;
21415 var output = input;
21416 Roo.each(he.swapCodes, function(sw) {
21417 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21419 output = output.replace(swapper, sw[1]);
21426 cleanUpChildren : function (n)
21428 if (!n.childNodes.length) {
21431 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21432 this.cleanUpChild(n.childNodes[i]);
21439 cleanUpChild : function (node)
21442 //console.log(node);
21443 if (node.nodeName == "#text") {
21444 // clean up silly Windows -- stuff?
21447 if (node.nodeName == "#comment") {
21448 node.parentNode.removeChild(node);
21449 // clean up silly Windows -- stuff?
21452 var lcname = node.tagName.toLowerCase();
21453 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21454 // whitelist of tags..
21456 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21458 node.parentNode.removeChild(node);
21463 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21465 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21466 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21468 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21469 // remove_keep_children = true;
21472 if (remove_keep_children) {
21473 this.cleanUpChildren(node);
21474 // inserts everything just before this node...
21475 while (node.childNodes.length) {
21476 var cn = node.childNodes[0];
21477 node.removeChild(cn);
21478 node.parentNode.insertBefore(cn, node);
21480 node.parentNode.removeChild(node);
21484 if (!node.attributes || !node.attributes.length) {
21485 this.cleanUpChildren(node);
21489 function cleanAttr(n,v)
21492 if (v.match(/^\./) || v.match(/^\//)) {
21495 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21498 if (v.match(/^#/)) {
21501 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21502 node.removeAttribute(n);
21506 var cwhite = this.cwhite;
21507 var cblack = this.cblack;
21509 function cleanStyle(n,v)
21511 if (v.match(/expression/)) { //XSS?? should we even bother..
21512 node.removeAttribute(n);
21516 var parts = v.split(/;/);
21519 Roo.each(parts, function(p) {
21520 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21524 var l = p.split(':').shift().replace(/\s+/g,'');
21525 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21527 if ( cwhite.length && cblack.indexOf(l) > -1) {
21528 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21529 //node.removeAttribute(n);
21533 // only allow 'c whitelisted system attributes'
21534 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21535 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21536 //node.removeAttribute(n);
21546 if (clean.length) {
21547 node.setAttribute(n, clean.join(';'));
21549 node.removeAttribute(n);
21555 for (var i = node.attributes.length-1; i > -1 ; i--) {
21556 var a = node.attributes[i];
21559 if (a.name.toLowerCase().substr(0,2)=='on') {
21560 node.removeAttribute(a.name);
21563 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21564 node.removeAttribute(a.name);
21567 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21568 cleanAttr(a.name,a.value); // fixme..
21571 if (a.name == 'style') {
21572 cleanStyle(a.name,a.value);
21575 /// clean up MS crap..
21576 // tecnically this should be a list of valid class'es..
21579 if (a.name == 'class') {
21580 if (a.value.match(/^Mso/)) {
21581 node.className = '';
21584 if (a.value.match(/^body$/)) {
21585 node.className = '';
21596 this.cleanUpChildren(node);
21602 * Clean up MS wordisms...
21604 cleanWord : function(node)
21609 this.cleanWord(this.doc.body);
21612 if (node.nodeName == "#text") {
21613 // clean up silly Windows -- stuff?
21616 if (node.nodeName == "#comment") {
21617 node.parentNode.removeChild(node);
21618 // clean up silly Windows -- stuff?
21622 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21623 node.parentNode.removeChild(node);
21627 // remove - but keep children..
21628 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21629 while (node.childNodes.length) {
21630 var cn = node.childNodes[0];
21631 node.removeChild(cn);
21632 node.parentNode.insertBefore(cn, node);
21634 node.parentNode.removeChild(node);
21635 this.iterateChildren(node, this.cleanWord);
21639 if (node.className.length) {
21641 var cn = node.className.split(/\W+/);
21643 Roo.each(cn, function(cls) {
21644 if (cls.match(/Mso[a-zA-Z]+/)) {
21649 node.className = cna.length ? cna.join(' ') : '';
21651 node.removeAttribute("class");
21655 if (node.hasAttribute("lang")) {
21656 node.removeAttribute("lang");
21659 if (node.hasAttribute("style")) {
21661 var styles = node.getAttribute("style").split(";");
21663 Roo.each(styles, function(s) {
21664 if (!s.match(/:/)) {
21667 var kv = s.split(":");
21668 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21671 // what ever is left... we allow.
21674 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21675 if (!nstyle.length) {
21676 node.removeAttribute('style');
21679 this.iterateChildren(node, this.cleanWord);
21685 * iterateChildren of a Node, calling fn each time, using this as the scole..
21686 * @param {DomNode} node node to iterate children of.
21687 * @param {Function} fn method of this class to call on each item.
21689 iterateChildren : function(node, fn)
21691 if (!node.childNodes.length) {
21694 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21695 fn.call(this, node.childNodes[i])
21701 * cleanTableWidths.
21703 * Quite often pasting from word etc.. results in tables with column and widths.
21704 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21707 cleanTableWidths : function(node)
21712 this.cleanTableWidths(this.doc.body);
21717 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21720 Roo.log(node.tagName);
21721 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21722 this.iterateChildren(node, this.cleanTableWidths);
21725 if (node.hasAttribute('width')) {
21726 node.removeAttribute('width');
21730 if (node.hasAttribute("style")) {
21733 var styles = node.getAttribute("style").split(";");
21735 Roo.each(styles, function(s) {
21736 if (!s.match(/:/)) {
21739 var kv = s.split(":");
21740 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21743 // what ever is left... we allow.
21746 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21747 if (!nstyle.length) {
21748 node.removeAttribute('style');
21752 this.iterateChildren(node, this.cleanTableWidths);
21760 domToHTML : function(currentElement, depth, nopadtext) {
21762 depth = depth || 0;
21763 nopadtext = nopadtext || false;
21765 if (!currentElement) {
21766 return this.domToHTML(this.doc.body);
21769 //Roo.log(currentElement);
21771 var allText = false;
21772 var nodeName = currentElement.nodeName;
21773 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21775 if (nodeName == '#text') {
21777 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21782 if (nodeName != 'BODY') {
21785 // Prints the node tagName, such as <A>, <IMG>, etc
21788 for(i = 0; i < currentElement.attributes.length;i++) {
21790 var aname = currentElement.attributes.item(i).name;
21791 if (!currentElement.attributes.item(i).value.length) {
21794 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21797 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21806 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21809 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21814 // Traverse the tree
21816 var currentElementChild = currentElement.childNodes.item(i);
21817 var allText = true;
21818 var innerHTML = '';
21820 while (currentElementChild) {
21821 // Formatting code (indent the tree so it looks nice on the screen)
21822 var nopad = nopadtext;
21823 if (lastnode == 'SPAN') {
21827 if (currentElementChild.nodeName == '#text') {
21828 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21829 toadd = nopadtext ? toadd : toadd.trim();
21830 if (!nopad && toadd.length > 80) {
21831 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21833 innerHTML += toadd;
21836 currentElementChild = currentElement.childNodes.item(i);
21842 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21844 // Recursively traverse the tree structure of the child node
21845 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21846 lastnode = currentElementChild.nodeName;
21848 currentElementChild=currentElement.childNodes.item(i);
21854 // The remaining code is mostly for formatting the tree
21855 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21860 ret+= "</"+tagName+">";
21866 applyBlacklists : function()
21868 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21869 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21873 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21874 if (b.indexOf(tag) > -1) {
21877 this.white.push(tag);
21881 Roo.each(w, function(tag) {
21882 if (b.indexOf(tag) > -1) {
21885 if (this.white.indexOf(tag) > -1) {
21888 this.white.push(tag);
21893 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21894 if (w.indexOf(tag) > -1) {
21897 this.black.push(tag);
21901 Roo.each(b, function(tag) {
21902 if (w.indexOf(tag) > -1) {
21905 if (this.black.indexOf(tag) > -1) {
21908 this.black.push(tag);
21913 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21914 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21918 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21919 if (b.indexOf(tag) > -1) {
21922 this.cwhite.push(tag);
21926 Roo.each(w, function(tag) {
21927 if (b.indexOf(tag) > -1) {
21930 if (this.cwhite.indexOf(tag) > -1) {
21933 this.cwhite.push(tag);
21938 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21939 if (w.indexOf(tag) > -1) {
21942 this.cblack.push(tag);
21946 Roo.each(b, function(tag) {
21947 if (w.indexOf(tag) > -1) {
21950 if (this.cblack.indexOf(tag) > -1) {
21953 this.cblack.push(tag);
21958 setStylesheets : function(stylesheets)
21960 if(typeof(stylesheets) == 'string'){
21961 Roo.get(this.iframe.contentDocument.head).createChild({
21963 rel : 'stylesheet',
21972 Roo.each(stylesheets, function(s) {
21977 Roo.get(_this.iframe.contentDocument.head).createChild({
21979 rel : 'stylesheet',
21988 removeStylesheets : function()
21992 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21997 setStyle : function(style)
21999 Roo.get(this.iframe.contentDocument.head).createChild({
22008 // hide stuff that is not compatible
22022 * @event specialkey
22026 * @cfg {String} fieldClass @hide
22029 * @cfg {String} focusClass @hide
22032 * @cfg {String} autoCreate @hide
22035 * @cfg {String} inputType @hide
22038 * @cfg {String} invalidClass @hide
22041 * @cfg {String} invalidText @hide
22044 * @cfg {String} msgFx @hide
22047 * @cfg {String} validateOnBlur @hide
22051 Roo.HtmlEditorCore.white = [
22052 'area', 'br', 'img', 'input', 'hr', 'wbr',
22054 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
22055 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
22056 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
22057 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
22058 'table', 'ul', 'xmp',
22060 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
22063 'dir', 'menu', 'ol', 'ul', 'dl',
22069 Roo.HtmlEditorCore.black = [
22070 // 'embed', 'object', // enable - backend responsiblity to clean thiese
22072 'base', 'basefont', 'bgsound', 'blink', 'body',
22073 'frame', 'frameset', 'head', 'html', 'ilayer',
22074 'iframe', 'layer', 'link', 'meta', 'object',
22075 'script', 'style' ,'title', 'xml' // clean later..
22077 Roo.HtmlEditorCore.clean = [
22078 'script', 'style', 'title', 'xml'
22080 Roo.HtmlEditorCore.remove = [
22085 Roo.HtmlEditorCore.ablack = [
22089 Roo.HtmlEditorCore.aclean = [
22090 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
22094 Roo.HtmlEditorCore.pwhite= [
22095 'http', 'https', 'mailto'
22098 // white listed style attributes.
22099 Roo.HtmlEditorCore.cwhite= [
22100 // 'text-align', /// default is to allow most things..
22106 // black listed style attributes.
22107 Roo.HtmlEditorCore.cblack= [
22108 // 'font-size' -- this can be set by the project
22112 Roo.HtmlEditorCore.swapCodes =[
22123 //<script type="text/javascript">
22126 * Ext JS Library 1.1.1
22127 * Copyright(c) 2006-2007, Ext JS, LLC.
22133 Roo.form.HtmlEditor = function(config){
22137 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22139 if (!this.toolbars) {
22140 this.toolbars = [];
22142 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22148 * @class Roo.form.HtmlEditor
22149 * @extends Roo.form.Field
22150 * Provides a lightweight HTML Editor component.
22152 * This has been tested on Fireforx / Chrome.. IE may not be so great..
22154 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22155 * supported by this editor.</b><br/><br/>
22156 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22157 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22159 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22161 * @cfg {Boolean} clearUp
22165 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22170 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22175 * @cfg {Number} height (in pixels)
22179 * @cfg {Number} width (in pixels)
22184 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22187 stylesheets: false,
22191 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22196 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22202 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22207 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22215 // private properties
22216 validationEvent : false,
22218 initialized : false,
22221 onFocus : Roo.emptyFn,
22223 hideMode:'offsets',
22225 actionMode : 'container', // defaults to hiding it...
22227 defaultAutoCreate : { // modified by initCompnoent..
22229 style:"width:500px;height:300px;",
22230 autocomplete: "new-password"
22234 initComponent : function(){
22237 * @event initialize
22238 * Fires when the editor is fully initialized (including the iframe)
22239 * @param {HtmlEditor} this
22244 * Fires when the editor is first receives the focus. Any insertion must wait
22245 * until after this event.
22246 * @param {HtmlEditor} this
22250 * @event beforesync
22251 * Fires before the textarea is updated with content from the editor iframe. Return false
22252 * to cancel the sync.
22253 * @param {HtmlEditor} this
22254 * @param {String} html
22258 * @event beforepush
22259 * Fires before the iframe editor is updated with content from the textarea. Return false
22260 * to cancel the push.
22261 * @param {HtmlEditor} this
22262 * @param {String} html
22267 * Fires when the textarea is updated with content from the editor iframe.
22268 * @param {HtmlEditor} this
22269 * @param {String} html
22274 * Fires when the iframe editor is updated with content from the textarea.
22275 * @param {HtmlEditor} this
22276 * @param {String} html
22280 * @event editmodechange
22281 * Fires when the editor switches edit modes
22282 * @param {HtmlEditor} this
22283 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22285 editmodechange: true,
22287 * @event editorevent
22288 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22289 * @param {HtmlEditor} this
22293 * @event firstfocus
22294 * Fires when on first focus - needed by toolbars..
22295 * @param {HtmlEditor} this
22300 * Auto save the htmlEditor value as a file into Events
22301 * @param {HtmlEditor} this
22305 * @event savedpreview
22306 * preview the saved version of htmlEditor
22307 * @param {HtmlEditor} this
22309 savedpreview: true,
22312 * @event stylesheetsclick
22313 * Fires when press the Sytlesheets button
22314 * @param {Roo.HtmlEditorCore} this
22316 stylesheetsclick: true
22318 this.defaultAutoCreate = {
22320 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22321 autocomplete: "new-password"
22326 * Protected method that will not generally be called directly. It
22327 * is called when the editor creates its toolbar. Override this method if you need to
22328 * add custom toolbar buttons.
22329 * @param {HtmlEditor} editor
22331 createToolbar : function(editor){
22332 Roo.log("create toolbars");
22333 if (!editor.toolbars || !editor.toolbars.length) {
22334 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22337 for (var i =0 ; i < editor.toolbars.length;i++) {
22338 editor.toolbars[i] = Roo.factory(
22339 typeof(editor.toolbars[i]) == 'string' ?
22340 { xtype: editor.toolbars[i]} : editor.toolbars[i],
22341 Roo.form.HtmlEditor);
22342 editor.toolbars[i].init(editor);
22350 onRender : function(ct, position)
22353 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22355 this.wrap = this.el.wrap({
22356 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22359 this.editorcore.onRender(ct, position);
22361 if (this.resizable) {
22362 this.resizeEl = new Roo.Resizable(this.wrap, {
22366 minHeight : this.height,
22367 height: this.height,
22368 handles : this.resizable,
22371 resize : function(r, w, h) {
22372 _t.onResize(w,h); // -something
22378 this.createToolbar(this);
22382 this.setSize(this.wrap.getSize());
22384 if (this.resizeEl) {
22385 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22386 // should trigger onReize..
22389 this.keyNav = new Roo.KeyNav(this.el, {
22391 "tab" : function(e){
22392 e.preventDefault();
22394 var value = this.getValue();
22396 var start = this.el.dom.selectionStart;
22397 var end = this.el.dom.selectionEnd;
22401 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22402 this.el.dom.setSelectionRange(end + 1, end + 1);
22406 var f = value.substring(0, start).split("\t");
22408 if(f.pop().length != 0){
22412 this.setValue(f.join("\t") + value.substring(end));
22413 this.el.dom.setSelectionRange(start - 1, start - 1);
22417 "home" : function(e){
22418 e.preventDefault();
22420 var curr = this.el.dom.selectionStart;
22421 var lines = this.getValue().split("\n");
22428 this.el.dom.setSelectionRange(0, 0);
22434 for (var i = 0; i < lines.length;i++) {
22435 pos += lines[i].length;
22445 pos -= lines[i].length;
22451 this.el.dom.setSelectionRange(pos, pos);
22455 this.el.dom.selectionStart = pos;
22456 this.el.dom.selectionEnd = curr;
22459 "end" : function(e){
22460 e.preventDefault();
22462 var curr = this.el.dom.selectionStart;
22463 var lines = this.getValue().split("\n");
22470 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22476 for (var i = 0; i < lines.length;i++) {
22478 pos += lines[i].length;
22492 this.el.dom.setSelectionRange(pos, pos);
22496 this.el.dom.selectionStart = curr;
22497 this.el.dom.selectionEnd = pos;
22502 doRelay : function(foo, bar, hname){
22503 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22509 // if(this.autosave && this.w){
22510 // this.autoSaveFn = setInterval(this.autosave, 1000);
22515 onResize : function(w, h)
22517 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22522 if(typeof w == 'number'){
22523 var aw = w - this.wrap.getFrameWidth('lr');
22524 this.el.setWidth(this.adjustWidth('textarea', aw));
22527 if(typeof h == 'number'){
22529 for (var i =0; i < this.toolbars.length;i++) {
22530 // fixme - ask toolbars for heights?
22531 tbh += this.toolbars[i].tb.el.getHeight();
22532 if (this.toolbars[i].footer) {
22533 tbh += this.toolbars[i].footer.el.getHeight();
22540 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22541 ah -= 5; // knock a few pixes off for look..
22543 this.el.setHeight(this.adjustWidth('textarea', ah));
22547 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22548 this.editorcore.onResize(ew,eh);
22553 * Toggles the editor between standard and source edit mode.
22554 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22556 toggleSourceEdit : function(sourceEditMode)
22558 this.editorcore.toggleSourceEdit(sourceEditMode);
22560 if(this.editorcore.sourceEditMode){
22561 Roo.log('editor - showing textarea');
22564 // Roo.log(this.syncValue());
22565 this.editorcore.syncValue();
22566 this.el.removeClass('x-hidden');
22567 this.el.dom.removeAttribute('tabIndex');
22570 for (var i = 0; i < this.toolbars.length; i++) {
22571 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22572 this.toolbars[i].tb.hide();
22573 this.toolbars[i].footer.hide();
22578 Roo.log('editor - hiding textarea');
22580 // Roo.log(this.pushValue());
22581 this.editorcore.pushValue();
22583 this.el.addClass('x-hidden');
22584 this.el.dom.setAttribute('tabIndex', -1);
22586 for (var i = 0; i < this.toolbars.length; i++) {
22587 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22588 this.toolbars[i].tb.show();
22589 this.toolbars[i].footer.show();
22593 //this.deferFocus();
22596 this.setSize(this.wrap.getSize());
22597 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22599 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22602 // private (for BoxComponent)
22603 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22605 // private (for BoxComponent)
22606 getResizeEl : function(){
22610 // private (for BoxComponent)
22611 getPositionEl : function(){
22616 initEvents : function(){
22617 this.originalValue = this.getValue();
22621 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22624 markInvalid : Roo.emptyFn,
22626 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22629 clearInvalid : Roo.emptyFn,
22631 setValue : function(v){
22632 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22633 this.editorcore.pushValue();
22638 deferFocus : function(){
22639 this.focus.defer(10, this);
22643 focus : function(){
22644 this.editorcore.focus();
22650 onDestroy : function(){
22656 for (var i =0; i < this.toolbars.length;i++) {
22657 // fixme - ask toolbars for heights?
22658 this.toolbars[i].onDestroy();
22661 this.wrap.dom.innerHTML = '';
22662 this.wrap.remove();
22667 onFirstFocus : function(){
22668 //Roo.log("onFirstFocus");
22669 this.editorcore.onFirstFocus();
22670 for (var i =0; i < this.toolbars.length;i++) {
22671 this.toolbars[i].onFirstFocus();
22677 syncValue : function()
22679 this.editorcore.syncValue();
22682 pushValue : function()
22684 this.editorcore.pushValue();
22687 setStylesheets : function(stylesheets)
22689 this.editorcore.setStylesheets(stylesheets);
22692 removeStylesheets : function()
22694 this.editorcore.removeStylesheets();
22698 // hide stuff that is not compatible
22712 * @event specialkey
22716 * @cfg {String} fieldClass @hide
22719 * @cfg {String} focusClass @hide
22722 * @cfg {String} autoCreate @hide
22725 * @cfg {String} inputType @hide
22728 * @cfg {String} invalidClass @hide
22731 * @cfg {String} invalidText @hide
22734 * @cfg {String} msgFx @hide
22737 * @cfg {String} validateOnBlur @hide
22741 // <script type="text/javascript">
22744 * Ext JS Library 1.1.1
22745 * Copyright(c) 2006-2007, Ext JS, LLC.
22751 * @class Roo.form.HtmlEditorToolbar1
22756 new Roo.form.HtmlEditor({
22759 new Roo.form.HtmlEditorToolbar1({
22760 disable : { fonts: 1 , format: 1, ..., ... , ...],
22766 * @cfg {Object} disable List of elements to disable..
22767 * @cfg {Array} btns List of additional buttons.
22771 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22774 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22777 Roo.apply(this, config);
22779 // default disabled, based on 'good practice'..
22780 this.disable = this.disable || {};
22781 Roo.applyIf(this.disable, {
22784 specialElements : true
22788 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22789 // dont call parent... till later.
22792 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype, {
22799 editorcore : false,
22801 * @cfg {Object} disable List of toolbar elements to disable
22808 * @cfg {String} createLinkText The default text for the create link prompt
22810 createLinkText : 'Please enter the URL for the link:',
22812 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22814 defaultLinkValue : 'http:/'+'/',
22818 * @cfg {Array} fontFamilies An array of available font families
22836 // "á" , ?? a acute?
22841 "°" // , // degrees
22843 // "é" , // e ecute
22844 // "ú" , // u ecute?
22847 specialElements : [
22849 text: "Insert Table",
22852 ihtml : '<table><tr><td>Cell</td></tr></table>'
22856 text: "Insert Image",
22859 ihtml : '<img src="about:blank"/>'
22868 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
22869 "input:submit", "input:button", "select", "textarea", "label" ],
22872 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
22874 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22882 * @cfg {String} defaultFont default font to use.
22884 defaultFont: 'tahoma',
22886 fontSelect : false,
22889 formatCombo : false,
22891 init : function(editor)
22893 this.editor = editor;
22894 this.editorcore = editor.editorcore ? editor.editorcore : editor;
22895 var editorcore = this.editorcore;
22899 var fid = editorcore.frameId;
22901 function btn(id, toggle, handler){
22902 var xid = fid + '-'+ id ;
22906 cls : 'x-btn-icon x-edit-'+id,
22907 enableToggle:toggle !== false,
22908 scope: _t, // was editor...
22909 handler:handler||_t.relayBtnCmd,
22910 clickEvent:'mousedown',
22911 tooltip: etb.buttonTips[id] || undefined, ///tips ???
22918 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
22920 // stop form submits
22921 tb.el.on('click', function(e){
22922 e.preventDefault(); // what does this do?
22925 if(!this.disable.font) { // && !Roo.isSafari){
22926 /* why no safari for fonts
22927 editor.fontSelect = tb.el.createChild({
22930 cls:'x-font-select',
22931 html: this.createFontOptions()
22934 editor.fontSelect.on('change', function(){
22935 var font = editor.fontSelect.dom.value;
22936 editor.relayCmd('fontname', font);
22937 editor.deferFocus();
22941 editor.fontSelect.dom,
22947 if(!this.disable.formats){
22948 this.formatCombo = new Roo.form.ComboBox({
22949 store: new Roo.data.SimpleStore({
22952 data : this.formats // from states.js
22956 //autoCreate : {tag: "div", size: "20"},
22957 displayField:'tag',
22961 triggerAction: 'all',
22962 emptyText:'Add tag',
22963 selectOnFocus:true,
22966 'select': function(c, r, i) {
22967 editorcore.insertTag(r.get('tag'));
22973 tb.addField(this.formatCombo);
22977 if(!this.disable.format){
22982 btn('strikethrough')
22985 if(!this.disable.fontSize){
22990 btn('increasefontsize', false, editorcore.adjustFont),
22991 btn('decreasefontsize', false, editorcore.adjustFont)
22996 if(!this.disable.colors){
22999 id:editorcore.frameId +'-forecolor',
23000 cls:'x-btn-icon x-edit-forecolor',
23001 clickEvent:'mousedown',
23002 tooltip: this.buttonTips['forecolor'] || undefined,
23004 menu : new Roo.menu.ColorMenu({
23005 allowReselect: true,
23006 focus: Roo.emptyFn,
23009 selectHandler: function(cp, color){
23010 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23011 editor.deferFocus();
23014 clickEvent:'mousedown'
23017 id:editorcore.frameId +'backcolor',
23018 cls:'x-btn-icon x-edit-backcolor',
23019 clickEvent:'mousedown',
23020 tooltip: this.buttonTips['backcolor'] || undefined,
23022 menu : new Roo.menu.ColorMenu({
23023 focus: Roo.emptyFn,
23026 allowReselect: true,
23027 selectHandler: function(cp, color){
23029 editorcore.execCmd('useCSS', false);
23030 editorcore.execCmd('hilitecolor', color);
23031 editorcore.execCmd('useCSS', true);
23032 editor.deferFocus();
23034 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
23035 Roo.isSafari || Roo.isIE ? '#'+color : color);
23036 editor.deferFocus();
23040 clickEvent:'mousedown'
23045 // now add all the items...
23048 if(!this.disable.alignments){
23051 btn('justifyleft'),
23052 btn('justifycenter'),
23053 btn('justifyright')
23057 //if(!Roo.isSafari){
23058 if(!this.disable.links){
23061 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
23065 if(!this.disable.lists){
23068 btn('insertorderedlist'),
23069 btn('insertunorderedlist')
23072 if(!this.disable.sourceEdit){
23075 btn('sourceedit', true, function(btn){
23076 this.toggleSourceEdit(btn.pressed);
23083 // special menu.. - needs to be tidied up..
23084 if (!this.disable.special) {
23087 cls: 'x-edit-none',
23093 for (var i =0; i < this.specialChars.length; i++) {
23094 smenu.menu.items.push({
23096 html: this.specialChars[i],
23097 handler: function(a,b) {
23098 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23099 //editor.insertAtCursor(a.html);
23113 if (!this.disable.cleanStyles) {
23115 cls: 'x-btn-icon x-btn-clear',
23121 for (var i =0; i < this.cleanStyles.length; i++) {
23122 cmenu.menu.items.push({
23123 actiontype : this.cleanStyles[i],
23124 html: 'Remove ' + this.cleanStyles[i],
23125 handler: function(a,b) {
23128 var c = Roo.get(editorcore.doc.body);
23129 c.select('[style]').each(function(s) {
23130 s.dom.style.removeProperty(a.actiontype);
23132 editorcore.syncValue();
23137 cmenu.menu.items.push({
23138 actiontype : 'tablewidths',
23139 html: 'Remove Table Widths',
23140 handler: function(a,b) {
23141 editorcore.cleanTableWidths();
23142 editorcore.syncValue();
23146 cmenu.menu.items.push({
23147 actiontype : 'word',
23148 html: 'Remove MS Word Formating',
23149 handler: function(a,b) {
23150 editorcore.cleanWord();
23151 editorcore.syncValue();
23156 cmenu.menu.items.push({
23157 actiontype : 'all',
23158 html: 'Remove All Styles',
23159 handler: function(a,b) {
23161 var c = Roo.get(editorcore.doc.body);
23162 c.select('[style]').each(function(s) {
23163 s.dom.removeAttribute('style');
23165 editorcore.syncValue();
23170 cmenu.menu.items.push({
23171 actiontype : 'all',
23172 html: 'Remove All CSS Classes',
23173 handler: function(a,b) {
23175 var c = Roo.get(editorcore.doc.body);
23176 c.select('[class]').each(function(s) {
23177 s.dom.className = '';
23179 editorcore.syncValue();
23184 cmenu.menu.items.push({
23185 actiontype : 'tidy',
23186 html: 'Tidy HTML Source',
23187 handler: function(a,b) {
23188 editorcore.doc.body.innerHTML = editorcore.domToHTML();
23189 editorcore.syncValue();
23198 if (!this.disable.specialElements) {
23201 cls: 'x-edit-none',
23206 for (var i =0; i < this.specialElements.length; i++) {
23207 semenu.menu.items.push(
23209 handler: function(a,b) {
23210 editor.insertAtCursor(this.ihtml);
23212 }, this.specialElements[i])
23224 for(var i =0; i< this.btns.length;i++) {
23225 var b = Roo.factory(this.btns[i],Roo.form);
23226 b.cls = 'x-edit-none';
23228 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23229 b.cls += ' x-init-enable';
23232 b.scope = editorcore;
23240 // disable everything...
23242 this.tb.items.each(function(item){
23245 item.id != editorcore.frameId+ '-sourceedit' &&
23246 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23252 this.rendered = true;
23254 // the all the btns;
23255 editor.on('editorevent', this.updateToolbar, this);
23256 // other toolbars need to implement this..
23257 //editor.on('editmodechange', this.updateToolbar, this);
23261 relayBtnCmd : function(btn) {
23262 this.editorcore.relayCmd(btn.cmd);
23264 // private used internally
23265 createLink : function(){
23266 Roo.log("create link?");
23267 var url = prompt(this.createLinkText, this.defaultLinkValue);
23268 if(url && url != 'http:/'+'/'){
23269 this.editorcore.relayCmd('createlink', url);
23275 * Protected method that will not generally be called directly. It triggers
23276 * a toolbar update by reading the markup state of the current selection in the editor.
23278 updateToolbar: function(){
23280 if(!this.editorcore.activated){
23281 this.editor.onFirstFocus();
23285 var btns = this.tb.items.map,
23286 doc = this.editorcore.doc,
23287 frameId = this.editorcore.frameId;
23289 if(!this.disable.font && !Roo.isSafari){
23291 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23292 if(name != this.fontSelect.dom.value){
23293 this.fontSelect.dom.value = name;
23297 if(!this.disable.format){
23298 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23299 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23300 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23301 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23303 if(!this.disable.alignments){
23304 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23305 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23306 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23308 if(!Roo.isSafari && !this.disable.lists){
23309 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23310 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23313 var ans = this.editorcore.getAllAncestors();
23314 if (this.formatCombo) {
23317 var store = this.formatCombo.store;
23318 this.formatCombo.setValue("");
23319 for (var i =0; i < ans.length;i++) {
23320 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23322 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23330 // hides menus... - so this cant be on a menu...
23331 Roo.menu.MenuMgr.hideAll();
23333 //this.editorsyncValue();
23337 createFontOptions : function(){
23338 var buf = [], fs = this.fontFamilies, ff, lc;
23342 for(var i = 0, len = fs.length; i< len; i++){
23344 lc = ff.toLowerCase();
23346 '<option value="',lc,'" style="font-family:',ff,';"',
23347 (this.defaultFont == lc ? ' selected="true">' : '>'),
23352 return buf.join('');
23355 toggleSourceEdit : function(sourceEditMode){
23357 Roo.log("toolbar toogle");
23358 if(sourceEditMode === undefined){
23359 sourceEditMode = !this.sourceEditMode;
23361 this.sourceEditMode = sourceEditMode === true;
23362 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23363 // just toggle the button?
23364 if(btn.pressed !== this.sourceEditMode){
23365 btn.toggle(this.sourceEditMode);
23369 if(sourceEditMode){
23370 Roo.log("disabling buttons");
23371 this.tb.items.each(function(item){
23372 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23378 Roo.log("enabling buttons");
23379 if(this.editorcore.initialized){
23380 this.tb.items.each(function(item){
23386 Roo.log("calling toggole on editor");
23387 // tell the editor that it's been pressed..
23388 this.editor.toggleSourceEdit(sourceEditMode);
23392 * Object collection of toolbar tooltips for the buttons in the editor. The key
23393 * is the command id associated with that button and the value is a valid QuickTips object.
23398 title: 'Bold (Ctrl+B)',
23399 text: 'Make the selected text bold.',
23400 cls: 'x-html-editor-tip'
23403 title: 'Italic (Ctrl+I)',
23404 text: 'Make the selected text italic.',
23405 cls: 'x-html-editor-tip'
23413 title: 'Bold (Ctrl+B)',
23414 text: 'Make the selected text bold.',
23415 cls: 'x-html-editor-tip'
23418 title: 'Italic (Ctrl+I)',
23419 text: 'Make the selected text italic.',
23420 cls: 'x-html-editor-tip'
23423 title: 'Underline (Ctrl+U)',
23424 text: 'Underline the selected text.',
23425 cls: 'x-html-editor-tip'
23428 title: 'Strikethrough',
23429 text: 'Strikethrough the selected text.',
23430 cls: 'x-html-editor-tip'
23432 increasefontsize : {
23433 title: 'Grow Text',
23434 text: 'Increase the font size.',
23435 cls: 'x-html-editor-tip'
23437 decreasefontsize : {
23438 title: 'Shrink Text',
23439 text: 'Decrease the font size.',
23440 cls: 'x-html-editor-tip'
23443 title: 'Text Highlight Color',
23444 text: 'Change the background color of the selected text.',
23445 cls: 'x-html-editor-tip'
23448 title: 'Font Color',
23449 text: 'Change the color of the selected text.',
23450 cls: 'x-html-editor-tip'
23453 title: 'Align Text Left',
23454 text: 'Align text to the left.',
23455 cls: 'x-html-editor-tip'
23458 title: 'Center Text',
23459 text: 'Center text in the editor.',
23460 cls: 'x-html-editor-tip'
23463 title: 'Align Text Right',
23464 text: 'Align text to the right.',
23465 cls: 'x-html-editor-tip'
23467 insertunorderedlist : {
23468 title: 'Bullet List',
23469 text: 'Start a bulleted list.',
23470 cls: 'x-html-editor-tip'
23472 insertorderedlist : {
23473 title: 'Numbered List',
23474 text: 'Start a numbered list.',
23475 cls: 'x-html-editor-tip'
23478 title: 'Hyperlink',
23479 text: 'Make the selected text a hyperlink.',
23480 cls: 'x-html-editor-tip'
23483 title: 'Source Edit',
23484 text: 'Switch to source editing mode.',
23485 cls: 'x-html-editor-tip'
23489 onDestroy : function(){
23492 this.tb.items.each(function(item){
23494 item.menu.removeAll();
23496 item.menu.el.destroy();
23504 onFirstFocus: function() {
23505 this.tb.items.each(function(item){
23514 // <script type="text/javascript">
23517 * Ext JS Library 1.1.1
23518 * Copyright(c) 2006-2007, Ext JS, LLC.
23525 * @class Roo.form.HtmlEditor.ToolbarContext
23530 new Roo.form.HtmlEditor({
23533 { xtype: 'ToolbarStandard', styles : {} }
23534 { xtype: 'ToolbarContext', disable : {} }
23540 * @config : {Object} disable List of elements to disable.. (not done yet.)
23541 * @config : {Object} styles Map of styles available.
23545 Roo.form.HtmlEditor.ToolbarContext = function(config)
23548 Roo.apply(this, config);
23549 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23550 // dont call parent... till later.
23551 this.styles = this.styles || {};
23556 Roo.form.HtmlEditor.ToolbarContext.types = {
23568 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23634 opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23639 opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23649 style : 'fontFamily',
23650 displayField: 'display',
23651 optname : 'font-family',
23700 // should we really allow this??
23701 // should this just be
23712 style : 'fontFamily',
23713 displayField: 'display',
23714 optname : 'font-family',
23721 style : 'fontFamily',
23722 displayField: 'display',
23723 optname : 'font-family',
23730 style : 'fontFamily',
23731 displayField: 'display',
23732 optname : 'font-family',
23743 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23744 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23746 Roo.form.HtmlEditor.ToolbarContext.options = {
23748 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23749 [ 'Courier New', 'Courier New'],
23750 [ 'Tahoma', 'Tahoma'],
23751 [ 'Times New Roman,serif', 'Times'],
23752 [ 'Verdana','Verdana' ]
23756 // fixme - these need to be configurable..
23759 //Roo.form.HtmlEditor.ToolbarContext.types
23762 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
23769 editorcore : false,
23771 * @cfg {Object} disable List of toolbar elements to disable
23776 * @cfg {Object} styles List of styles
23777 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
23779 * These must be defined in the page, so they get rendered correctly..
23790 init : function(editor)
23792 this.editor = editor;
23793 this.editorcore = editor.editorcore ? editor.editorcore : editor;
23794 var editorcore = this.editorcore;
23796 var fid = editorcore.frameId;
23798 function btn(id, toggle, handler){
23799 var xid = fid + '-'+ id ;
23803 cls : 'x-btn-icon x-edit-'+id,
23804 enableToggle:toggle !== false,
23805 scope: editorcore, // was editor...
23806 handler:handler||editorcore.relayBtnCmd,
23807 clickEvent:'mousedown',
23808 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23812 // create a new element.
23813 var wdiv = editor.wrap.createChild({
23815 }, editor.wrap.dom.firstChild.nextSibling, true);
23817 // can we do this more than once??
23819 // stop form submits
23822 // disable everything...
23823 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23824 this.toolbars = {};
23826 for (var i in ty) {
23828 this.toolbars[i] = this.buildToolbar(ty[i],i);
23830 this.tb = this.toolbars.BODY;
23832 this.buildFooter();
23833 this.footer.show();
23834 editor.on('hide', function( ) { this.footer.hide() }, this);
23835 editor.on('show', function( ) { this.footer.show() }, this);
23838 this.rendered = true;
23840 // the all the btns;
23841 editor.on('editorevent', this.updateToolbar, this);
23842 // other toolbars need to implement this..
23843 //editor.on('editmodechange', this.updateToolbar, this);
23849 * Protected method that will not generally be called directly. It triggers
23850 * a toolbar update by reading the markup state of the current selection in the editor.
23852 * Note you can force an update by calling on('editorevent', scope, false)
23854 updateToolbar: function(editor,ev,sel){
23857 // capture mouse up - this is handy for selecting images..
23858 // perhaps should go somewhere else...
23859 if(!this.editorcore.activated){
23860 this.editor.onFirstFocus();
23866 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23867 // selectNode - might want to handle IE?
23869 (ev.type == 'mouseup' || ev.type == 'click' ) &&
23870 ev.target && ev.target.tagName == 'IMG') {
23871 // they have click on an image...
23872 // let's see if we can change the selection...
23875 var nodeRange = sel.ownerDocument.createRange();
23877 nodeRange.selectNode(sel);
23879 nodeRange.selectNodeContents(sel);
23881 //nodeRange.collapse(true);
23882 var s = this.editorcore.win.getSelection();
23883 s.removeAllRanges();
23884 s.addRange(nodeRange);
23888 var updateFooter = sel ? false : true;
23891 var ans = this.editorcore.getAllAncestors();
23894 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23897 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
23898 sel = sel ? sel : this.editorcore.doc.body;
23899 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
23902 // pick a menu that exists..
23903 var tn = sel.tagName.toUpperCase();
23904 //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
23906 tn = sel.tagName.toUpperCase();
23908 var lastSel = this.tb.selectedNode;
23910 this.tb.selectedNode = sel;
23912 // if current menu does not match..
23914 if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
23917 ///console.log("show: " + tn);
23918 this.tb = typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
23921 this.tb.items.first().el.innerHTML = tn + ': ';
23924 // update attributes
23925 if (this.tb.fields) {
23926 this.tb.fields.each(function(e) {
23928 e.setValue(sel.style[e.stylename]);
23931 e.setValue(sel.getAttribute(e.attrname));
23935 var hasStyles = false;
23936 for(var i in this.styles) {
23943 var st = this.tb.fields.item(0);
23945 st.store.removeAll();
23948 var cn = sel.className.split(/\s+/);
23951 if (this.styles['*']) {
23953 Roo.each(this.styles['*'], function(v) {
23954 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
23957 if (this.styles[tn]) {
23958 Roo.each(this.styles[tn], function(v) {
23959 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
23963 st.store.loadData(avs);
23967 // flag our selected Node.
23968 this.tb.selectedNode = sel;
23971 Roo.menu.MenuMgr.hideAll();
23975 if (!updateFooter) {
23976 //this.footDisp.dom.innerHTML = '';
23979 // update the footer
23983 this.footerEls = ans.reverse();
23984 Roo.each(this.footerEls, function(a,i) {
23985 if (!a) { return; }
23986 html += html.length ? ' > ' : '';
23988 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
23993 var sz = this.footDisp.up('td').getSize();
23994 this.footDisp.dom.style.width = (sz.width -10) + 'px';
23995 this.footDisp.dom.style.marginLeft = '5px';
23997 this.footDisp.dom.style.overflow = 'hidden';
23999 this.footDisp.dom.innerHTML = html;
24001 //this.editorsyncValue();
24008 onDestroy : function(){
24011 this.tb.items.each(function(item){
24013 item.menu.removeAll();
24015 item.menu.el.destroy();
24023 onFirstFocus: function() {
24024 // need to do this for all the toolbars..
24025 this.tb.items.each(function(item){
24029 buildToolbar: function(tlist, nm)
24031 var editor = this.editor;
24032 var editorcore = this.editorcore;
24033 // create a new element.
24034 var wdiv = editor.wrap.createChild({
24036 }, editor.wrap.dom.firstChild.nextSibling, true);
24039 var tb = new Roo.Toolbar(wdiv);
24042 tb.add(nm+ ": ");
24045 for(var i in this.styles) {
24050 if (styles && styles.length) {
24052 // this needs a multi-select checkbox...
24053 tb.addField( new Roo.form.ComboBox({
24054 store: new Roo.data.SimpleStore({
24056 fields: ['val', 'selected'],
24059 name : '-roo-edit-className',
24060 attrname : 'className',
24061 displayField: 'val',
24065 triggerAction: 'all',
24066 emptyText:'Select Style',
24067 selectOnFocus:true,
24070 'select': function(c, r, i) {
24071 // initial support only for on class per el..
24072 tb.selectedNode.className = r ? r.get('val') : '';
24073 editorcore.syncValue();
24080 var tbc = Roo.form.HtmlEditor.ToolbarContext;
24081 var tbops = tbc.options;
24083 for (var i in tlist) {
24085 var item = tlist[i];
24086 tb.add(item.title + ": ");
24089 //optname == used so you can configure the options available..
24090 var opts = item.opts ? item.opts : false;
24091 if (item.optname) {
24092 opts = tbops[item.optname];
24097 // opts == pulldown..
24098 tb.addField( new Roo.form.ComboBox({
24099 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24101 fields: ['val', 'display'],
24104 name : '-roo-edit-' + i,
24106 stylename : item.style ? item.style : false,
24107 displayField: item.displayField ? item.displayField : 'val',
24108 valueField : 'val',
24110 mode: typeof(tbc.stores[i]) != 'undefined' ? 'remote' : 'local',
24112 triggerAction: 'all',
24113 emptyText:'Select',
24114 selectOnFocus:true,
24115 width: item.width ? item.width : 130,
24117 'select': function(c, r, i) {
24119 tb.selectedNode.style[c.stylename] = r.get('val');
24122 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24131 tb.addField( new Roo.form.TextField({
24134 //allowBlank:false,
24139 tb.addField( new Roo.form.TextField({
24140 name: '-roo-edit-' + i,
24147 'change' : function(f, nv, ov) {
24148 tb.selectedNode.setAttribute(f.attrname, nv);
24149 editorcore.syncValue();
24162 text: 'Stylesheets',
24165 click : function ()
24167 _this.editor.fireEvent('stylesheetsclick', _this.editor);
24175 text: 'Remove Tag',
24178 click : function ()
24181 // undo does not work.
24183 var sn = tb.selectedNode;
24185 var pn = sn.parentNode;
24187 var stn = sn.childNodes[0];
24188 var en = sn.childNodes[sn.childNodes.length - 1 ];
24189 while (sn.childNodes.length) {
24190 var node = sn.childNodes[0];
24191 sn.removeChild(node);
24193 pn.insertBefore(node, sn);
24196 pn.removeChild(sn);
24197 var range = editorcore.createRange();
24199 range.setStart(stn,0);
24200 range.setEnd(en,0); //????
24201 //range.selectNode(sel);
24204 var selection = editorcore.getSelection();
24205 selection.removeAllRanges();
24206 selection.addRange(range);
24210 //_this.updateToolbar(null, null, pn);
24211 _this.updateToolbar(null, null, null);
24212 _this.footDisp.dom.innerHTML = '';
24222 tb.el.on('click', function(e){
24223 e.preventDefault(); // what does this do?
24225 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24228 // dont need to disable them... as they will get hidden
24233 buildFooter : function()
24236 var fel = this.editor.wrap.createChild();
24237 this.footer = new Roo.Toolbar(fel);
24238 // toolbar has scrolly on left / right?
24239 var footDisp= new Roo.Toolbar.Fill();
24245 handler : function() {
24246 _t.footDisp.scrollTo('left',0,true)
24250 this.footer.add( footDisp );
24255 handler : function() {
24257 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24261 var fel = Roo.get(footDisp.el);
24262 fel.addClass('x-editor-context');
24263 this.footDispWrap = fel;
24264 this.footDispWrap.overflow = 'hidden';
24266 this.footDisp = fel.createChild();
24267 this.footDispWrap.on('click', this.onContextClick, this)
24271 onContextClick : function (ev,dom)
24273 ev.preventDefault();
24274 var cn = dom.className;
24276 if (!cn.match(/x-ed-loc-/)) {
24279 var n = cn.split('-').pop();
24280 var ans = this.footerEls;
24284 var range = this.editorcore.createRange();
24286 range.selectNodeContents(sel);
24287 //range.selectNode(sel);
24290 var selection = this.editorcore.getSelection();
24291 selection.removeAllRanges();
24292 selection.addRange(range);
24296 this.updateToolbar(null, null, sel);
24313 * Ext JS Library 1.1.1
24314 * Copyright(c) 2006-2007, Ext JS, LLC.
24316 * Originally Released Under LGPL - original licence link has changed is not relivant.
24319 * <script type="text/javascript">
24323 * @class Roo.form.BasicForm
24324 * @extends Roo.util.Observable
24325 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24327 * @param {String/HTMLElement/Roo.Element} el The form element or its id
24328 * @param {Object} config Configuration options
24330 Roo.form.BasicForm = function(el, config){
24331 this.allItems = [];
24332 this.childForms = [];
24333 Roo.apply(this, config);
24335 * The Roo.form.Field items in this form.
24336 * @type MixedCollection
24340 this.items = new Roo.util.MixedCollection(false, function(o){
24341 return o.id || (o.id = Roo.id());
24345 * @event beforeaction
24346 * Fires before any action is performed. Return false to cancel the action.
24347 * @param {Form} this
24348 * @param {Action} action The action to be performed
24350 beforeaction: true,
24352 * @event actionfailed
24353 * Fires when an action fails.
24354 * @param {Form} this
24355 * @param {Action} action The action that failed
24357 actionfailed : true,
24359 * @event actioncomplete
24360 * Fires when an action is completed.
24361 * @param {Form} this
24362 * @param {Action} action The action that completed
24364 actioncomplete : true
24369 Roo.form.BasicForm.superclass.constructor.call(this);
24372 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24374 * @cfg {String} method
24375 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24378 * @cfg {DataReader} reader
24379 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24380 * This is optional as there is built-in support for processing JSON.
24383 * @cfg {DataReader} errorReader
24384 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24385 * This is completely optional as there is built-in support for processing JSON.
24388 * @cfg {String} url
24389 * The URL to use for form actions if one isn't supplied in the action options.
24392 * @cfg {Boolean} fileUpload
24393 * Set to true if this form is a file upload.
24397 * @cfg {Object} baseParams
24398 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24403 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24408 activeAction : null,
24411 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24412 * or setValues() data instead of when the form was first created.
24414 trackResetOnLoad : false,
24418 * childForms - used for multi-tab forms
24421 childForms : false,
24424 * allItems - full list of fields.
24430 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24431 * element by passing it or its id or mask the form itself by passing in true.
24434 waitMsgTarget : false,
24439 disableMask : false,
24442 initEl : function(el){
24443 this.el = Roo.get(el);
24444 this.id = this.el.id || Roo.id();
24445 this.el.on('submit', this.onSubmit, this);
24446 this.el.addClass('x-form');
24450 onSubmit : function(e){
24455 * Returns true if client-side validation on the form is successful.
24458 isValid : function(){
24460 this.items.each(function(f){
24469 * DEPRICATED Returns true if any fields in this form have changed since their original load.
24472 isDirty : function(){
24474 this.items.each(function(f){
24484 * Returns true if any fields in this form have changed since their original load. (New version)
24488 hasChanged : function()
24491 this.items.each(function(f){
24492 if(f.hasChanged()){
24501 * Resets all hasChanged to 'false' -
24502 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24503 * So hasChanged storage is only to be used for this purpose
24506 resetHasChanged : function()
24508 this.items.each(function(f){
24509 f.resetHasChanged();
24516 * Performs a predefined action (submit or load) or custom actions you define on this form.
24517 * @param {String} actionName The name of the action type
24518 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
24519 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24520 * accept other config options):
24522 Property Type Description
24523 ---------------- --------------- ----------------------------------------------------------------------------------
24524 url String The url for the action (defaults to the form's url)
24525 method String The form method to use (defaults to the form's method, or POST if not defined)
24526 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
24527 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
24528 validate the form on the client (defaults to false)
24530 * @return {BasicForm} this
24532 doAction : function(action, options){
24533 if(typeof action == 'string'){
24534 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24536 if(this.fireEvent('beforeaction', this, action) !== false){
24537 this.beforeAction(action);
24538 action.run.defer(100, action);
24544 * Shortcut to do a submit action.
24545 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24546 * @return {BasicForm} this
24548 submit : function(options){
24549 this.doAction('submit', options);
24554 * Shortcut to do a load action.
24555 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24556 * @return {BasicForm} this
24558 load : function(options){
24559 this.doAction('load', options);
24564 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24565 * @param {Record} record The record to edit
24566 * @return {BasicForm} this
24568 updateRecord : function(record){
24569 record.beginEdit();
24570 var fs = record.fields;
24571 fs.each(function(f){
24572 var field = this.findField(f.name);
24574 record.set(f.name, field.getValue());
24582 * Loads an Roo.data.Record into this form.
24583 * @param {Record} record The record to load
24584 * @return {BasicForm} this
24586 loadRecord : function(record){
24587 this.setValues(record.data);
24592 beforeAction : function(action){
24593 var o = action.options;
24595 if(!this.disableMask) {
24596 if(this.waitMsgTarget === true){
24597 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24598 }else if(this.waitMsgTarget){
24599 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24600 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24602 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24610 afterAction : function(action, success){
24611 this.activeAction = null;
24612 var o = action.options;
24614 if(!this.disableMask) {
24615 if(this.waitMsgTarget === true){
24617 }else if(this.waitMsgTarget){
24618 this.waitMsgTarget.unmask();
24620 Roo.MessageBox.updateProgress(1);
24621 Roo.MessageBox.hide();
24629 Roo.callback(o.success, o.scope, [this, action]);
24630 this.fireEvent('actioncomplete', this, action);
24634 // failure condition..
24635 // we have a scenario where updates need confirming.
24636 // eg. if a locking scenario exists..
24637 // we look for { errors : { needs_confirm : true }} in the response.
24639 (typeof(action.result) != 'undefined') &&
24640 (typeof(action.result.errors) != 'undefined') &&
24641 (typeof(action.result.errors.needs_confirm) != 'undefined')
24644 Roo.MessageBox.confirm(
24645 "Change requires confirmation",
24646 action.result.errorMsg,
24651 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
24661 Roo.callback(o.failure, o.scope, [this, action]);
24662 // show an error message if no failed handler is set..
24663 if (!this.hasListener('actionfailed')) {
24664 Roo.MessageBox.alert("Error",
24665 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24666 action.result.errorMsg :
24667 "Saving Failed, please check your entries or try again"
24671 this.fireEvent('actionfailed', this, action);
24677 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24678 * @param {String} id The value to search for
24681 findField : function(id){
24682 var field = this.items.get(id);
24684 this.items.each(function(f){
24685 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24691 return field || null;
24695 * Add a secondary form to this one,
24696 * Used to provide tabbed forms. One form is primary, with hidden values
24697 * which mirror the elements from the other forms.
24699 * @param {Roo.form.Form} form to add.
24702 addForm : function(form)
24705 if (this.childForms.indexOf(form) > -1) {
24709 this.childForms.push(form);
24711 Roo.each(form.allItems, function (fe) {
24713 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24714 if (this.findField(n)) { // already added..
24717 var add = new Roo.form.Hidden({
24720 add.render(this.el);
24727 * Mark fields in this form invalid in bulk.
24728 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24729 * @return {BasicForm} this
24731 markInvalid : function(errors){
24732 if(errors instanceof Array){
24733 for(var i = 0, len = errors.length; i < len; i++){
24734 var fieldError = errors[i];
24735 var f = this.findField(fieldError.id);
24737 f.markInvalid(fieldError.msg);
24743 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24744 field.markInvalid(errors[id]);
24748 Roo.each(this.childForms || [], function (f) {
24749 f.markInvalid(errors);
24756 * Set values for fields in this form in bulk.
24757 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24758 * @return {BasicForm} this
24760 setValues : function(values){
24761 if(values instanceof Array){ // array of objects
24762 for(var i = 0, len = values.length; i < len; i++){
24764 var f = this.findField(v.id);
24766 f.setValue(v.value);
24767 if(this.trackResetOnLoad){
24768 f.originalValue = f.getValue();
24772 }else{ // object hash
24775 if(typeof values[id] != 'function' && (field = this.findField(id))){
24777 if (field.setFromData &&
24778 field.valueField &&
24779 field.displayField &&
24780 // combos' with local stores can
24781 // be queried via setValue()
24782 // to set their value..
24783 (field.store && !field.store.isLocal)
24787 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24788 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24789 field.setFromData(sd);
24792 field.setValue(values[id]);
24796 if(this.trackResetOnLoad){
24797 field.originalValue = field.getValue();
24802 this.resetHasChanged();
24805 Roo.each(this.childForms || [], function (f) {
24806 f.setValues(values);
24807 f.resetHasChanged();
24814 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24815 * they are returned as an array.
24816 * @param {Boolean} asString
24819 getValues : function(asString){
24820 if (this.childForms) {
24821 // copy values from the child forms
24822 Roo.each(this.childForms, function (f) {
24823 this.setValues(f.getValues());
24829 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24830 if(asString === true){
24833 return Roo.urlDecode(fs);
24837 * Returns the fields in this form as an object with key/value pairs.
24838 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24841 getFieldValues : function(with_hidden)
24843 if (this.childForms) {
24844 // copy values from the child forms
24845 // should this call getFieldValues - probably not as we do not currently copy
24846 // hidden fields when we generate..
24847 Roo.each(this.childForms, function (f) {
24848 this.setValues(f.getValues());
24853 this.items.each(function(f){
24854 if (!f.getName()) {
24857 var v = f.getValue();
24858 if (f.inputType =='radio') {
24859 if (typeof(ret[f.getName()]) == 'undefined') {
24860 ret[f.getName()] = ''; // empty..
24863 if (!f.el.dom.checked) {
24867 v = f.el.dom.value;
24871 // not sure if this supported any more..
24872 if ((typeof(v) == 'object') && f.getRawValue) {
24873 v = f.getRawValue() ; // dates..
24875 // combo boxes where name != hiddenName...
24876 if (f.name != f.getName()) {
24877 ret[f.name] = f.getRawValue();
24879 ret[f.getName()] = v;
24886 * Clears all invalid messages in this form.
24887 * @return {BasicForm} this
24889 clearInvalid : function(){
24890 this.items.each(function(f){
24894 Roo.each(this.childForms || [], function (f) {
24903 * Resets this form.
24904 * @return {BasicForm} this
24906 reset : function(){
24907 this.items.each(function(f){
24911 Roo.each(this.childForms || [], function (f) {
24914 this.resetHasChanged();
24920 * Add Roo.form components to this form.
24921 * @param {Field} field1
24922 * @param {Field} field2 (optional)
24923 * @param {Field} etc (optional)
24924 * @return {BasicForm} this
24927 this.items.addAll(Array.prototype.slice.call(arguments, 0));
24933 * Removes a field from the items collection (does NOT remove its markup).
24934 * @param {Field} field
24935 * @return {BasicForm} this
24937 remove : function(field){
24938 this.items.remove(field);
24943 * Looks at the fields in this form, checks them for an id attribute,
24944 * and calls applyTo on the existing dom element with that id.
24945 * @return {BasicForm} this
24947 render : function(){
24948 this.items.each(function(f){
24949 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
24957 * Calls {@link Ext#apply} for all fields in this form with the passed object.
24958 * @param {Object} values
24959 * @return {BasicForm} this
24961 applyToFields : function(o){
24962 this.items.each(function(f){
24969 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
24970 * @param {Object} values
24971 * @return {BasicForm} this
24973 applyIfToFields : function(o){
24974 this.items.each(function(f){
24982 Roo.BasicForm = Roo.form.BasicForm;/*
24984 * Ext JS Library 1.1.1
24985 * Copyright(c) 2006-2007, Ext JS, LLC.
24987 * Originally Released Under LGPL - original licence link has changed is not relivant.
24990 * <script type="text/javascript">
24994 * @class Roo.form.Form
24995 * @extends Roo.form.BasicForm
24996 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
24998 * @param {Object} config Configuration options
25000 Roo.form.Form = function(config){
25002 if (config.items) {
25003 xitems = config.items;
25004 delete config.items;
25008 Roo.form.Form.superclass.constructor.call(this, null, config);
25009 this.url = this.url || this.action;
25011 this.root = new Roo.form.Layout(Roo.applyIf({
25015 this.active = this.root;
25017 * Array of all the buttons that have been added to this form via {@link addButton}
25021 this.allItems = [];
25024 * @event clientvalidation
25025 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25026 * @param {Form} this
25027 * @param {Boolean} valid true if the form has passed client-side validation
25029 clientvalidation: true,
25032 * Fires when the form is rendered
25033 * @param {Roo.form.Form} form
25038 if (this.progressUrl) {
25039 // push a hidden field onto the list of fields..
25043 name : 'UPLOAD_IDENTIFIER'
25048 Roo.each(xitems, this.addxtype, this);
25054 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25056 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25059 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25062 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25064 buttonAlign:'center',
25067 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25072 * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25073 * This property cascades to child containers if not set.
25078 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25079 * fires a looping event with that state. This is required to bind buttons to the valid
25080 * state using the config value formBind:true on the button.
25082 monitorValid : false,
25085 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25090 * @cfg {String} progressUrl - Url to return progress data
25093 progressUrl : false,
25095 * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25096 * sending a formdata with extra parameters - eg uploaded elements.
25102 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25103 * fields are added and the column is closed. If no fields are passed the column remains open
25104 * until end() is called.
25105 * @param {Object} config The config to pass to the column
25106 * @param {Field} field1 (optional)
25107 * @param {Field} field2 (optional)
25108 * @param {Field} etc (optional)
25109 * @return Column The column container object
25111 column : function(c){
25112 var col = new Roo.form.Column(c);
25114 if(arguments.length > 1){ // duplicate code required because of Opera
25115 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25122 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25123 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25124 * until end() is called.
25125 * @param {Object} config The config to pass to the fieldset
25126 * @param {Field} field1 (optional)
25127 * @param {Field} field2 (optional)
25128 * @param {Field} etc (optional)
25129 * @return FieldSet The fieldset container object
25131 fieldset : function(c){
25132 var fs = new Roo.form.FieldSet(c);
25134 if(arguments.length > 1){ // duplicate code required because of Opera
25135 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25142 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25143 * fields are added and the container is closed. If no fields are passed the container remains open
25144 * until end() is called.
25145 * @param {Object} config The config to pass to the Layout
25146 * @param {Field} field1 (optional)
25147 * @param {Field} field2 (optional)
25148 * @param {Field} etc (optional)
25149 * @return Layout The container object
25151 container : function(c){
25152 var l = new Roo.form.Layout(c);
25154 if(arguments.length > 1){ // duplicate code required because of Opera
25155 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25162 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25163 * @param {Object} container A Roo.form.Layout or subclass of Layout
25164 * @return {Form} this
25166 start : function(c){
25167 // cascade label info
25168 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25169 this.active.stack.push(c);
25170 c.ownerCt = this.active;
25176 * Closes the current open container
25177 * @return {Form} this
25180 if(this.active == this.root){
25183 this.active = this.active.ownerCt;
25188 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
25189 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25190 * as the label of the field.
25191 * @param {Field} field1
25192 * @param {Field} field2 (optional)
25193 * @param {Field} etc. (optional)
25194 * @return {Form} this
25197 this.active.stack.push.apply(this.active.stack, arguments);
25198 this.allItems.push.apply(this.allItems,arguments);
25200 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25201 if(a[i].isFormField){
25206 Roo.form.Form.superclass.add.apply(this, r);
25216 * Find any element that has been added to a form, using it's ID or name
25217 * This can include framesets, columns etc. along with regular fields..
25218 * @param {String} id - id or name to find.
25220 * @return {Element} e - or false if nothing found.
25222 findbyId : function(id)
25228 Roo.each(this.allItems, function(f){
25229 if (f.id == id || f.name == id ){
25240 * Render this form into the passed container. This should only be called once!
25241 * @param {String/HTMLElement/Element} container The element this component should be rendered into
25242 * @return {Form} this
25244 render : function(ct)
25250 var o = this.autoCreate || {
25252 method : this.method || 'POST',
25253 id : this.id || Roo.id()
25255 this.initEl(ct.createChild(o));
25257 this.root.render(this.el);
25261 this.items.each(function(f){
25262 f.render('x-form-el-'+f.id);
25265 if(this.buttons.length > 0){
25266 // tables are required to maintain order and for correct IE layout
25267 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25268 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25269 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25271 var tr = tb.getElementsByTagName('tr')[0];
25272 for(var i = 0, len = this.buttons.length; i < len; i++) {
25273 var b = this.buttons[i];
25274 var td = document.createElement('td');
25275 td.className = 'x-form-btn-td';
25276 b.render(tr.appendChild(td));
25279 if(this.monitorValid){ // initialize after render
25280 this.startMonitoring();
25282 this.fireEvent('rendered', this);
25287 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25288 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25289 * object or a valid Roo.DomHelper element config
25290 * @param {Function} handler The function called when the button is clicked
25291 * @param {Object} scope (optional) The scope of the handler function
25292 * @return {Roo.Button}
25294 addButton : function(config, handler, scope){
25298 minWidth: this.minButtonWidth,
25301 if(typeof config == "string"){
25304 Roo.apply(bc, config);
25306 var btn = new Roo.Button(null, bc);
25307 this.buttons.push(btn);
25312 * Adds a series of form elements (using the xtype property as the factory method.
25313 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25314 * @param {Object} config
25317 addxtype : function()
25319 var ar = Array.prototype.slice.call(arguments, 0);
25321 for(var i = 0; i < ar.length; i++) {
25323 continue; // skip -- if this happends something invalid got sent, we
25324 // should ignore it, as basically that interface element will not show up
25325 // and that should be pretty obvious!!
25328 if (Roo.form[ar[i].xtype]) {
25330 var fe = Roo.factory(ar[i], Roo.form);
25336 fe.store.form = this;
25341 this.allItems.push(fe);
25342 if (fe.items && fe.addxtype) {
25343 fe.addxtype.apply(fe, fe.items);
25353 // console.log('adding ' + ar[i].xtype);
25355 if (ar[i].xtype == 'Button') {
25356 //console.log('adding button');
25357 //console.log(ar[i]);
25358 this.addButton(ar[i]);
25359 this.allItems.push(fe);
25363 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25364 alert('end is not supported on xtype any more, use items');
25366 // //console.log('adding end');
25374 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25375 * option "monitorValid"
25377 startMonitoring : function(){
25380 Roo.TaskMgr.start({
25381 run : this.bindHandler,
25382 interval : this.monitorPoll || 200,
25389 * Stops monitoring of the valid state of this form
25391 stopMonitoring : function(){
25392 this.bound = false;
25396 bindHandler : function(){
25398 return false; // stops binding
25401 this.items.each(function(f){
25402 if(!f.isValid(true)){
25407 for(var i = 0, len = this.buttons.length; i < len; i++){
25408 var btn = this.buttons[i];
25409 if(btn.formBind === true && btn.disabled === valid){
25410 btn.setDisabled(!valid);
25413 this.fireEvent('clientvalidation', this, valid);
25427 Roo.Form = Roo.form.Form;
25430 * Ext JS Library 1.1.1
25431 * Copyright(c) 2006-2007, Ext JS, LLC.
25433 * Originally Released Under LGPL - original licence link has changed is not relivant.
25436 * <script type="text/javascript">
25439 // as we use this in bootstrap.
25440 Roo.namespace('Roo.form');
25442 * @class Roo.form.Action
25443 * Internal Class used to handle form actions
25445 * @param {Roo.form.BasicForm} el The form element or its id
25446 * @param {Object} config Configuration options
25451 // define the action interface
25452 Roo.form.Action = function(form, options){
25454 this.options = options || {};
25457 * Client Validation Failed
25460 Roo.form.Action.CLIENT_INVALID = 'client';
25462 * Server Validation Failed
25465 Roo.form.Action.SERVER_INVALID = 'server';
25467 * Connect to Server Failed
25470 Roo.form.Action.CONNECT_FAILURE = 'connect';
25472 * Reading Data from Server Failed
25475 Roo.form.Action.LOAD_FAILURE = 'load';
25477 Roo.form.Action.prototype = {
25479 failureType : undefined,
25480 response : undefined,
25481 result : undefined,
25483 // interface method
25484 run : function(options){
25488 // interface method
25489 success : function(response){
25493 // interface method
25494 handleResponse : function(response){
25498 // default connection failure
25499 failure : function(response){
25501 this.response = response;
25502 this.failureType = Roo.form.Action.CONNECT_FAILURE;
25503 this.form.afterAction(this, false);
25506 processResponse : function(response){
25507 this.response = response;
25508 if(!response.responseText){
25511 this.result = this.handleResponse(response);
25512 return this.result;
25515 // utility functions used internally
25516 getUrl : function(appendParams){
25517 var url = this.options.url || this.form.url || this.form.el.dom.action;
25519 var p = this.getParams();
25521 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25527 getMethod : function(){
25528 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25531 getParams : function(){
25532 var bp = this.form.baseParams;
25533 var p = this.options.params;
25535 if(typeof p == "object"){
25536 p = Roo.urlEncode(Roo.applyIf(p, bp));
25537 }else if(typeof p == 'string' && bp){
25538 p += '&' + Roo.urlEncode(bp);
25541 p = Roo.urlEncode(bp);
25546 createCallback : function(){
25548 success: this.success,
25549 failure: this.failure,
25551 timeout: (this.form.timeout*1000),
25552 upload: this.form.fileUpload ? this.success : undefined
25557 Roo.form.Action.Submit = function(form, options){
25558 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25561 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25564 haveProgress : false,
25565 uploadComplete : false,
25567 // uploadProgress indicator.
25568 uploadProgress : function()
25570 if (!this.form.progressUrl) {
25574 if (!this.haveProgress) {
25575 Roo.MessageBox.progress("Uploading", "Uploading");
25577 if (this.uploadComplete) {
25578 Roo.MessageBox.hide();
25582 this.haveProgress = true;
25584 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25586 var c = new Roo.data.Connection();
25588 url : this.form.progressUrl,
25593 success : function(req){
25594 //console.log(data);
25598 rdata = Roo.decode(req.responseText)
25600 Roo.log("Invalid data from server..");
25604 if (!rdata || !rdata.success) {
25606 Roo.MessageBox.alert(Roo.encode(rdata));
25609 var data = rdata.data;
25611 if (this.uploadComplete) {
25612 Roo.MessageBox.hide();
25617 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25618 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25621 this.uploadProgress.defer(2000,this);
25624 failure: function(data) {
25625 Roo.log('progress url failed ');
25636 // run get Values on the form, so it syncs any secondary forms.
25637 this.form.getValues();
25639 var o = this.options;
25640 var method = this.getMethod();
25641 var isPost = method == 'POST';
25642 if(o.clientValidation === false || this.form.isValid()){
25644 if (this.form.progressUrl) {
25645 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25646 (new Date() * 1) + '' + Math.random());
25651 Roo.Ajax.request(Roo.apply(this.createCallback(), {
25652 form:this.form.el.dom,
25653 url:this.getUrl(!isPost),
25655 params:isPost ? this.getParams() : null,
25656 isUpload: this.form.fileUpload,
25657 formData : this.form.formData
25660 this.uploadProgress();
25662 }else if (o.clientValidation !== false){ // client validation failed
25663 this.failureType = Roo.form.Action.CLIENT_INVALID;
25664 this.form.afterAction(this, false);
25668 success : function(response)
25670 this.uploadComplete= true;
25671 if (this.haveProgress) {
25672 Roo.MessageBox.hide();
25676 var result = this.processResponse(response);
25677 if(result === true || result.success){
25678 this.form.afterAction(this, true);
25682 this.form.markInvalid(result.errors);
25683 this.failureType = Roo.form.Action.SERVER_INVALID;
25685 this.form.afterAction(this, false);
25687 failure : function(response)
25689 this.uploadComplete= true;
25690 if (this.haveProgress) {
25691 Roo.MessageBox.hide();
25694 this.response = response;
25695 this.failureType = Roo.form.Action.CONNECT_FAILURE;
25696 this.form.afterAction(this, false);
25699 handleResponse : function(response){
25700 if(this.form.errorReader){
25701 var rs = this.form.errorReader.read(response);
25704 for(var i = 0, len = rs.records.length; i < len; i++) {
25705 var r = rs.records[i];
25706 errors[i] = r.data;
25709 if(errors.length < 1){
25713 success : rs.success,
25719 ret = Roo.decode(response.responseText);
25723 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
25733 Roo.form.Action.Load = function(form, options){
25734 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
25735 this.reader = this.form.reader;
25738 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
25743 Roo.Ajax.request(Roo.apply(
25744 this.createCallback(), {
25745 method:this.getMethod(),
25746 url:this.getUrl(false),
25747 params:this.getParams()
25751 success : function(response){
25753 var result = this.processResponse(response);
25754 if(result === true || !result.success || !result.data){
25755 this.failureType = Roo.form.Action.LOAD_FAILURE;
25756 this.form.afterAction(this, false);
25759 this.form.clearInvalid();
25760 this.form.setValues(result.data);
25761 this.form.afterAction(this, true);
25764 handleResponse : function(response){
25765 if(this.form.reader){
25766 var rs = this.form.reader.read(response);
25767 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
25769 success : rs.success,
25773 return Roo.decode(response.responseText);
25777 Roo.form.Action.ACTION_TYPES = {
25778 'load' : Roo.form.Action.Load,
25779 'submit' : Roo.form.Action.Submit
25782 * Ext JS Library 1.1.1
25783 * Copyright(c) 2006-2007, Ext JS, LLC.
25785 * Originally Released Under LGPL - original licence link has changed is not relivant.
25788 * <script type="text/javascript">
25792 * @class Roo.form.Layout
25793 * @extends Roo.Component
25794 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
25796 * @param {Object} config Configuration options
25798 Roo.form.Layout = function(config){
25800 if (config.items) {
25801 xitems = config.items;
25802 delete config.items;
25804 Roo.form.Layout.superclass.constructor.call(this, config);
25806 Roo.each(xitems, this.addxtype, this);
25810 Roo.extend(Roo.form.Layout, Roo.Component, {
25812 * @cfg {String/Object} autoCreate
25813 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
25816 * @cfg {String/Object/Function} style
25817 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
25818 * a function which returns such a specification.
25821 * @cfg {String} labelAlign
25822 * Valid values are "left," "top" and "right" (defaults to "left")
25825 * @cfg {Number} labelWidth
25826 * Fixed width in pixels of all field labels (defaults to undefined)
25829 * @cfg {Boolean} clear
25830 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
25834 * @cfg {String} labelSeparator
25835 * The separator to use after field labels (defaults to ':')
25837 labelSeparator : ':',
25839 * @cfg {Boolean} hideLabels
25840 * True to suppress the display of field labels in this layout (defaults to false)
25842 hideLabels : false,
25845 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
25850 onRender : function(ct, position){
25851 if(this.el){ // from markup
25852 this.el = Roo.get(this.el);
25853 }else { // generate
25854 var cfg = this.getAutoCreate();
25855 this.el = ct.createChild(cfg, position);
25858 this.el.applyStyles(this.style);
25860 if(this.labelAlign){
25861 this.el.addClass('x-form-label-'+this.labelAlign);
25863 if(this.hideLabels){
25864 this.labelStyle = "display:none";
25865 this.elementStyle = "padding-left:0;";
25867 if(typeof this.labelWidth == 'number'){
25868 this.labelStyle = "width:"+this.labelWidth+"px;";
25869 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
25871 if(this.labelAlign == 'top'){
25872 this.labelStyle = "width:auto;";
25873 this.elementStyle = "padding-left:0;";
25876 var stack = this.stack;
25877 var slen = stack.length;
25879 if(!this.fieldTpl){
25880 var t = new Roo.Template(
25881 '<div class="x-form-item {5}">',
25882 '<label for="{0}" style="{2}">{1}{4}</label>',
25883 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
25885 '</div><div class="x-form-clear-left"></div>'
25887 t.disableFormats = true;
25889 Roo.form.Layout.prototype.fieldTpl = t;
25891 for(var i = 0; i < slen; i++) {
25892 if(stack[i].isFormField){
25893 this.renderField(stack[i]);
25895 this.renderComponent(stack[i]);
25900 this.el.createChild({cls:'x-form-clear'});
25905 renderField : function(f){
25906 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
25909 f.labelStyle||this.labelStyle||'', //2
25910 this.elementStyle||'', //3
25911 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
25912 f.itemCls||this.itemCls||'' //5
25913 ], true).getPrevSibling());
25917 renderComponent : function(c){
25918 c.render(c.isLayout ? this.el : this.el.createChild());
25921 * Adds a object form elements (using the xtype property as the factory method.)
25922 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
25923 * @param {Object} config
25925 addxtype : function(o)
25927 // create the lement.
25928 o.form = this.form;
25929 var fe = Roo.factory(o, Roo.form);
25930 this.form.allItems.push(fe);
25931 this.stack.push(fe);
25933 if (fe.isFormField) {
25934 this.form.items.add(fe);
25942 * @class Roo.form.Column
25943 * @extends Roo.form.Layout
25944 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
25946 * @param {Object} config Configuration options
25948 Roo.form.Column = function(config){
25949 Roo.form.Column.superclass.constructor.call(this, config);
25952 Roo.extend(Roo.form.Column, Roo.form.Layout, {
25954 * @cfg {Number/String} width
25955 * The fixed width of the column in pixels or CSS value (defaults to "auto")
25958 * @cfg {String/Object} autoCreate
25959 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
25963 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
25966 onRender : function(ct, position){
25967 Roo.form.Column.superclass.onRender.call(this, ct, position);
25969 this.el.setWidth(this.width);
25976 * @class Roo.form.Row
25977 * @extends Roo.form.Layout
25978 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
25980 * @param {Object} config Configuration options
25984 Roo.form.Row = function(config){
25985 Roo.form.Row.superclass.constructor.call(this, config);
25988 Roo.extend(Roo.form.Row, Roo.form.Layout, {
25990 * @cfg {Number/String} width
25991 * The fixed width of the column in pixels or CSS value (defaults to "auto")
25994 * @cfg {Number/String} height
25995 * The fixed height of the column in pixels or CSS value (defaults to "auto")
25997 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26001 onRender : function(ct, position){
26002 //console.log('row render');
26004 var t = new Roo.Template(
26005 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26006 '<label for="{0}" style="{2}">{1}{4}</label>',
26007 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26011 t.disableFormats = true;
26013 Roo.form.Layout.prototype.rowTpl = t;
26015 this.fieldTpl = this.rowTpl;
26017 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26018 var labelWidth = 100;
26020 if ((this.labelAlign != 'top')) {
26021 if (typeof this.labelWidth == 'number') {
26022 labelWidth = this.labelWidth
26024 this.padWidth = 20 + labelWidth;
26028 Roo.form.Column.superclass.onRender.call(this, ct, position);
26030 this.el.setWidth(this.width);
26033 this.el.setHeight(this.height);
26038 renderField : function(f){
26039 f.fieldEl = this.fieldTpl.append(this.el, [
26040 f.id, f.fieldLabel,
26041 f.labelStyle||this.labelStyle||'',
26042 this.elementStyle||'',
26043 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26044 f.itemCls||this.itemCls||'',
26045 f.width ? f.width + this.padWidth : 160 + this.padWidth
26052 * @class Roo.form.FieldSet
26053 * @extends Roo.form.Layout
26054 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26056 * @param {Object} config Configuration options
26058 Roo.form.FieldSet = function(config){
26059 Roo.form.FieldSet.superclass.constructor.call(this, config);
26062 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26064 * @cfg {String} legend
26065 * The text to display as the legend for the FieldSet (defaults to '')
26068 * @cfg {String/Object} autoCreate
26069 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26073 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26076 onRender : function(ct, position){
26077 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26079 this.setLegend(this.legend);
26084 setLegend : function(text){
26086 this.el.child('legend').update(text);
26091 * Ext JS Library 1.1.1
26092 * Copyright(c) 2006-2007, Ext JS, LLC.
26094 * Originally Released Under LGPL - original licence link has changed is not relivant.
26097 * <script type="text/javascript">
26100 * @class Roo.form.VTypes
26101 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26104 Roo.form.VTypes = function(){
26105 // closure these in so they are only created once.
26106 var alpha = /^[a-zA-Z_]+$/;
26107 var alphanum = /^[a-zA-Z0-9_]+$/;
26108 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26109 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26111 // All these messages and functions are configurable
26114 * The function used to validate email addresses
26115 * @param {String} value The email address
26117 'email' : function(v){
26118 return email.test(v);
26121 * The error text to display when the email validation function returns false
26124 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26126 * The keystroke filter mask to be applied on email input
26129 'emailMask' : /[a-z0-9_\.\-@]/i,
26132 * The function used to validate URLs
26133 * @param {String} value The URL
26135 'url' : function(v){
26136 return url.test(v);
26139 * The error text to display when the url validation function returns false
26142 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26145 * The function used to validate alpha values
26146 * @param {String} value The value
26148 'alpha' : function(v){
26149 return alpha.test(v);
26152 * The error text to display when the alpha validation function returns false
26155 'alphaText' : 'This field should only contain letters and _',
26157 * The keystroke filter mask to be applied on alpha input
26160 'alphaMask' : /[a-z_]/i,
26163 * The function used to validate alphanumeric values
26164 * @param {String} value The value
26166 'alphanum' : function(v){
26167 return alphanum.test(v);
26170 * The error text to display when the alphanumeric validation function returns false
26173 'alphanumText' : 'This field should only contain letters, numbers and _',
26175 * The keystroke filter mask to be applied on alphanumeric input
26178 'alphanumMask' : /[a-z0-9_]/i
26180 }();//<script type="text/javascript">
26183 * @class Roo.form.FCKeditor
26184 * @extends Roo.form.TextArea
26185 * Wrapper around the FCKEditor http://www.fckeditor.net
26187 * Creates a new FCKeditor
26188 * @param {Object} config Configuration options
26190 Roo.form.FCKeditor = function(config){
26191 Roo.form.FCKeditor.superclass.constructor.call(this, config);
26194 * @event editorinit
26195 * Fired when the editor is initialized - you can add extra handlers here..
26196 * @param {FCKeditor} this
26197 * @param {Object} the FCK object.
26204 Roo.form.FCKeditor.editors = { };
26205 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26207 //defaultAutoCreate : {
26208 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
26212 * @cfg {Object} fck options - see fck manual for details.
26217 * @cfg {Object} fck toolbar set (Basic or Default)
26219 toolbarSet : 'Basic',
26221 * @cfg {Object} fck BasePath
26223 basePath : '/fckeditor/',
26231 onRender : function(ct, position)
26234 this.defaultAutoCreate = {
26236 style:"width:300px;height:60px;",
26237 autocomplete: "new-password"
26240 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26243 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26244 if(this.preventScrollbars){
26245 this.el.setStyle("overflow", "hidden");
26247 this.el.setHeight(this.growMin);
26250 //console.log('onrender' + this.getId() );
26251 Roo.form.FCKeditor.editors[this.getId()] = this;
26254 this.replaceTextarea() ;
26258 getEditor : function() {
26259 return this.fckEditor;
26262 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
26263 * @param {Mixed} value The value to set
26267 setValue : function(value)
26269 //console.log('setValue: ' + value);
26271 if(typeof(value) == 'undefined') { // not sure why this is happending...
26274 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26276 //if(!this.el || !this.getEditor()) {
26277 // this.value = value;
26278 //this.setValue.defer(100,this,[value]);
26282 if(!this.getEditor()) {
26286 this.getEditor().SetData(value);
26293 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
26294 * @return {Mixed} value The field value
26296 getValue : function()
26299 if (this.frame && this.frame.dom.style.display == 'none') {
26300 return Roo.form.FCKeditor.superclass.getValue.call(this);
26303 if(!this.el || !this.getEditor()) {
26305 // this.getValue.defer(100,this);
26310 var value=this.getEditor().GetData();
26311 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26312 return Roo.form.FCKeditor.superclass.getValue.call(this);
26318 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
26319 * @return {Mixed} value The field value
26321 getRawValue : function()
26323 if (this.frame && this.frame.dom.style.display == 'none') {
26324 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26327 if(!this.el || !this.getEditor()) {
26328 //this.getRawValue.defer(100,this);
26335 var value=this.getEditor().GetData();
26336 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26337 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26341 setSize : function(w,h) {
26345 //if (this.frame && this.frame.dom.style.display == 'none') {
26346 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26349 //if(!this.el || !this.getEditor()) {
26350 // this.setSize.defer(100,this, [w,h]);
26356 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26358 this.frame.dom.setAttribute('width', w);
26359 this.frame.dom.setAttribute('height', h);
26360 this.frame.setSize(w,h);
26364 toggleSourceEdit : function(value) {
26368 this.el.dom.style.display = value ? '' : 'none';
26369 this.frame.dom.style.display = value ? 'none' : '';
26374 focus: function(tag)
26376 if (this.frame.dom.style.display == 'none') {
26377 return Roo.form.FCKeditor.superclass.focus.call(this);
26379 if(!this.el || !this.getEditor()) {
26380 this.focus.defer(100,this, [tag]);
26387 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26388 this.getEditor().Focus();
26390 if (!this.getEditor().Selection.GetSelection()) {
26391 this.focus.defer(100,this, [tag]);
26396 var r = this.getEditor().EditorDocument.createRange();
26397 r.setStart(tgs[0],0);
26398 r.setEnd(tgs[0],0);
26399 this.getEditor().Selection.GetSelection().removeAllRanges();
26400 this.getEditor().Selection.GetSelection().addRange(r);
26401 this.getEditor().Focus();
26408 replaceTextarea : function()
26410 if ( document.getElementById( this.getId() + '___Frame' ) ) {
26413 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26415 // We must check the elements firstly using the Id and then the name.
26416 var oTextarea = document.getElementById( this.getId() );
26418 var colElementsByName = document.getElementsByName( this.getId() ) ;
26420 oTextarea.style.display = 'none' ;
26422 if ( oTextarea.tabIndex ) {
26423 this.TabIndex = oTextarea.tabIndex ;
26426 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26427 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26428 this.frame = Roo.get(this.getId() + '___Frame')
26431 _getConfigHtml : function()
26435 for ( var o in this.fckconfig ) {
26436 sConfig += sConfig.length > 0 ? '&' : '';
26437 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26440 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26444 _getIFrameHtml : function()
26446 var sFile = 'fckeditor.html' ;
26447 /* no idea what this is about..
26450 if ( (/fcksource=true/i).test( window.top.location.search ) )
26451 sFile = 'fckeditor.original.html' ;
26456 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26457 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
26460 var html = '<iframe id="' + this.getId() +
26461 '___Frame" src="' + sLink +
26462 '" width="' + this.width +
26463 '" height="' + this.height + '"' +
26464 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
26465 ' frameborder="0" scrolling="no"></iframe>' ;
26470 _insertHtmlBefore : function( html, element )
26472 if ( element.insertAdjacentHTML ) {
26474 element.insertAdjacentHTML( 'beforeBegin', html ) ;
26476 var oRange = document.createRange() ;
26477 oRange.setStartBefore( element ) ;
26478 var oFragment = oRange.createContextualFragment( html );
26479 element.parentNode.insertBefore( oFragment, element ) ;
26492 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26494 function FCKeditor_OnComplete(editorInstance){
26495 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26496 f.fckEditor = editorInstance;
26497 //console.log("loaded");
26498 f.fireEvent('editorinit', f, editorInstance);
26518 //<script type="text/javascript">
26520 * @class Roo.form.GridField
26521 * @extends Roo.form.Field
26522 * Embed a grid (or editable grid into a form)
26525 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26527 * xgrid.store = Roo.data.Store
26528 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26529 * xgrid.store.reader = Roo.data.JsonReader
26533 * Creates a new GridField
26534 * @param {Object} config Configuration options
26536 Roo.form.GridField = function(config){
26537 Roo.form.GridField.superclass.constructor.call(this, config);
26541 Roo.extend(Roo.form.GridField, Roo.form.Field, {
26543 * @cfg {Number} width - used to restrict width of grid..
26547 * @cfg {Number} height - used to restrict height of grid..
26551 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26557 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26558 * {tag: "input", type: "checkbox", autocomplete: "off"})
26560 // defaultAutoCreate : { tag: 'div' },
26561 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26563 * @cfg {String} addTitle Text to include for adding a title.
26567 onResize : function(){
26568 Roo.form.Field.superclass.onResize.apply(this, arguments);
26571 initEvents : function(){
26572 // Roo.form.Checkbox.superclass.initEvents.call(this);
26573 // has no events...
26578 getResizeEl : function(){
26582 getPositionEl : function(){
26587 onRender : function(ct, position){
26589 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26590 var style = this.style;
26593 Roo.form.GridField.superclass.onRender.call(this, ct, position);
26594 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26595 this.viewEl = this.wrap.createChild({ tag: 'div' });
26597 this.viewEl.applyStyles(style);
26600 this.viewEl.setWidth(this.width);
26603 this.viewEl.setHeight(this.height);
26605 //if(this.inputValue !== undefined){
26606 //this.setValue(this.value);
26609 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26612 this.grid.render();
26613 this.grid.getDataSource().on('remove', this.refreshValue, this);
26614 this.grid.getDataSource().on('update', this.refreshValue, this);
26615 this.grid.on('afteredit', this.refreshValue, this);
26621 * Sets the value of the item.
26622 * @param {String} either an object or a string..
26624 setValue : function(v){
26626 v = v || []; // empty set..
26627 // this does not seem smart - it really only affects memoryproxy grids..
26628 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26629 var ds = this.grid.getDataSource();
26630 // assumes a json reader..
26632 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
26633 ds.loadData( data);
26635 // clear selection so it does not get stale.
26636 if (this.grid.sm) {
26637 this.grid.sm.clearSelections();
26640 Roo.form.GridField.superclass.setValue.call(this, v);
26641 this.refreshValue();
26642 // should load data in the grid really....
26646 refreshValue: function() {
26648 this.grid.getDataSource().each(function(r) {
26651 this.el.dom.value = Roo.encode(val);
26659 * Ext JS Library 1.1.1
26660 * Copyright(c) 2006-2007, Ext JS, LLC.
26662 * Originally Released Under LGPL - original licence link has changed is not relivant.
26665 * <script type="text/javascript">
26668 * @class Roo.form.DisplayField
26669 * @extends Roo.form.Field
26670 * A generic Field to display non-editable data.
26671 * @cfg {Boolean} closable (true|false) default false
26673 * Creates a new Display Field item.
26674 * @param {Object} config Configuration options
26676 Roo.form.DisplayField = function(config){
26677 Roo.form.DisplayField.superclass.constructor.call(this, config);
26682 * Fires after the click the close btn
26683 * @param {Roo.form.DisplayField} this
26689 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
26690 inputType: 'hidden',
26696 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26698 focusClass : undefined,
26700 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26702 fieldClass: 'x-form-field',
26705 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
26707 valueRenderer: undefined,
26711 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26712 * {tag: "input", type: "checkbox", autocomplete: "off"})
26715 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
26719 onResize : function(){
26720 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
26724 initEvents : function(){
26725 // Roo.form.Checkbox.superclass.initEvents.call(this);
26726 // has no events...
26729 this.closeEl.on('click', this.onClose, this);
26735 getResizeEl : function(){
26739 getPositionEl : function(){
26744 onRender : function(ct, position){
26746 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
26747 //if(this.inputValue !== undefined){
26748 this.wrap = this.el.wrap();
26750 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
26753 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
26756 if (this.bodyStyle) {
26757 this.viewEl.applyStyles(this.bodyStyle);
26759 //this.viewEl.setStyle('padding', '2px');
26761 this.setValue(this.value);
26766 initValue : Roo.emptyFn,
26771 onClick : function(){
26776 * Sets the checked state of the checkbox.
26777 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
26779 setValue : function(v){
26781 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
26782 // this might be called before we have a dom element..
26783 if (!this.viewEl) {
26786 this.viewEl.dom.innerHTML = html;
26787 Roo.form.DisplayField.superclass.setValue.call(this, v);
26791 onClose : function(e)
26793 e.preventDefault();
26795 this.fireEvent('close', this);
26804 * @class Roo.form.DayPicker
26805 * @extends Roo.form.Field
26806 * A Day picker show [M] [T] [W] ....
26808 * Creates a new Day Picker
26809 * @param {Object} config Configuration options
26811 Roo.form.DayPicker= function(config){
26812 Roo.form.DayPicker.superclass.constructor.call(this, config);
26816 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
26818 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26820 focusClass : undefined,
26822 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26824 fieldClass: "x-form-field",
26827 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26828 * {tag: "input", type: "checkbox", autocomplete: "off"})
26830 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
26833 actionMode : 'viewEl',
26837 inputType : 'hidden',
26840 inputElement: false, // real input element?
26841 basedOn: false, // ????
26843 isFormField: true, // not sure where this is needed!!!!
26845 onResize : function(){
26846 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
26847 if(!this.boxLabel){
26848 this.el.alignTo(this.wrap, 'c-c');
26852 initEvents : function(){
26853 Roo.form.Checkbox.superclass.initEvents.call(this);
26854 this.el.on("click", this.onClick, this);
26855 this.el.on("change", this.onClick, this);
26859 getResizeEl : function(){
26863 getPositionEl : function(){
26869 onRender : function(ct, position){
26870 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
26872 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
26874 var r1 = '<table><tr>';
26875 var r2 = '<tr class="x-form-daypick-icons">';
26876 for (var i=0; i < 7; i++) {
26877 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
26878 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
26881 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
26882 viewEl.select('img').on('click', this.onClick, this);
26883 this.viewEl = viewEl;
26886 // this will not work on Chrome!!!
26887 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
26888 this.el.on('propertychange', this.setFromHidden, this); //ie
26896 initValue : Roo.emptyFn,
26899 * Returns the checked state of the checkbox.
26900 * @return {Boolean} True if checked, else false
26902 getValue : function(){
26903 return this.el.dom.value;
26908 onClick : function(e){
26909 //this.setChecked(!this.checked);
26910 Roo.get(e.target).toggleClass('x-menu-item-checked');
26911 this.refreshValue();
26912 //if(this.el.dom.checked != this.checked){
26913 // this.setValue(this.el.dom.checked);
26918 refreshValue : function()
26921 this.viewEl.select('img',true).each(function(e,i,n) {
26922 val += e.is(".x-menu-item-checked") ? String(n) : '';
26924 this.setValue(val, true);
26928 * Sets the checked state of the checkbox.
26929 * On is always based on a string comparison between inputValue and the param.
26930 * @param {Boolean/String} value - the value to set
26931 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
26933 setValue : function(v,suppressEvent){
26934 if (!this.el.dom) {
26937 var old = this.el.dom.value ;
26938 this.el.dom.value = v;
26939 if (suppressEvent) {
26943 // update display..
26944 this.viewEl.select('img',true).each(function(e,i,n) {
26946 var on = e.is(".x-menu-item-checked");
26947 var newv = v.indexOf(String(n)) > -1;
26949 e.toggleClass('x-menu-item-checked');
26955 this.fireEvent('change', this, v, old);
26960 // handle setting of hidden value by some other method!!?!?
26961 setFromHidden: function()
26966 //console.log("SET FROM HIDDEN");
26967 //alert('setFrom hidden');
26968 this.setValue(this.el.dom.value);
26971 onDestroy : function()
26974 Roo.get(this.viewEl).remove();
26977 Roo.form.DayPicker.superclass.onDestroy.call(this);
26981 * RooJS Library 1.1.1
26982 * Copyright(c) 2008-2011 Alan Knowles
26989 * @class Roo.form.ComboCheck
26990 * @extends Roo.form.ComboBox
26991 * A combobox for multiple select items.
26993 * FIXME - could do with a reset button..
26996 * Create a new ComboCheck
26997 * @param {Object} config Configuration options
26999 Roo.form.ComboCheck = function(config){
27000 Roo.form.ComboCheck.superclass.constructor.call(this, config);
27001 // should verify some data...
27003 // hiddenName = required..
27004 // displayField = required
27005 // valudField == required
27006 var req= [ 'hiddenName', 'displayField', 'valueField' ];
27008 Roo.each(req, function(e) {
27009 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27010 throw "Roo.form.ComboCheck : missing value for: " + e;
27017 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27022 selectedClass: 'x-menu-item-checked',
27025 onRender : function(ct, position){
27031 var cls = 'x-combo-list';
27034 this.tpl = new Roo.Template({
27035 html : '<div class="'+cls+'-item x-menu-check-item">' +
27036 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
27037 '<span>{' + this.displayField + '}</span>' +
27044 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27045 this.view.singleSelect = false;
27046 this.view.multiSelect = true;
27047 this.view.toggleSelect = true;
27048 this.pageTb.add(new Roo.Toolbar.Fill(), {
27051 handler: function()
27058 onViewOver : function(e, t){
27064 onViewClick : function(doFocus,index){
27068 select: function () {
27069 //Roo.log("SELECT CALLED");
27072 selectByValue : function(xv, scrollIntoView){
27073 var ar = this.getValueArray();
27076 Roo.each(ar, function(v) {
27077 if(v === undefined || v === null){
27080 var r = this.findRecord(this.valueField, v);
27082 sels.push(this.store.indexOf(r))
27086 this.view.select(sels);
27092 onSelect : function(record, index){
27093 // Roo.log("onselect Called");
27094 // this is only called by the clear button now..
27095 this.view.clearSelections();
27096 this.setValue('[]');
27097 if (this.value != this.valueBefore) {
27098 this.fireEvent('change', this, this.value, this.valueBefore);
27099 this.valueBefore = this.value;
27102 getValueArray : function()
27107 //Roo.log(this.value);
27108 if (typeof(this.value) == 'undefined') {
27111 var ar = Roo.decode(this.value);
27112 return ar instanceof Array ? ar : []; //?? valid?
27115 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
27120 expand : function ()
27123 Roo.form.ComboCheck.superclass.expand.call(this);
27124 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27125 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27130 collapse : function(){
27131 Roo.form.ComboCheck.superclass.collapse.call(this);
27132 var sl = this.view.getSelectedIndexes();
27133 var st = this.store;
27137 Roo.each(sl, function(i) {
27139 nv.push(r.get(this.valueField));
27141 this.setValue(Roo.encode(nv));
27142 if (this.value != this.valueBefore) {
27144 this.fireEvent('change', this, this.value, this.valueBefore);
27145 this.valueBefore = this.value;
27150 setValue : function(v){
27154 var vals = this.getValueArray();
27156 Roo.each(vals, function(k) {
27157 var r = this.findRecord(this.valueField, k);
27159 tv.push(r.data[this.displayField]);
27160 }else if(this.valueNotFoundText !== undefined){
27161 tv.push( this.valueNotFoundText );
27166 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27167 this.hiddenField.value = v;
27173 * Ext JS Library 1.1.1
27174 * Copyright(c) 2006-2007, Ext JS, LLC.
27176 * Originally Released Under LGPL - original licence link has changed is not relivant.
27179 * <script type="text/javascript">
27183 * @class Roo.form.Signature
27184 * @extends Roo.form.Field
27188 * @param {Object} config Configuration options
27191 Roo.form.Signature = function(config){
27192 Roo.form.Signature.superclass.constructor.call(this, config);
27194 this.addEvents({// not in used??
27197 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27198 * @param {Roo.form.Signature} combo This combo box
27203 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27204 * @param {Roo.form.ComboBox} combo This combo box
27205 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27211 Roo.extend(Roo.form.Signature, Roo.form.Field, {
27213 * @cfg {Object} labels Label to use when rendering a form.
27217 * confirm : "Confirm"
27222 confirm : "Confirm"
27225 * @cfg {Number} width The signature panel width (defaults to 300)
27229 * @cfg {Number} height The signature panel height (defaults to 100)
27233 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27235 allowBlank : false,
27238 // {Object} signPanel The signature SVG panel element (defaults to {})
27240 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27241 isMouseDown : false,
27242 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27243 isConfirmed : false,
27244 // {String} signatureTmp SVG mapping string (defaults to empty string)
27248 defaultAutoCreate : { // modified by initCompnoent..
27254 onRender : function(ct, position){
27256 Roo.form.Signature.superclass.onRender.call(this, ct, position);
27258 this.wrap = this.el.wrap({
27259 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27262 this.createToolbar(this);
27263 this.signPanel = this.wrap.createChild({
27265 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27269 this.svgID = Roo.id();
27270 this.svgEl = this.signPanel.createChild({
27271 xmlns : 'http://www.w3.org/2000/svg',
27273 id : this.svgID + "-svg",
27275 height: this.height,
27276 viewBox: '0 0 '+this.width+' '+this.height,
27280 id: this.svgID + "-svg-r",
27282 height: this.height,
27287 id: this.svgID + "-svg-l",
27289 y1: (this.height*0.8), // start set the line in 80% of height
27290 x2: this.width, // end
27291 y2: (this.height*0.8), // end set the line in 80% of height
27293 'stroke-width': "1",
27294 'stroke-dasharray': "3",
27295 'shape-rendering': "crispEdges",
27296 'pointer-events': "none"
27300 id: this.svgID + "-svg-p",
27302 'stroke-width': "3",
27304 'pointer-events': 'none'
27309 this.svgBox = this.svgEl.dom.getScreenCTM();
27311 createSVG : function(){
27312 var svg = this.signPanel;
27313 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27316 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27317 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27318 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27319 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27320 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27321 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27322 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27325 isTouchEvent : function(e){
27326 return e.type.match(/^touch/);
27328 getCoords : function (e) {
27329 var pt = this.svgEl.dom.createSVGPoint();
27332 if (this.isTouchEvent(e)) {
27333 pt.x = e.targetTouches[0].clientX;
27334 pt.y = e.targetTouches[0].clientY;
27336 var a = this.svgEl.dom.getScreenCTM();
27337 var b = a.inverse();
27338 var mx = pt.matrixTransform(b);
27339 return mx.x + ',' + mx.y;
27341 //mouse event headler
27342 down : function (e) {
27343 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27344 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27346 this.isMouseDown = true;
27348 e.preventDefault();
27350 move : function (e) {
27351 if (this.isMouseDown) {
27352 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27353 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27356 e.preventDefault();
27358 up : function (e) {
27359 this.isMouseDown = false;
27360 var sp = this.signatureTmp.split(' ');
27363 if(!sp[sp.length-2].match(/^L/)){
27367 this.signatureTmp = sp.join(" ");
27370 if(this.getValue() != this.signatureTmp){
27371 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27372 this.isConfirmed = false;
27374 e.preventDefault();
27378 * Protected method that will not generally be called directly. It
27379 * is called when the editor creates its toolbar. Override this method if you need to
27380 * add custom toolbar buttons.
27381 * @param {HtmlEditor} editor
27383 createToolbar : function(editor){
27384 function btn(id, toggle, handler){
27385 var xid = fid + '-'+ id ;
27389 cls : 'x-btn-icon x-edit-'+id,
27390 enableToggle:toggle !== false,
27391 scope: editor, // was editor...
27392 handler:handler||editor.relayBtnCmd,
27393 clickEvent:'mousedown',
27394 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27400 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27404 cls : ' x-signature-btn x-signature-'+id,
27405 scope: editor, // was editor...
27406 handler: this.reset,
27407 clickEvent:'mousedown',
27408 text: this.labels.clear
27415 cls : ' x-signature-btn x-signature-'+id,
27416 scope: editor, // was editor...
27417 handler: this.confirmHandler,
27418 clickEvent:'mousedown',
27419 text: this.labels.confirm
27426 * when user is clicked confirm then show this image.....
27428 * @return {String} Image Data URI
27430 getImageDataURI : function(){
27431 var svg = this.svgEl.dom.parentNode.innerHTML;
27432 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27437 * @return {Boolean} this.isConfirmed
27439 getConfirmed : function(){
27440 return this.isConfirmed;
27444 * @return {Number} this.width
27446 getWidth : function(){
27451 * @return {Number} this.height
27453 getHeight : function(){
27454 return this.height;
27457 getSignature : function(){
27458 return this.signatureTmp;
27461 reset : function(){
27462 this.signatureTmp = '';
27463 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27464 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27465 this.isConfirmed = false;
27466 Roo.form.Signature.superclass.reset.call(this);
27468 setSignature : function(s){
27469 this.signatureTmp = s;
27470 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27471 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27473 this.isConfirmed = false;
27474 Roo.form.Signature.superclass.reset.call(this);
27477 // Roo.log(this.signPanel.dom.contentWindow.up())
27480 setConfirmed : function(){
27484 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27487 confirmHandler : function(){
27488 if(!this.getSignature()){
27492 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27493 this.setValue(this.getSignature());
27494 this.isConfirmed = true;
27496 this.fireEvent('confirm', this);
27499 // Subclasses should provide the validation implementation by overriding this
27500 validateValue : function(value){
27501 if(this.allowBlank){
27505 if(this.isConfirmed){
27512 * Ext JS Library 1.1.1
27513 * Copyright(c) 2006-2007, Ext JS, LLC.
27515 * Originally Released Under LGPL - original licence link has changed is not relivant.
27518 * <script type="text/javascript">
27523 * @class Roo.form.ComboBox
27524 * @extends Roo.form.TriggerField
27525 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27527 * Create a new ComboBox.
27528 * @param {Object} config Configuration options
27530 Roo.form.Select = function(config){
27531 Roo.form.Select.superclass.constructor.call(this, config);
27535 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27537 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27540 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27541 * rendering into an Roo.Editor, defaults to false)
27544 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27545 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27548 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27551 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27552 * the dropdown list (defaults to undefined, with no header element)
27556 * @cfg {String/Roo.Template} tpl The template to use to render the output
27560 defaultAutoCreate : {tag: "select" },
27562 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27564 listWidth: undefined,
27566 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27567 * mode = 'remote' or 'text' if mode = 'local')
27569 displayField: undefined,
27571 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27572 * mode = 'remote' or 'value' if mode = 'local').
27573 * Note: use of a valueField requires the user make a selection
27574 * in order for a value to be mapped.
27576 valueField: undefined,
27580 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27581 * field's data value (defaults to the underlying DOM element's name)
27583 hiddenName: undefined,
27585 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27589 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27591 selectedClass: 'x-combo-selected',
27593 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
27594 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27595 * which displays a downward arrow icon).
27597 triggerClass : 'x-form-arrow-trigger',
27599 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27603 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27604 * anchor positions (defaults to 'tl-bl')
27606 listAlign: 'tl-bl?',
27608 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27612 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
27613 * query specified by the allQuery config option (defaults to 'query')
27615 triggerAction: 'query',
27617 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27618 * (defaults to 4, does not apply if editable = false)
27622 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27623 * delay (typeAheadDelay) if it matches a known value (defaults to false)
27627 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27628 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27632 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27633 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
27637 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
27638 * when editable = true (defaults to false)
27640 selectOnFocus:false,
27642 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27644 queryParam: 'query',
27646 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
27647 * when mode = 'remote' (defaults to 'Loading...')
27649 loadingText: 'Loading...',
27651 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27655 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27659 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27660 * traditional select (defaults to true)
27664 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27668 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27672 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27673 * listWidth has a higher value)
27677 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27678 * allow the user to set arbitrary text into the field (defaults to false)
27680 forceSelection:false,
27682 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27683 * if typeAhead = true (defaults to 250)
27685 typeAheadDelay : 250,
27687 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27688 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27690 valueNotFoundText : undefined,
27693 * @cfg {String} defaultValue The value displayed after loading the store.
27698 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27700 blockFocus : false,
27703 * @cfg {Boolean} disableClear Disable showing of clear button.
27705 disableClear : false,
27707 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
27709 alwaysQuery : false,
27715 // element that contains real text value.. (when hidden is used..)
27718 onRender : function(ct, position){
27719 Roo.form.Field.prototype.onRender.call(this, ct, position);
27722 this.store.on('beforeload', this.onBeforeLoad, this);
27723 this.store.on('load', this.onLoad, this);
27724 this.store.on('loadexception', this.onLoadException, this);
27725 this.store.load({});
27733 initEvents : function(){
27734 //Roo.form.ComboBox.superclass.initEvents.call(this);
27738 onDestroy : function(){
27741 this.store.un('beforeload', this.onBeforeLoad, this);
27742 this.store.un('load', this.onLoad, this);
27743 this.store.un('loadexception', this.onLoadException, this);
27745 //Roo.form.ComboBox.superclass.onDestroy.call(this);
27749 fireKey : function(e){
27750 if(e.isNavKeyPress() && !this.list.isVisible()){
27751 this.fireEvent("specialkey", this, e);
27756 onResize: function(w, h){
27764 * Allow or prevent the user from directly editing the field text. If false is passed,
27765 * the user will only be able to select from the items defined in the dropdown list. This method
27766 * is the runtime equivalent of setting the 'editable' config option at config time.
27767 * @param {Boolean} value True to allow the user to directly edit the field text
27769 setEditable : function(value){
27774 onBeforeLoad : function(){
27776 Roo.log("Select before load");
27779 this.innerList.update(this.loadingText ?
27780 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
27781 //this.restrictHeight();
27782 this.selectedIndex = -1;
27786 onLoad : function(){
27789 var dom = this.el.dom;
27790 dom.innerHTML = '';
27791 var od = dom.ownerDocument;
27793 if (this.emptyText) {
27794 var op = od.createElement('option');
27795 op.setAttribute('value', '');
27796 op.innerHTML = String.format('{0}', this.emptyText);
27797 dom.appendChild(op);
27799 if(this.store.getCount() > 0){
27801 var vf = this.valueField;
27802 var df = this.displayField;
27803 this.store.data.each(function(r) {
27804 // which colmsn to use... testing - cdoe / title..
27805 var op = od.createElement('option');
27806 op.setAttribute('value', r.data[vf]);
27807 op.innerHTML = String.format('{0}', r.data[df]);
27808 dom.appendChild(op);
27810 if (typeof(this.defaultValue != 'undefined')) {
27811 this.setValue(this.defaultValue);
27816 //this.onEmptyResults();
27821 onLoadException : function()
27823 dom.innerHTML = '';
27825 Roo.log("Select on load exception");
27829 Roo.log(this.store.reader.jsonData);
27830 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
27831 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
27837 onTypeAhead : function(){
27842 onSelect : function(record, index){
27843 Roo.log('on select?');
27845 if(this.fireEvent('beforeselect', this, record, index) !== false){
27846 this.setFromData(index > -1 ? record.data : false);
27848 this.fireEvent('select', this, record, index);
27853 * Returns the currently selected field value or empty string if no value is set.
27854 * @return {String} value The selected value
27856 getValue : function(){
27857 var dom = this.el.dom;
27858 this.value = dom.options[dom.selectedIndex].value;
27864 * Clears any text/value currently set in the field
27866 clearValue : function(){
27868 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
27873 * Sets the specified value into the field. If the value finds a match, the corresponding record text
27874 * will be displayed in the field. If the value does not match the data value of an existing item,
27875 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
27876 * Otherwise the field will be blank (although the value will still be set).
27877 * @param {String} value The value to match
27879 setValue : function(v){
27880 var d = this.el.dom;
27881 for (var i =0; i < d.options.length;i++) {
27882 if (v == d.options[i].value) {
27883 d.selectedIndex = i;
27891 * @property {Object} the last set data for the element
27896 * Sets the value of the field based on a object which is related to the record format for the store.
27897 * @param {Object} value the value to set as. or false on reset?
27899 setFromData : function(o){
27900 Roo.log('setfrom data?');
27906 reset : function(){
27910 findRecord : function(prop, value){
27915 if(this.store.getCount() > 0){
27916 this.store.each(function(r){
27917 if(r.data[prop] == value){
27927 getName: function()
27929 // returns hidden if it's set..
27930 if (!this.rendered) {return ''};
27931 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
27939 onEmptyResults : function(){
27940 Roo.log('empty results');
27945 * Returns true if the dropdown list is expanded, else false.
27947 isExpanded : function(){
27952 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
27953 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27954 * @param {String} value The data value of the item to select
27955 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27956 * selected item if it is not currently in view (defaults to true)
27957 * @return {Boolean} True if the value matched an item in the list, else false
27959 selectByValue : function(v, scrollIntoView){
27960 Roo.log('select By Value');
27963 if(v !== undefined && v !== null){
27964 var r = this.findRecord(this.valueField || this.displayField, v);
27966 this.select(this.store.indexOf(r), scrollIntoView);
27974 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
27975 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
27976 * @param {Number} index The zero-based index of the list item to select
27977 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
27978 * selected item if it is not currently in view (defaults to true)
27980 select : function(index, scrollIntoView){
27981 Roo.log('select ');
27984 this.selectedIndex = index;
27985 this.view.select(index);
27986 if(scrollIntoView !== false){
27987 var el = this.view.getNode(index);
27989 this.innerList.scrollChildIntoView(el, false);
27997 validateBlur : function(){
28004 initQuery : function(){
28005 this.doQuery(this.getRawValue());
28009 doForce : function(){
28010 if(this.el.dom.value.length > 0){
28011 this.el.dom.value =
28012 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28018 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
28019 * query allowing the query action to be canceled if needed.
28020 * @param {String} query The SQL query to execute
28021 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28022 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
28023 * saved in the current store (defaults to false)
28025 doQuery : function(q, forceAll){
28027 Roo.log('doQuery?');
28028 if(q === undefined || q === null){
28033 forceAll: forceAll,
28037 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28041 forceAll = qe.forceAll;
28042 if(forceAll === true || (q.length >= this.minChars)){
28043 if(this.lastQuery != q || this.alwaysQuery){
28044 this.lastQuery = q;
28045 if(this.mode == 'local'){
28046 this.selectedIndex = -1;
28048 this.store.clearFilter();
28050 this.store.filter(this.displayField, q);
28054 this.store.baseParams[this.queryParam] = q;
28056 params: this.getParams(q)
28061 this.selectedIndex = -1;
28068 getParams : function(q){
28070 //p[this.queryParam] = q;
28073 p.limit = this.pageSize;
28079 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28081 collapse : function(){
28086 collapseIf : function(e){
28091 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28093 expand : function(){
28101 * @cfg {Boolean} grow
28105 * @cfg {Number} growMin
28109 * @cfg {Number} growMax
28117 setWidth : function()
28121 getResizeEl : function(){
28124 });//<script type="text/javasscript">
28128 * @class Roo.DDView
28129 * A DnD enabled version of Roo.View.
28130 * @param {Element/String} container The Element in which to create the View.
28131 * @param {String} tpl The template string used to create the markup for each element of the View
28132 * @param {Object} config The configuration properties. These include all the config options of
28133 * {@link Roo.View} plus some specific to this class.<br>
28135 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28136 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28138 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28139 .x-view-drag-insert-above {
28140 border-top:1px dotted #3366cc;
28142 .x-view-drag-insert-below {
28143 border-bottom:1px dotted #3366cc;
28149 Roo.DDView = function(container, tpl, config) {
28150 Roo.DDView.superclass.constructor.apply(this, arguments);
28151 this.getEl().setStyle("outline", "0px none");
28152 this.getEl().unselectable();
28153 if (this.dragGroup) {
28154 this.setDraggable(this.dragGroup.split(","));
28156 if (this.dropGroup) {
28157 this.setDroppable(this.dropGroup.split(","));
28159 if (this.deletable) {
28160 this.setDeletable();
28162 this.isDirtyFlag = false;
28168 Roo.extend(Roo.DDView, Roo.View, {
28169 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28170 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28171 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28172 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28176 reset: Roo.emptyFn,
28178 clearInvalid: Roo.form.Field.prototype.clearInvalid,
28180 validate: function() {
28184 destroy: function() {
28185 this.purgeListeners();
28186 this.getEl.removeAllListeners();
28187 this.getEl().remove();
28188 if (this.dragZone) {
28189 if (this.dragZone.destroy) {
28190 this.dragZone.destroy();
28193 if (this.dropZone) {
28194 if (this.dropZone.destroy) {
28195 this.dropZone.destroy();
28200 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28201 getName: function() {
28205 /** Loads the View from a JSON string representing the Records to put into the Store. */
28206 setValue: function(v) {
28208 throw "DDView.setValue(). DDView must be constructed with a valid Store";
28211 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28212 this.store.proxy = new Roo.data.MemoryProxy(data);
28216 /** @return {String} a parenthesised list of the ids of the Records in the View. */
28217 getValue: function() {
28219 this.store.each(function(rec) {
28220 result += rec.id + ',';
28222 return result.substr(0, result.length - 1) + ')';
28225 getIds: function() {
28226 var i = 0, result = new Array(this.store.getCount());
28227 this.store.each(function(rec) {
28228 result[i++] = rec.id;
28233 isDirty: function() {
28234 return this.isDirtyFlag;
28238 * Part of the Roo.dd.DropZone interface. If no target node is found, the
28239 * whole Element becomes the target, and this causes the drop gesture to append.
28241 getTargetFromEvent : function(e) {
28242 var target = e.getTarget();
28243 while ((target !== null) && (target.parentNode != this.el.dom)) {
28244 target = target.parentNode;
28247 target = this.el.dom.lastChild || this.el.dom;
28253 * Create the drag data which consists of an object which has the property "ddel" as
28254 * the drag proxy element.
28256 getDragData : function(e) {
28257 var target = this.findItemFromChild(e.getTarget());
28259 this.handleSelection(e);
28260 var selNodes = this.getSelectedNodes();
28263 copy: this.copy || (this.allowCopy && e.ctrlKey),
28267 var selectedIndices = this.getSelectedIndexes();
28268 for (var i = 0; i < selectedIndices.length; i++) {
28269 dragData.records.push(this.store.getAt(selectedIndices[i]));
28271 if (selNodes.length == 1) {
28272 dragData.ddel = target.cloneNode(true); // the div element
28274 var div = document.createElement('div'); // create the multi element drag "ghost"
28275 div.className = 'multi-proxy';
28276 for (var i = 0, len = selNodes.length; i < len; i++) {
28277 div.appendChild(selNodes[i].cloneNode(true));
28279 dragData.ddel = div;
28281 //console.log(dragData)
28282 //console.log(dragData.ddel.innerHTML)
28285 //console.log('nodragData')
28289 /** Specify to which ddGroup items in this DDView may be dragged. */
28290 setDraggable: function(ddGroup) {
28291 if (ddGroup instanceof Array) {
28292 Roo.each(ddGroup, this.setDraggable, this);
28295 if (this.dragZone) {
28296 this.dragZone.addToGroup(ddGroup);
28298 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28299 containerScroll: true,
28303 // Draggability implies selection. DragZone's mousedown selects the element.
28304 if (!this.multiSelect) { this.singleSelect = true; }
28306 // Wire the DragZone's handlers up to methods in *this*
28307 this.dragZone.getDragData = this.getDragData.createDelegate(this);
28311 /** Specify from which ddGroup this DDView accepts drops. */
28312 setDroppable: function(ddGroup) {
28313 if (ddGroup instanceof Array) {
28314 Roo.each(ddGroup, this.setDroppable, this);
28317 if (this.dropZone) {
28318 this.dropZone.addToGroup(ddGroup);
28320 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28321 containerScroll: true,
28325 // Wire the DropZone's handlers up to methods in *this*
28326 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28327 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28328 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28329 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28330 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28334 /** Decide whether to drop above or below a View node. */
28335 getDropPoint : function(e, n, dd){
28336 if (n == this.el.dom) { return "above"; }
28337 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28338 var c = t + (b - t) / 2;
28339 var y = Roo.lib.Event.getPageY(e);
28347 onNodeEnter : function(n, dd, e, data){
28351 onNodeOver : function(n, dd, e, data){
28352 var pt = this.getDropPoint(e, n, dd);
28353 // set the insert point style on the target node
28354 var dragElClass = this.dropNotAllowed;
28357 if (pt == "above"){
28358 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28359 targetElClass = "x-view-drag-insert-above";
28361 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28362 targetElClass = "x-view-drag-insert-below";
28364 if (this.lastInsertClass != targetElClass){
28365 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28366 this.lastInsertClass = targetElClass;
28369 return dragElClass;
28372 onNodeOut : function(n, dd, e, data){
28373 this.removeDropIndicators(n);
28376 onNodeDrop : function(n, dd, e, data){
28377 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28380 var pt = this.getDropPoint(e, n, dd);
28381 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28382 if (pt == "below") { insertAt++; }
28383 for (var i = 0; i < data.records.length; i++) {
28384 var r = data.records[i];
28385 var dup = this.store.getById(r.id);
28386 if (dup && (dd != this.dragZone)) {
28387 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28390 this.store.insert(insertAt++, r.copy());
28392 data.source.isDirtyFlag = true;
28394 this.store.insert(insertAt++, r);
28396 this.isDirtyFlag = true;
28399 this.dragZone.cachedTarget = null;
28403 removeDropIndicators : function(n){
28405 Roo.fly(n).removeClass([
28406 "x-view-drag-insert-above",
28407 "x-view-drag-insert-below"]);
28408 this.lastInsertClass = "_noclass";
28413 * Utility method. Add a delete option to the DDView's context menu.
28414 * @param {String} imageUrl The URL of the "delete" icon image.
28416 setDeletable: function(imageUrl) {
28417 if (!this.singleSelect && !this.multiSelect) {
28418 this.singleSelect = true;
28420 var c = this.getContextMenu();
28421 this.contextMenu.on("itemclick", function(item) {
28424 this.remove(this.getSelectedIndexes());
28428 this.contextMenu.add({
28435 /** Return the context menu for this DDView. */
28436 getContextMenu: function() {
28437 if (!this.contextMenu) {
28438 // Create the View's context menu
28439 this.contextMenu = new Roo.menu.Menu({
28440 id: this.id + "-contextmenu"
28442 this.el.on("contextmenu", this.showContextMenu, this);
28444 return this.contextMenu;
28447 disableContextMenu: function() {
28448 if (this.contextMenu) {
28449 this.el.un("contextmenu", this.showContextMenu, this);
28453 showContextMenu: function(e, item) {
28454 item = this.findItemFromChild(e.getTarget());
28457 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28458 this.contextMenu.showAt(e.getXY());
28463 * Remove {@link Roo.data.Record}s at the specified indices.
28464 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28466 remove: function(selectedIndices) {
28467 selectedIndices = [].concat(selectedIndices);
28468 for (var i = 0; i < selectedIndices.length; i++) {
28469 var rec = this.store.getAt(selectedIndices[i]);
28470 this.store.remove(rec);
28475 * Double click fires the event, but also, if this is draggable, and there is only one other
28476 * related DropZone, it transfers the selected node.
28478 onDblClick : function(e){
28479 var item = this.findItemFromChild(e.getTarget());
28481 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28484 if (this.dragGroup) {
28485 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28486 while (targets.indexOf(this.dropZone) > -1) {
28487 targets.remove(this.dropZone);
28489 if (targets.length == 1) {
28490 this.dragZone.cachedTarget = null;
28491 var el = Roo.get(targets[0].getEl());
28492 var box = el.getBox(true);
28493 targets[0].onNodeDrop(el.dom, {
28495 xy: [box.x, box.y + box.height - 1]
28496 }, null, this.getDragData(e));
28502 handleSelection: function(e) {
28503 this.dragZone.cachedTarget = null;
28504 var item = this.findItemFromChild(e.getTarget());
28506 this.clearSelections(true);
28509 if (item && (this.multiSelect || this.singleSelect)){
28510 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28511 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28512 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28513 this.unselect(item);
28515 this.select(item, this.multiSelect && e.ctrlKey);
28516 this.lastSelection = item;
28521 onItemClick : function(item, index, e){
28522 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28528 unselect : function(nodeInfo, suppressEvent){
28529 var node = this.getNode(nodeInfo);
28530 if(node && this.isSelected(node)){
28531 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28532 Roo.fly(node).removeClass(this.selectedClass);
28533 this.selections.remove(node);
28534 if(!suppressEvent){
28535 this.fireEvent("selectionchange", this, this.selections);
28543 * Ext JS Library 1.1.1
28544 * Copyright(c) 2006-2007, Ext JS, LLC.
28546 * Originally Released Under LGPL - original licence link has changed is not relivant.
28549 * <script type="text/javascript">
28553 * @class Roo.LayoutManager
28554 * @extends Roo.util.Observable
28555 * Base class for layout managers.
28557 Roo.LayoutManager = function(container, config){
28558 Roo.LayoutManager.superclass.constructor.call(this);
28559 this.el = Roo.get(container);
28560 // ie scrollbar fix
28561 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28562 document.body.scroll = "no";
28563 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28564 this.el.position('relative');
28566 this.id = this.el.id;
28567 this.el.addClass("x-layout-container");
28568 /** false to disable window resize monitoring @type Boolean */
28569 this.monitorWindowResize = true;
28574 * Fires when a layout is performed.
28575 * @param {Roo.LayoutManager} this
28579 * @event regionresized
28580 * Fires when the user resizes a region.
28581 * @param {Roo.LayoutRegion} region The resized region
28582 * @param {Number} newSize The new size (width for east/west, height for north/south)
28584 "regionresized" : true,
28586 * @event regioncollapsed
28587 * Fires when a region is collapsed.
28588 * @param {Roo.LayoutRegion} region The collapsed region
28590 "regioncollapsed" : true,
28592 * @event regionexpanded
28593 * Fires when a region is expanded.
28594 * @param {Roo.LayoutRegion} region The expanded region
28596 "regionexpanded" : true
28598 this.updating = false;
28599 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28602 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28604 * Returns true if this layout is currently being updated
28605 * @return {Boolean}
28607 isUpdating : function(){
28608 return this.updating;
28612 * Suspend the LayoutManager from doing auto-layouts while
28613 * making multiple add or remove calls
28615 beginUpdate : function(){
28616 this.updating = true;
28620 * Restore auto-layouts and optionally disable the manager from performing a layout
28621 * @param {Boolean} noLayout true to disable a layout update
28623 endUpdate : function(noLayout){
28624 this.updating = false;
28630 layout: function(){
28634 onRegionResized : function(region, newSize){
28635 this.fireEvent("regionresized", region, newSize);
28639 onRegionCollapsed : function(region){
28640 this.fireEvent("regioncollapsed", region);
28643 onRegionExpanded : function(region){
28644 this.fireEvent("regionexpanded", region);
28648 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28649 * performs box-model adjustments.
28650 * @return {Object} The size as an object {width: (the width), height: (the height)}
28652 getViewSize : function(){
28654 if(this.el.dom != document.body){
28655 size = this.el.getSize();
28657 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28659 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28660 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28665 * Returns the Element this layout is bound to.
28666 * @return {Roo.Element}
28668 getEl : function(){
28673 * Returns the specified region.
28674 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28675 * @return {Roo.LayoutRegion}
28677 getRegion : function(target){
28678 return this.regions[target.toLowerCase()];
28681 onWindowResize : function(){
28682 if(this.monitorWindowResize){
28688 * Ext JS Library 1.1.1
28689 * Copyright(c) 2006-2007, Ext JS, LLC.
28691 * Originally Released Under LGPL - original licence link has changed is not relivant.
28694 * <script type="text/javascript">
28697 * @class Roo.BorderLayout
28698 * @extends Roo.LayoutManager
28699 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28700 * please see: <br><br>
28701 * <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>
28702 * <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>
28705 var layout = new Roo.BorderLayout(document.body, {
28739 preferredTabWidth: 150
28744 var CP = Roo.ContentPanel;
28746 layout.beginUpdate();
28747 layout.add("north", new CP("north", "North"));
28748 layout.add("south", new CP("south", {title: "South", closable: true}));
28749 layout.add("west", new CP("west", {title: "West"}));
28750 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28751 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28752 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28753 layout.getRegion("center").showPanel("center1");
28754 layout.endUpdate();
28757 <b>The container the layout is rendered into can be either the body element or any other element.
28758 If it is not the body element, the container needs to either be an absolute positioned element,
28759 or you will need to add "position:relative" to the css of the container. You will also need to specify
28760 the container size if it is not the body element.</b>
28763 * Create a new BorderLayout
28764 * @param {String/HTMLElement/Element} container The container this layout is bound to
28765 * @param {Object} config Configuration options
28767 Roo.BorderLayout = function(container, config){
28768 config = config || {};
28769 Roo.BorderLayout.superclass.constructor.call(this, container, config);
28770 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28771 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28772 var target = this.factory.validRegions[i];
28773 if(config[target]){
28774 this.addRegion(target, config[target]);
28779 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28781 * Creates and adds a new region if it doesn't already exist.
28782 * @param {String} target The target region key (north, south, east, west or center).
28783 * @param {Object} config The regions config object
28784 * @return {BorderLayoutRegion} The new region
28786 addRegion : function(target, config){
28787 if(!this.regions[target]){
28788 var r = this.factory.create(target, this, config);
28789 this.bindRegion(target, r);
28791 return this.regions[target];
28795 bindRegion : function(name, r){
28796 this.regions[name] = r;
28797 r.on("visibilitychange", this.layout, this);
28798 r.on("paneladded", this.layout, this);
28799 r.on("panelremoved", this.layout, this);
28800 r.on("invalidated", this.layout, this);
28801 r.on("resized", this.onRegionResized, this);
28802 r.on("collapsed", this.onRegionCollapsed, this);
28803 r.on("expanded", this.onRegionExpanded, this);
28807 * Performs a layout update.
28809 layout : function(){
28810 if(this.updating) {
28813 var size = this.getViewSize();
28814 var w = size.width;
28815 var h = size.height;
28820 //var x = 0, y = 0;
28822 var rs = this.regions;
28823 var north = rs["north"];
28824 var south = rs["south"];
28825 var west = rs["west"];
28826 var east = rs["east"];
28827 var center = rs["center"];
28828 //if(this.hideOnLayout){ // not supported anymore
28829 //c.el.setStyle("display", "none");
28831 if(north && north.isVisible()){
28832 var b = north.getBox();
28833 var m = north.getMargins();
28834 b.width = w - (m.left+m.right);
28837 centerY = b.height + b.y + m.bottom;
28838 centerH -= centerY;
28839 north.updateBox(this.safeBox(b));
28841 if(south && south.isVisible()){
28842 var b = south.getBox();
28843 var m = south.getMargins();
28844 b.width = w - (m.left+m.right);
28846 var totalHeight = (b.height + m.top + m.bottom);
28847 b.y = h - totalHeight + m.top;
28848 centerH -= totalHeight;
28849 south.updateBox(this.safeBox(b));
28851 if(west && west.isVisible()){
28852 var b = west.getBox();
28853 var m = west.getMargins();
28854 b.height = centerH - (m.top+m.bottom);
28856 b.y = centerY + m.top;
28857 var totalWidth = (b.width + m.left + m.right);
28858 centerX += totalWidth;
28859 centerW -= totalWidth;
28860 west.updateBox(this.safeBox(b));
28862 if(east && east.isVisible()){
28863 var b = east.getBox();
28864 var m = east.getMargins();
28865 b.height = centerH - (m.top+m.bottom);
28866 var totalWidth = (b.width + m.left + m.right);
28867 b.x = w - totalWidth + m.left;
28868 b.y = centerY + m.top;
28869 centerW -= totalWidth;
28870 east.updateBox(this.safeBox(b));
28873 var m = center.getMargins();
28875 x: centerX + m.left,
28876 y: centerY + m.top,
28877 width: centerW - (m.left+m.right),
28878 height: centerH - (m.top+m.bottom)
28880 //if(this.hideOnLayout){
28881 //center.el.setStyle("display", "block");
28883 center.updateBox(this.safeBox(centerBox));
28886 this.fireEvent("layout", this);
28890 safeBox : function(box){
28891 box.width = Math.max(0, box.width);
28892 box.height = Math.max(0, box.height);
28897 * Adds a ContentPanel (or subclass) to this layout.
28898 * @param {String} target The target region key (north, south, east, west or center).
28899 * @param {Roo.ContentPanel} panel The panel to add
28900 * @return {Roo.ContentPanel} The added panel
28902 add : function(target, panel){
28904 target = target.toLowerCase();
28905 return this.regions[target].add(panel);
28909 * Remove a ContentPanel (or subclass) to this layout.
28910 * @param {String} target The target region key (north, south, east, west or center).
28911 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28912 * @return {Roo.ContentPanel} The removed panel
28914 remove : function(target, panel){
28915 target = target.toLowerCase();
28916 return this.regions[target].remove(panel);
28920 * Searches all regions for a panel with the specified id
28921 * @param {String} panelId
28922 * @return {Roo.ContentPanel} The panel or null if it wasn't found
28924 findPanel : function(panelId){
28925 var rs = this.regions;
28926 for(var target in rs){
28927 if(typeof rs[target] != "function"){
28928 var p = rs[target].getPanel(panelId);
28938 * Searches all regions for a panel with the specified id and activates (shows) it.
28939 * @param {String/ContentPanel} panelId The panels id or the panel itself
28940 * @return {Roo.ContentPanel} The shown panel or null
28942 showPanel : function(panelId) {
28943 var rs = this.regions;
28944 for(var target in rs){
28945 var r = rs[target];
28946 if(typeof r != "function"){
28947 if(r.hasPanel(panelId)){
28948 return r.showPanel(panelId);
28956 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28957 * @param {Roo.state.Provider} provider (optional) An alternate state provider
28959 restoreState : function(provider){
28961 provider = Roo.state.Manager;
28963 var sm = new Roo.LayoutStateManager();
28964 sm.init(this, provider);
28968 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
28969 * object should contain properties for each region to add ContentPanels to, and each property's value should be
28970 * a valid ContentPanel config object. Example:
28972 // Create the main layout
28973 var layout = new Roo.BorderLayout('main-ct', {
28984 // Create and add multiple ContentPanels at once via configs
28987 id: 'source-files',
28989 title:'Ext Source Files',
29002 * @param {Object} regions An object containing ContentPanel configs by region name
29004 batchAdd : function(regions){
29005 this.beginUpdate();
29006 for(var rname in regions){
29007 var lr = this.regions[rname];
29009 this.addTypedPanels(lr, regions[rname]);
29016 addTypedPanels : function(lr, ps){
29017 if(typeof ps == 'string'){
29018 lr.add(new Roo.ContentPanel(ps));
29020 else if(ps instanceof Array){
29021 for(var i =0, len = ps.length; i < len; i++){
29022 this.addTypedPanels(lr, ps[i]);
29025 else if(!ps.events){ // raw config?
29027 delete ps.el; // prevent conflict
29028 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29030 else { // panel object assumed!
29035 * Adds a xtype elements to the layout.
29039 xtype : 'ContentPanel',
29046 xtype : 'NestedLayoutPanel',
29052 items : [ ... list of content panels or nested layout panels.. ]
29056 * @param {Object} cfg Xtype definition of item to add.
29058 addxtype : function(cfg)
29060 // basically accepts a pannel...
29061 // can accept a layout region..!?!?
29062 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29064 if (!cfg.xtype.match(/Panel$/)) {
29069 if (typeof(cfg.region) == 'undefined') {
29070 Roo.log("Failed to add Panel, region was not set");
29074 var region = cfg.region;
29080 xitems = cfg.items;
29087 case 'ContentPanel': // ContentPanel (el, cfg)
29088 case 'ScrollPanel': // ContentPanel (el, cfg)
29090 if(cfg.autoCreate) {
29091 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29093 var el = this.el.createChild();
29094 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29097 this.add(region, ret);
29101 case 'TreePanel': // our new panel!
29102 cfg.el = this.el.createChild();
29103 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29104 this.add(region, ret);
29107 case 'NestedLayoutPanel':
29108 // create a new Layout (which is a Border Layout...
29109 var el = this.el.createChild();
29110 var clayout = cfg.layout;
29112 clayout.items = clayout.items || [];
29113 // replace this exitems with the clayout ones..
29114 xitems = clayout.items;
29117 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29118 cfg.background = false;
29120 var layout = new Roo.BorderLayout(el, clayout);
29122 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29123 //console.log('adding nested layout panel ' + cfg.toSource());
29124 this.add(region, ret);
29125 nb = {}; /// find first...
29130 // needs grid and region
29132 //var el = this.getRegion(region).el.createChild();
29133 var el = this.el.createChild();
29134 // create the grid first...
29136 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29138 if (region == 'center' && this.active ) {
29139 cfg.background = false;
29141 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29143 this.add(region, ret);
29144 if (cfg.background) {
29145 ret.on('activate', function(gp) {
29146 if (!gp.grid.rendered) {
29161 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29163 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29164 this.add(region, ret);
29167 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29171 // GridPanel (grid, cfg)
29174 this.beginUpdate();
29178 Roo.each(xitems, function(i) {
29179 region = nb && i.region ? i.region : false;
29181 var add = ret.addxtype(i);
29184 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29185 if (!i.background) {
29186 abn[region] = nb[region] ;
29193 // make the last non-background panel active..
29194 //if (nb) { Roo.log(abn); }
29197 for(var r in abn) {
29198 region = this.getRegion(r);
29200 // tried using nb[r], but it does not work..
29202 region.showPanel(abn[r]);
29213 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29214 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
29215 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29216 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
29219 var CP = Roo.ContentPanel;
29221 var layout = Roo.BorderLayout.create({
29225 panels: [new CP("north", "North")]
29234 panels: [new CP("west", {title: "West"})]
29243 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29252 panels: [new CP("south", {title: "South", closable: true})]
29259 preferredTabWidth: 150,
29261 new CP("center1", {title: "Close Me", closable: true}),
29262 new CP("center2", {title: "Center Panel", closable: false})
29267 layout.getRegion("center").showPanel("center1");
29272 Roo.BorderLayout.create = function(config, targetEl){
29273 var layout = new Roo.BorderLayout(targetEl || document.body, config);
29274 layout.beginUpdate();
29275 var regions = Roo.BorderLayout.RegionFactory.validRegions;
29276 for(var j = 0, jlen = regions.length; j < jlen; j++){
29277 var lr = regions[j];
29278 if(layout.regions[lr] && config[lr].panels){
29279 var r = layout.regions[lr];
29280 var ps = config[lr].panels;
29281 layout.addTypedPanels(r, ps);
29284 layout.endUpdate();
29289 Roo.BorderLayout.RegionFactory = {
29291 validRegions : ["north","south","east","west","center"],
29294 create : function(target, mgr, config){
29295 target = target.toLowerCase();
29296 if(config.lightweight || config.basic){
29297 return new Roo.BasicLayoutRegion(mgr, config, target);
29301 return new Roo.NorthLayoutRegion(mgr, config);
29303 return new Roo.SouthLayoutRegion(mgr, config);
29305 return new Roo.EastLayoutRegion(mgr, config);
29307 return new Roo.WestLayoutRegion(mgr, config);
29309 return new Roo.CenterLayoutRegion(mgr, config);
29311 throw 'Layout region "'+target+'" not supported.';
29315 * Ext JS Library 1.1.1
29316 * Copyright(c) 2006-2007, Ext JS, LLC.
29318 * Originally Released Under LGPL - original licence link has changed is not relivant.
29321 * <script type="text/javascript">
29325 * @class Roo.BasicLayoutRegion
29326 * @extends Roo.util.Observable
29327 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29328 * and does not have a titlebar, tabs or any other features. All it does is size and position
29329 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29331 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29333 this.position = pos;
29336 * @scope Roo.BasicLayoutRegion
29340 * @event beforeremove
29341 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29342 * @param {Roo.LayoutRegion} this
29343 * @param {Roo.ContentPanel} panel The panel
29344 * @param {Object} e The cancel event object
29346 "beforeremove" : true,
29348 * @event invalidated
29349 * Fires when the layout for this region is changed.
29350 * @param {Roo.LayoutRegion} this
29352 "invalidated" : true,
29354 * @event visibilitychange
29355 * Fires when this region is shown or hidden
29356 * @param {Roo.LayoutRegion} this
29357 * @param {Boolean} visibility true or false
29359 "visibilitychange" : true,
29361 * @event paneladded
29362 * Fires when a panel is added.
29363 * @param {Roo.LayoutRegion} this
29364 * @param {Roo.ContentPanel} panel The panel
29366 "paneladded" : true,
29368 * @event panelremoved
29369 * Fires when a panel is removed.
29370 * @param {Roo.LayoutRegion} this
29371 * @param {Roo.ContentPanel} panel The panel
29373 "panelremoved" : true,
29375 * @event beforecollapse
29376 * Fires when this region before collapse.
29377 * @param {Roo.LayoutRegion} this
29379 "beforecollapse" : true,
29382 * Fires when this region is collapsed.
29383 * @param {Roo.LayoutRegion} this
29385 "collapsed" : true,
29388 * Fires when this region is expanded.
29389 * @param {Roo.LayoutRegion} this
29394 * Fires when this region is slid into view.
29395 * @param {Roo.LayoutRegion} this
29397 "slideshow" : true,
29400 * Fires when this region slides out of view.
29401 * @param {Roo.LayoutRegion} this
29403 "slidehide" : true,
29405 * @event panelactivated
29406 * Fires when a panel is activated.
29407 * @param {Roo.LayoutRegion} this
29408 * @param {Roo.ContentPanel} panel The activated panel
29410 "panelactivated" : true,
29413 * Fires when the user resizes this region.
29414 * @param {Roo.LayoutRegion} this
29415 * @param {Number} newSize The new size (width for east/west, height for north/south)
29419 /** A collection of panels in this region. @type Roo.util.MixedCollection */
29420 this.panels = new Roo.util.MixedCollection();
29421 this.panels.getKey = this.getPanelId.createDelegate(this);
29423 this.activePanel = null;
29424 // ensure listeners are added...
29426 if (config.listeners || config.events) {
29427 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29428 listeners : config.listeners || {},
29429 events : config.events || {}
29433 if(skipConfig !== true){
29434 this.applyConfig(config);
29438 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29439 getPanelId : function(p){
29443 applyConfig : function(config){
29444 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29445 this.config = config;
29450 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
29451 * the width, for horizontal (north, south) the height.
29452 * @param {Number} newSize The new width or height
29454 resizeTo : function(newSize){
29455 var el = this.el ? this.el :
29456 (this.activePanel ? this.activePanel.getEl() : null);
29458 switch(this.position){
29461 el.setWidth(newSize);
29462 this.fireEvent("resized", this, newSize);
29466 el.setHeight(newSize);
29467 this.fireEvent("resized", this, newSize);
29473 getBox : function(){
29474 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29477 getMargins : function(){
29478 return this.margins;
29481 updateBox : function(box){
29483 var el = this.activePanel.getEl();
29484 el.dom.style.left = box.x + "px";
29485 el.dom.style.top = box.y + "px";
29486 this.activePanel.setSize(box.width, box.height);
29490 * Returns the container element for this region.
29491 * @return {Roo.Element}
29493 getEl : function(){
29494 return this.activePanel;
29498 * Returns true if this region is currently visible.
29499 * @return {Boolean}
29501 isVisible : function(){
29502 return this.activePanel ? true : false;
29505 setActivePanel : function(panel){
29506 panel = this.getPanel(panel);
29507 if(this.activePanel && this.activePanel != panel){
29508 this.activePanel.setActiveState(false);
29509 this.activePanel.getEl().setLeftTop(-10000,-10000);
29511 this.activePanel = panel;
29512 panel.setActiveState(true);
29514 panel.setSize(this.box.width, this.box.height);
29516 this.fireEvent("panelactivated", this, panel);
29517 this.fireEvent("invalidated");
29521 * Show the specified panel.
29522 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29523 * @return {Roo.ContentPanel} The shown panel or null
29525 showPanel : function(panel){
29526 if(panel = this.getPanel(panel)){
29527 this.setActivePanel(panel);
29533 * Get the active panel for this region.
29534 * @return {Roo.ContentPanel} The active panel or null
29536 getActivePanel : function(){
29537 return this.activePanel;
29541 * Add the passed ContentPanel(s)
29542 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29543 * @return {Roo.ContentPanel} The panel added (if only one was added)
29545 add : function(panel){
29546 if(arguments.length > 1){
29547 for(var i = 0, len = arguments.length; i < len; i++) {
29548 this.add(arguments[i]);
29552 if(this.hasPanel(panel)){
29553 this.showPanel(panel);
29556 var el = panel.getEl();
29557 if(el.dom.parentNode != this.mgr.el.dom){
29558 this.mgr.el.dom.appendChild(el.dom);
29560 if(panel.setRegion){
29561 panel.setRegion(this);
29563 this.panels.add(panel);
29564 el.setStyle("position", "absolute");
29565 if(!panel.background){
29566 this.setActivePanel(panel);
29567 if(this.config.initialSize && this.panels.getCount()==1){
29568 this.resizeTo(this.config.initialSize);
29571 this.fireEvent("paneladded", this, panel);
29576 * Returns true if the panel is in this region.
29577 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29578 * @return {Boolean}
29580 hasPanel : function(panel){
29581 if(typeof panel == "object"){ // must be panel obj
29582 panel = panel.getId();
29584 return this.getPanel(panel) ? true : false;
29588 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29589 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29590 * @param {Boolean} preservePanel Overrides the config preservePanel option
29591 * @return {Roo.ContentPanel} The panel that was removed
29593 remove : function(panel, preservePanel){
29594 panel = this.getPanel(panel);
29599 this.fireEvent("beforeremove", this, panel, e);
29600 if(e.cancel === true){
29603 var panelId = panel.getId();
29604 this.panels.removeKey(panelId);
29609 * Returns the panel specified or null if it's not in this region.
29610 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29611 * @return {Roo.ContentPanel}
29613 getPanel : function(id){
29614 if(typeof id == "object"){ // must be panel obj
29617 return this.panels.get(id);
29621 * Returns this regions position (north/south/east/west/center).
29624 getPosition: function(){
29625 return this.position;
29629 * Ext JS Library 1.1.1
29630 * Copyright(c) 2006-2007, Ext JS, LLC.
29632 * Originally Released Under LGPL - original licence link has changed is not relivant.
29635 * <script type="text/javascript">
29639 * @class Roo.LayoutRegion
29640 * @extends Roo.BasicLayoutRegion
29641 * This class represents a region in a layout manager.
29642 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
29643 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
29644 * @cfg {Boolean} floatable False to disable floating (defaults to true)
29645 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29646 * @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})
29647 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
29648 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
29649 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
29650 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
29651 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
29652 * @cfg {String} title The title for the region (overrides panel titles)
29653 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
29654 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29655 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
29656 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29657 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
29658 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29659 * the space available, similar to FireFox 1.5 tabs (defaults to false)
29660 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
29661 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
29662 * @cfg {Boolean} showPin True to show a pin button
29663 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
29664 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
29665 * @cfg {Boolean} disableTabTips True to disable tab tooltips
29666 * @cfg {Number} width For East/West panels
29667 * @cfg {Number} height For North/South panels
29668 * @cfg {Boolean} split To show the splitter
29669 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
29671 Roo.LayoutRegion = function(mgr, config, pos){
29672 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29673 var dh = Roo.DomHelper;
29674 /** This region's container element
29675 * @type Roo.Element */
29676 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29677 /** This region's title element
29678 * @type Roo.Element */
29680 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29681 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
29682 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29684 this.titleEl.enableDisplayMode();
29685 /** This region's title text element
29686 * @type HTMLElement */
29687 this.titleTextEl = this.titleEl.dom.firstChild;
29688 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29689 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29690 this.closeBtn.enableDisplayMode();
29691 this.closeBtn.on("click", this.closeClicked, this);
29692 this.closeBtn.hide();
29694 this.createBody(config);
29695 this.visible = true;
29696 this.collapsed = false;
29698 if(config.hideWhenEmpty){
29700 this.on("paneladded", this.validateVisibility, this);
29701 this.on("panelremoved", this.validateVisibility, this);
29703 this.applyConfig(config);
29706 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29708 createBody : function(){
29709 /** This region's body element
29710 * @type Roo.Element */
29711 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29714 applyConfig : function(c){
29715 if(c.collapsible && this.position != "center" && !this.collapsedEl){
29716 var dh = Roo.DomHelper;
29717 if(c.titlebar !== false){
29718 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29719 this.collapseBtn.on("click", this.collapse, this);
29720 this.collapseBtn.enableDisplayMode();
29722 if(c.showPin === true || this.showPin){
29723 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29724 this.stickBtn.enableDisplayMode();
29725 this.stickBtn.on("click", this.expand, this);
29726 this.stickBtn.hide();
29729 /** This region's collapsed element
29730 * @type Roo.Element */
29731 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29732 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29734 if(c.floatable !== false){
29735 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29736 this.collapsedEl.on("click", this.collapseClick, this);
29739 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29740 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29741 id: "message", unselectable: "on", style:{"float":"left"}});
29742 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29744 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29745 this.expandBtn.on("click", this.expand, this);
29747 if(this.collapseBtn){
29748 this.collapseBtn.setVisible(c.collapsible == true);
29750 this.cmargins = c.cmargins || this.cmargins ||
29751 (this.position == "west" || this.position == "east" ?
29752 {top: 0, left: 2, right:2, bottom: 0} :
29753 {top: 2, left: 0, right:0, bottom: 2});
29754 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29755 this.bottomTabs = c.tabPosition != "top";
29756 this.autoScroll = c.autoScroll || false;
29757 if(this.autoScroll){
29758 this.bodyEl.setStyle("overflow", "auto");
29760 this.bodyEl.setStyle("overflow", "hidden");
29762 //if(c.titlebar !== false){
29763 if((!c.titlebar && !c.title) || c.titlebar === false){
29764 this.titleEl.hide();
29766 this.titleEl.show();
29768 this.titleTextEl.innerHTML = c.title;
29772 this.duration = c.duration || .30;
29773 this.slideDuration = c.slideDuration || .45;
29776 this.collapse(true);
29783 * Returns true if this region is currently visible.
29784 * @return {Boolean}
29786 isVisible : function(){
29787 return this.visible;
29791 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29792 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
29794 setCollapsedTitle : function(title){
29795 title = title || " ";
29796 if(this.collapsedTitleTextEl){
29797 this.collapsedTitleTextEl.innerHTML = title;
29801 getBox : function(){
29803 if(!this.collapsed){
29804 b = this.el.getBox(false, true);
29806 b = this.collapsedEl.getBox(false, true);
29811 getMargins : function(){
29812 return this.collapsed ? this.cmargins : this.margins;
29815 highlight : function(){
29816 this.el.addClass("x-layout-panel-dragover");
29819 unhighlight : function(){
29820 this.el.removeClass("x-layout-panel-dragover");
29823 updateBox : function(box){
29825 if(!this.collapsed){
29826 this.el.dom.style.left = box.x + "px";
29827 this.el.dom.style.top = box.y + "px";
29828 this.updateBody(box.width, box.height);
29830 this.collapsedEl.dom.style.left = box.x + "px";
29831 this.collapsedEl.dom.style.top = box.y + "px";
29832 this.collapsedEl.setSize(box.width, box.height);
29835 this.tabs.autoSizeTabs();
29839 updateBody : function(w, h){
29841 this.el.setWidth(w);
29842 w -= this.el.getBorderWidth("rl");
29843 if(this.config.adjustments){
29844 w += this.config.adjustments[0];
29848 this.el.setHeight(h);
29849 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29850 h -= this.el.getBorderWidth("tb");
29851 if(this.config.adjustments){
29852 h += this.config.adjustments[1];
29854 this.bodyEl.setHeight(h);
29856 h = this.tabs.syncHeight(h);
29859 if(this.panelSize){
29860 w = w !== null ? w : this.panelSize.width;
29861 h = h !== null ? h : this.panelSize.height;
29863 if(this.activePanel){
29864 var el = this.activePanel.getEl();
29865 w = w !== null ? w : el.getWidth();
29866 h = h !== null ? h : el.getHeight();
29867 this.panelSize = {width: w, height: h};
29868 this.activePanel.setSize(w, h);
29870 if(Roo.isIE && this.tabs){
29871 this.tabs.el.repaint();
29876 * Returns the container element for this region.
29877 * @return {Roo.Element}
29879 getEl : function(){
29884 * Hides this region.
29887 if(!this.collapsed){
29888 this.el.dom.style.left = "-2000px";
29891 this.collapsedEl.dom.style.left = "-2000px";
29892 this.collapsedEl.hide();
29894 this.visible = false;
29895 this.fireEvent("visibilitychange", this, false);
29899 * Shows this region if it was previously hidden.
29902 if(!this.collapsed){
29905 this.collapsedEl.show();
29907 this.visible = true;
29908 this.fireEvent("visibilitychange", this, true);
29911 closeClicked : function(){
29912 if(this.activePanel){
29913 this.remove(this.activePanel);
29917 collapseClick : function(e){
29919 e.stopPropagation();
29922 e.stopPropagation();
29928 * Collapses this region.
29929 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29931 collapse : function(skipAnim, skipCheck = false){
29932 if(this.collapsed) {
29936 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
29938 this.collapsed = true;
29940 this.split.el.hide();
29942 if(this.config.animate && skipAnim !== true){
29943 this.fireEvent("invalidated", this);
29944 this.animateCollapse();
29946 this.el.setLocation(-20000,-20000);
29948 this.collapsedEl.show();
29949 this.fireEvent("collapsed", this);
29950 this.fireEvent("invalidated", this);
29956 animateCollapse : function(){
29961 * Expands this region if it was previously collapsed.
29962 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29963 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29965 expand : function(e, skipAnim){
29967 e.stopPropagation();
29969 if(!this.collapsed || this.el.hasActiveFx()) {
29973 this.afterSlideIn();
29976 this.collapsed = false;
29977 if(this.config.animate && skipAnim !== true){
29978 this.animateExpand();
29982 this.split.el.show();
29984 this.collapsedEl.setLocation(-2000,-2000);
29985 this.collapsedEl.hide();
29986 this.fireEvent("invalidated", this);
29987 this.fireEvent("expanded", this);
29991 animateExpand : function(){
29995 initTabs : function()
29997 this.bodyEl.setStyle("overflow", "hidden");
29998 var ts = new Roo.TabPanel(
30001 tabPosition: this.bottomTabs ? 'bottom' : 'top',
30002 disableTooltips: this.config.disableTabTips,
30003 toolbar : this.config.toolbar
30006 if(this.config.hideTabs){
30007 ts.stripWrap.setDisplayed(false);
30010 ts.resizeTabs = this.config.resizeTabs === true;
30011 ts.minTabWidth = this.config.minTabWidth || 40;
30012 ts.maxTabWidth = this.config.maxTabWidth || 250;
30013 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30014 ts.monitorResize = false;
30015 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30016 ts.bodyEl.addClass('x-layout-tabs-body');
30017 this.panels.each(this.initPanelAsTab, this);
30020 initPanelAsTab : function(panel){
30021 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30022 this.config.closeOnTab && panel.isClosable());
30023 if(panel.tabTip !== undefined){
30024 ti.setTooltip(panel.tabTip);
30026 ti.on("activate", function(){
30027 this.setActivePanel(panel);
30029 if(this.config.closeOnTab){
30030 ti.on("beforeclose", function(t, e){
30032 this.remove(panel);
30038 updatePanelTitle : function(panel, title){
30039 if(this.activePanel == panel){
30040 this.updateTitle(title);
30043 var ti = this.tabs.getTab(panel.getEl().id);
30045 if(panel.tabTip !== undefined){
30046 ti.setTooltip(panel.tabTip);
30051 updateTitle : function(title){
30052 if(this.titleTextEl && !this.config.title){
30053 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
30057 setActivePanel : function(panel){
30058 panel = this.getPanel(panel);
30059 if(this.activePanel && this.activePanel != panel){
30060 this.activePanel.setActiveState(false);
30062 this.activePanel = panel;
30063 panel.setActiveState(true);
30064 if(this.panelSize){
30065 panel.setSize(this.panelSize.width, this.panelSize.height);
30068 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30070 this.updateTitle(panel.getTitle());
30072 this.fireEvent("invalidated", this);
30074 this.fireEvent("panelactivated", this, panel);
30078 * Shows the specified panel.
30079 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30080 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30082 showPanel : function(panel)
30084 panel = this.getPanel(panel);
30087 var tab = this.tabs.getTab(panel.getEl().id);
30088 if(tab.isHidden()){
30089 this.tabs.unhideTab(tab.id);
30093 this.setActivePanel(panel);
30100 * Get the active panel for this region.
30101 * @return {Roo.ContentPanel} The active panel or null
30103 getActivePanel : function(){
30104 return this.activePanel;
30107 validateVisibility : function(){
30108 if(this.panels.getCount() < 1){
30109 this.updateTitle(" ");
30110 this.closeBtn.hide();
30113 if(!this.isVisible()){
30120 * Adds the passed ContentPanel(s) to this region.
30121 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30122 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30124 add : function(panel){
30125 if(arguments.length > 1){
30126 for(var i = 0, len = arguments.length; i < len; i++) {
30127 this.add(arguments[i]);
30131 if(this.hasPanel(panel)){
30132 this.showPanel(panel);
30135 panel.setRegion(this);
30136 this.panels.add(panel);
30137 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30138 this.bodyEl.dom.appendChild(panel.getEl().dom);
30139 if(panel.background !== true){
30140 this.setActivePanel(panel);
30142 this.fireEvent("paneladded", this, panel);
30148 this.initPanelAsTab(panel);
30150 if(panel.background !== true){
30151 this.tabs.activate(panel.getEl().id);
30153 this.fireEvent("paneladded", this, panel);
30158 * Hides the tab for the specified panel.
30159 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30161 hidePanel : function(panel){
30162 if(this.tabs && (panel = this.getPanel(panel))){
30163 this.tabs.hideTab(panel.getEl().id);
30168 * Unhides the tab for a previously hidden panel.
30169 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30171 unhidePanel : function(panel){
30172 if(this.tabs && (panel = this.getPanel(panel))){
30173 this.tabs.unhideTab(panel.getEl().id);
30177 clearPanels : function(){
30178 while(this.panels.getCount() > 0){
30179 this.remove(this.panels.first());
30184 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30185 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30186 * @param {Boolean} preservePanel Overrides the config preservePanel option
30187 * @return {Roo.ContentPanel} The panel that was removed
30189 remove : function(panel, preservePanel){
30190 panel = this.getPanel(panel);
30195 this.fireEvent("beforeremove", this, panel, e);
30196 if(e.cancel === true){
30199 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30200 var panelId = panel.getId();
30201 this.panels.removeKey(panelId);
30203 document.body.appendChild(panel.getEl().dom);
30206 this.tabs.removeTab(panel.getEl().id);
30207 }else if (!preservePanel){
30208 this.bodyEl.dom.removeChild(panel.getEl().dom);
30210 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30211 var p = this.panels.first();
30212 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30213 tempEl.appendChild(p.getEl().dom);
30214 this.bodyEl.update("");
30215 this.bodyEl.dom.appendChild(p.getEl().dom);
30217 this.updateTitle(p.getTitle());
30219 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30220 this.setActivePanel(p);
30222 panel.setRegion(null);
30223 if(this.activePanel == panel){
30224 this.activePanel = null;
30226 if(this.config.autoDestroy !== false && preservePanel !== true){
30227 try{panel.destroy();}catch(e){}
30229 this.fireEvent("panelremoved", this, panel);
30234 * Returns the TabPanel component used by this region
30235 * @return {Roo.TabPanel}
30237 getTabs : function(){
30241 createTool : function(parentEl, className){
30242 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30243 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
30244 btn.addClassOnOver("x-layout-tools-button-over");
30249 * Ext JS Library 1.1.1
30250 * Copyright(c) 2006-2007, Ext JS, LLC.
30252 * Originally Released Under LGPL - original licence link has changed is not relivant.
30255 * <script type="text/javascript">
30261 * @class Roo.SplitLayoutRegion
30262 * @extends Roo.LayoutRegion
30263 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30265 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30266 this.cursor = cursor;
30267 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30270 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30271 splitTip : "Drag to resize.",
30272 collapsibleSplitTip : "Drag to resize. Double click to hide.",
30273 useSplitTips : false,
30275 applyConfig : function(config){
30276 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30279 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
30280 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
30281 /** The SplitBar for this region
30282 * @type Roo.SplitBar */
30283 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30284 this.split.on("moved", this.onSplitMove, this);
30285 this.split.useShim = config.useShim === true;
30286 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30287 if(this.useSplitTips){
30288 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30290 if(config.collapsible){
30291 this.split.el.on("dblclick", this.collapse, this);
30294 if(typeof config.minSize != "undefined"){
30295 this.split.minSize = config.minSize;
30297 if(typeof config.maxSize != "undefined"){
30298 this.split.maxSize = config.maxSize;
30300 if(config.hideWhenEmpty || config.hidden || config.collapsed){
30301 this.hideSplitter();
30306 getHMaxSize : function(){
30307 var cmax = this.config.maxSize || 10000;
30308 var center = this.mgr.getRegion("center");
30309 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30312 getVMaxSize : function(){
30313 var cmax = this.config.maxSize || 10000;
30314 var center = this.mgr.getRegion("center");
30315 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30318 onSplitMove : function(split, newSize){
30319 this.fireEvent("resized", this, newSize);
30323 * Returns the {@link Roo.SplitBar} for this region.
30324 * @return {Roo.SplitBar}
30326 getSplitBar : function(){
30331 this.hideSplitter();
30332 Roo.SplitLayoutRegion.superclass.hide.call(this);
30335 hideSplitter : function(){
30337 this.split.el.setLocation(-2000,-2000);
30338 this.split.el.hide();
30344 this.split.el.show();
30346 Roo.SplitLayoutRegion.superclass.show.call(this);
30349 beforeSlide: function(){
30350 if(Roo.isGecko){// firefox overflow auto bug workaround
30351 this.bodyEl.clip();
30353 this.tabs.bodyEl.clip();
30355 if(this.activePanel){
30356 this.activePanel.getEl().clip();
30358 if(this.activePanel.beforeSlide){
30359 this.activePanel.beforeSlide();
30365 afterSlide : function(){
30366 if(Roo.isGecko){// firefox overflow auto bug workaround
30367 this.bodyEl.unclip();
30369 this.tabs.bodyEl.unclip();
30371 if(this.activePanel){
30372 this.activePanel.getEl().unclip();
30373 if(this.activePanel.afterSlide){
30374 this.activePanel.afterSlide();
30380 initAutoHide : function(){
30381 if(this.autoHide !== false){
30382 if(!this.autoHideHd){
30383 var st = new Roo.util.DelayedTask(this.slideIn, this);
30384 this.autoHideHd = {
30385 "mouseout": function(e){
30386 if(!e.within(this.el, true)){
30390 "mouseover" : function(e){
30396 this.el.on(this.autoHideHd);
30400 clearAutoHide : function(){
30401 if(this.autoHide !== false){
30402 this.el.un("mouseout", this.autoHideHd.mouseout);
30403 this.el.un("mouseover", this.autoHideHd.mouseover);
30407 clearMonitor : function(){
30408 Roo.get(document).un("click", this.slideInIf, this);
30411 // these names are backwards but not changed for compat
30412 slideOut : function(){
30413 if(this.isSlid || this.el.hasActiveFx()){
30416 this.isSlid = true;
30417 if(this.collapseBtn){
30418 this.collapseBtn.hide();
30420 this.closeBtnState = this.closeBtn.getStyle('display');
30421 this.closeBtn.hide();
30423 this.stickBtn.show();
30426 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30427 this.beforeSlide();
30428 this.el.setStyle("z-index", 10001);
30429 this.el.slideIn(this.getSlideAnchor(), {
30430 callback: function(){
30432 this.initAutoHide();
30433 Roo.get(document).on("click", this.slideInIf, this);
30434 this.fireEvent("slideshow", this);
30441 afterSlideIn : function(){
30442 this.clearAutoHide();
30443 this.isSlid = false;
30444 this.clearMonitor();
30445 this.el.setStyle("z-index", "");
30446 if(this.collapseBtn){
30447 this.collapseBtn.show();
30449 this.closeBtn.setStyle('display', this.closeBtnState);
30451 this.stickBtn.hide();
30453 this.fireEvent("slidehide", this);
30456 slideIn : function(cb){
30457 if(!this.isSlid || this.el.hasActiveFx()){
30461 this.isSlid = false;
30462 this.beforeSlide();
30463 this.el.slideOut(this.getSlideAnchor(), {
30464 callback: function(){
30465 this.el.setLeftTop(-10000, -10000);
30467 this.afterSlideIn();
30475 slideInIf : function(e){
30476 if(!e.within(this.el)){
30481 animateCollapse : function(){
30482 this.beforeSlide();
30483 this.el.setStyle("z-index", 20000);
30484 var anchor = this.getSlideAnchor();
30485 this.el.slideOut(anchor, {
30486 callback : function(){
30487 this.el.setStyle("z-index", "");
30488 this.collapsedEl.slideIn(anchor, {duration:.3});
30490 this.el.setLocation(-10000,-10000);
30492 this.fireEvent("collapsed", this);
30499 animateExpand : function(){
30500 this.beforeSlide();
30501 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30502 this.el.setStyle("z-index", 20000);
30503 this.collapsedEl.hide({
30506 this.el.slideIn(this.getSlideAnchor(), {
30507 callback : function(){
30508 this.el.setStyle("z-index", "");
30511 this.split.el.show();
30513 this.fireEvent("invalidated", this);
30514 this.fireEvent("expanded", this);
30542 getAnchor : function(){
30543 return this.anchors[this.position];
30546 getCollapseAnchor : function(){
30547 return this.canchors[this.position];
30550 getSlideAnchor : function(){
30551 return this.sanchors[this.position];
30554 getAlignAdj : function(){
30555 var cm = this.cmargins;
30556 switch(this.position){
30572 getExpandAdj : function(){
30573 var c = this.collapsedEl, cm = this.cmargins;
30574 switch(this.position){
30576 return [-(cm.right+c.getWidth()+cm.left), 0];
30579 return [cm.right+c.getWidth()+cm.left, 0];
30582 return [0, -(cm.top+cm.bottom+c.getHeight())];
30585 return [0, cm.top+cm.bottom+c.getHeight()];
30591 * Ext JS Library 1.1.1
30592 * Copyright(c) 2006-2007, Ext JS, LLC.
30594 * Originally Released Under LGPL - original licence link has changed is not relivant.
30597 * <script type="text/javascript">
30600 * These classes are private internal classes
30602 Roo.CenterLayoutRegion = function(mgr, config){
30603 Roo.LayoutRegion.call(this, mgr, config, "center");
30604 this.visible = true;
30605 this.minWidth = config.minWidth || 20;
30606 this.minHeight = config.minHeight || 20;
30609 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30611 // center panel can't be hidden
30615 // center panel can't be hidden
30618 getMinWidth: function(){
30619 return this.minWidth;
30622 getMinHeight: function(){
30623 return this.minHeight;
30628 Roo.NorthLayoutRegion = function(mgr, config){
30629 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30631 this.split.placement = Roo.SplitBar.TOP;
30632 this.split.orientation = Roo.SplitBar.VERTICAL;
30633 this.split.el.addClass("x-layout-split-v");
30635 var size = config.initialSize || config.height;
30636 if(typeof size != "undefined"){
30637 this.el.setHeight(size);
30640 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30641 orientation: Roo.SplitBar.VERTICAL,
30642 getBox : function(){
30643 if(this.collapsed){
30644 return this.collapsedEl.getBox();
30646 var box = this.el.getBox();
30648 box.height += this.split.el.getHeight();
30653 updateBox : function(box){
30654 if(this.split && !this.collapsed){
30655 box.height -= this.split.el.getHeight();
30656 this.split.el.setLeft(box.x);
30657 this.split.el.setTop(box.y+box.height);
30658 this.split.el.setWidth(box.width);
30660 if(this.collapsed){
30661 this.updateBody(box.width, null);
30663 Roo.LayoutRegion.prototype.updateBox.call(this, box);
30667 Roo.SouthLayoutRegion = function(mgr, config){
30668 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30670 this.split.placement = Roo.SplitBar.BOTTOM;
30671 this.split.orientation = Roo.SplitBar.VERTICAL;
30672 this.split.el.addClass("x-layout-split-v");
30674 var size = config.initialSize || config.height;
30675 if(typeof size != "undefined"){
30676 this.el.setHeight(size);
30679 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30680 orientation: Roo.SplitBar.VERTICAL,
30681 getBox : function(){
30682 if(this.collapsed){
30683 return this.collapsedEl.getBox();
30685 var box = this.el.getBox();
30687 var sh = this.split.el.getHeight();
30694 updateBox : function(box){
30695 if(this.split && !this.collapsed){
30696 var sh = this.split.el.getHeight();
30699 this.split.el.setLeft(box.x);
30700 this.split.el.setTop(box.y-sh);
30701 this.split.el.setWidth(box.width);
30703 if(this.collapsed){
30704 this.updateBody(box.width, null);
30706 Roo.LayoutRegion.prototype.updateBox.call(this, box);
30710 Roo.EastLayoutRegion = function(mgr, config){
30711 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30713 this.split.placement = Roo.SplitBar.RIGHT;
30714 this.split.orientation = Roo.SplitBar.HORIZONTAL;
30715 this.split.el.addClass("x-layout-split-h");
30717 var size = config.initialSize || config.width;
30718 if(typeof size != "undefined"){
30719 this.el.setWidth(size);
30722 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30723 orientation: Roo.SplitBar.HORIZONTAL,
30724 getBox : function(){
30725 if(this.collapsed){
30726 return this.collapsedEl.getBox();
30728 var box = this.el.getBox();
30730 var sw = this.split.el.getWidth();
30737 updateBox : function(box){
30738 if(this.split && !this.collapsed){
30739 var sw = this.split.el.getWidth();
30741 this.split.el.setLeft(box.x);
30742 this.split.el.setTop(box.y);
30743 this.split.el.setHeight(box.height);
30746 if(this.collapsed){
30747 this.updateBody(null, box.height);
30749 Roo.LayoutRegion.prototype.updateBox.call(this, box);
30753 Roo.WestLayoutRegion = function(mgr, config){
30754 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30756 this.split.placement = Roo.SplitBar.LEFT;
30757 this.split.orientation = Roo.SplitBar.HORIZONTAL;
30758 this.split.el.addClass("x-layout-split-h");
30760 var size = config.initialSize || config.width;
30761 if(typeof size != "undefined"){
30762 this.el.setWidth(size);
30765 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30766 orientation: Roo.SplitBar.HORIZONTAL,
30767 getBox : function(){
30768 if(this.collapsed){
30769 return this.collapsedEl.getBox();
30771 var box = this.el.getBox();
30773 box.width += this.split.el.getWidth();
30778 updateBox : function(box){
30779 if(this.split && !this.collapsed){
30780 var sw = this.split.el.getWidth();
30782 this.split.el.setLeft(box.x+box.width);
30783 this.split.el.setTop(box.y);
30784 this.split.el.setHeight(box.height);
30786 if(this.collapsed){
30787 this.updateBody(null, box.height);
30789 Roo.LayoutRegion.prototype.updateBox.call(this, box);
30794 * Ext JS Library 1.1.1
30795 * Copyright(c) 2006-2007, Ext JS, LLC.
30797 * Originally Released Under LGPL - original licence link has changed is not relivant.
30800 * <script type="text/javascript">
30805 * Private internal class for reading and applying state
30807 Roo.LayoutStateManager = function(layout){
30808 // default empty state
30817 Roo.LayoutStateManager.prototype = {
30818 init : function(layout, provider){
30819 this.provider = provider;
30820 var state = provider.get(layout.id+"-layout-state");
30822 var wasUpdating = layout.isUpdating();
30824 layout.beginUpdate();
30826 for(var key in state){
30827 if(typeof state[key] != "function"){
30828 var rstate = state[key];
30829 var r = layout.getRegion(key);
30832 r.resizeTo(rstate.size);
30834 if(rstate.collapsed == true){
30837 r.expand(null, true);
30843 layout.endUpdate();
30845 this.state = state;
30847 this.layout = layout;
30848 layout.on("regionresized", this.onRegionResized, this);
30849 layout.on("regioncollapsed", this.onRegionCollapsed, this);
30850 layout.on("regionexpanded", this.onRegionExpanded, this);
30853 storeState : function(){
30854 this.provider.set(this.layout.id+"-layout-state", this.state);
30857 onRegionResized : function(region, newSize){
30858 this.state[region.getPosition()].size = newSize;
30862 onRegionCollapsed : function(region){
30863 this.state[region.getPosition()].collapsed = true;
30867 onRegionExpanded : function(region){
30868 this.state[region.getPosition()].collapsed = false;
30873 * Ext JS Library 1.1.1
30874 * Copyright(c) 2006-2007, Ext JS, LLC.
30876 * Originally Released Under LGPL - original licence link has changed is not relivant.
30879 * <script type="text/javascript">
30882 * @class Roo.ContentPanel
30883 * @extends Roo.util.Observable
30884 * A basic ContentPanel element.
30885 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
30886 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
30887 * @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
30888 * @cfg {Boolean} closable True if the panel can be closed/removed
30889 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
30890 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30891 * @cfg {Toolbar} toolbar A toolbar for this panel
30892 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
30893 * @cfg {String} title The title for this panel
30894 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30895 * @cfg {String} url Calls {@link #setUrl} with this value
30896 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30897 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
30898 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
30899 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
30902 * Create a new ContentPanel.
30903 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30904 * @param {String/Object} config A string to set only the title or a config object
30905 * @param {String} content (optional) Set the HTML content for this panel
30906 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30908 Roo.ContentPanel = function(el, config, content){
30912 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30916 if (config && config.parentLayout) {
30917 el = config.parentLayout.el.createChild();
30920 if(el.autoCreate){ // xtype is available if this is called from factory
30924 this.el = Roo.get(el);
30925 if(!this.el && config && config.autoCreate){
30926 if(typeof config.autoCreate == "object"){
30927 if(!config.autoCreate.id){
30928 config.autoCreate.id = config.id||el;
30930 this.el = Roo.DomHelper.append(document.body,
30931 config.autoCreate, true);
30933 this.el = Roo.DomHelper.append(document.body,
30934 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30937 this.closable = false;
30938 this.loaded = false;
30939 this.active = false;
30940 if(typeof config == "string"){
30941 this.title = config;
30943 Roo.apply(this, config);
30946 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30947 this.wrapEl = this.el.wrap();
30948 this.toolbar.container = this.el.insertSibling(false, 'before');
30949 this.toolbar = new Roo.Toolbar(this.toolbar);
30952 // xtype created footer. - not sure if will work as we normally have to render first..
30953 if (this.footer && !this.footer.el && this.footer.xtype) {
30954 if (!this.wrapEl) {
30955 this.wrapEl = this.el.wrap();
30958 this.footer.container = this.wrapEl.createChild();
30960 this.footer = Roo.factory(this.footer, Roo);
30965 this.resizeEl = Roo.get(this.resizeEl, true);
30967 this.resizeEl = this.el;
30969 // handle view.xtype
30977 * Fires when this panel is activated.
30978 * @param {Roo.ContentPanel} this
30982 * @event deactivate
30983 * Fires when this panel is activated.
30984 * @param {Roo.ContentPanel} this
30986 "deactivate" : true,
30990 * Fires when this panel is resized if fitToFrame is true.
30991 * @param {Roo.ContentPanel} this
30992 * @param {Number} width The width after any component adjustments
30993 * @param {Number} height The height after any component adjustments
30999 * Fires when this tab is created
31000 * @param {Roo.ContentPanel} this
31010 if(this.autoScroll){
31011 this.resizeEl.setStyle("overflow", "auto");
31013 // fix randome scrolling
31014 this.el.on('scroll', function() {
31015 Roo.log('fix random scolling');
31016 this.scrollTo('top',0);
31019 content = content || this.content;
31021 this.setContent(content);
31023 if(config && config.url){
31024 this.setUrl(this.url, this.params, this.loadOnce);
31029 Roo.ContentPanel.superclass.constructor.call(this);
31031 if (this.view && typeof(this.view.xtype) != 'undefined') {
31032 this.view.el = this.el.appendChild(document.createElement("div"));
31033 this.view = Roo.factory(this.view);
31034 this.view.render && this.view.render(false, '');
31038 this.fireEvent('render', this);
31041 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31043 setRegion : function(region){
31044 this.region = region;
31046 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31048 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31053 * Returns the toolbar for this Panel if one was configured.
31054 * @return {Roo.Toolbar}
31056 getToolbar : function(){
31057 return this.toolbar;
31060 setActiveState : function(active){
31061 this.active = active;
31063 this.fireEvent("deactivate", this);
31065 this.fireEvent("activate", this);
31069 * Updates this panel's element
31070 * @param {String} content The new content
31071 * @param {Boolean} loadScripts (optional) true to look for and process scripts
31073 setContent : function(content, loadScripts){
31074 this.el.update(content, loadScripts);
31077 ignoreResize : function(w, h){
31078 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31081 this.lastSize = {width: w, height: h};
31086 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31087 * @return {Roo.UpdateManager} The UpdateManager
31089 getUpdateManager : function(){
31090 return this.el.getUpdateManager();
31093 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31094 * @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:
31097 url: "your-url.php",
31098 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31099 callback: yourFunction,
31100 scope: yourObject, //(optional scope)
31103 text: "Loading...",
31108 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31109 * 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.
31110 * @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}
31111 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31112 * @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.
31113 * @return {Roo.ContentPanel} this
31116 var um = this.el.getUpdateManager();
31117 um.update.apply(um, arguments);
31123 * 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.
31124 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31125 * @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)
31126 * @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)
31127 * @return {Roo.UpdateManager} The UpdateManager
31129 setUrl : function(url, params, loadOnce){
31130 if(this.refreshDelegate){
31131 this.removeListener("activate", this.refreshDelegate);
31133 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31134 this.on("activate", this.refreshDelegate);
31135 return this.el.getUpdateManager();
31138 _handleRefresh : function(url, params, loadOnce){
31139 if(!loadOnce || !this.loaded){
31140 var updater = this.el.getUpdateManager();
31141 updater.update(url, params, this._setLoaded.createDelegate(this));
31145 _setLoaded : function(){
31146 this.loaded = true;
31150 * Returns this panel's id
31153 getId : function(){
31158 * Returns this panel's element - used by regiosn to add.
31159 * @return {Roo.Element}
31161 getEl : function(){
31162 return this.wrapEl || this.el;
31165 adjustForComponents : function(width, height)
31167 //Roo.log('adjustForComponents ');
31168 if(this.resizeEl != this.el){
31169 width -= this.el.getFrameWidth('lr');
31170 height -= this.el.getFrameWidth('tb');
31173 var te = this.toolbar.getEl();
31174 height -= te.getHeight();
31175 te.setWidth(width);
31178 var te = this.footer.getEl();
31179 //Roo.log("footer:" + te.getHeight());
31181 height -= te.getHeight();
31182 te.setWidth(width);
31186 if(this.adjustments){
31187 width += this.adjustments[0];
31188 height += this.adjustments[1];
31190 return {"width": width, "height": height};
31193 setSize : function(width, height){
31194 if(this.fitToFrame && !this.ignoreResize(width, height)){
31195 if(this.fitContainer && this.resizeEl != this.el){
31196 this.el.setSize(width, height);
31198 var size = this.adjustForComponents(width, height);
31199 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31200 this.fireEvent('resize', this, size.width, size.height);
31205 * Returns this panel's title
31208 getTitle : function(){
31213 * Set this panel's title
31214 * @param {String} title
31216 setTitle : function(title){
31217 this.title = title;
31219 this.region.updatePanelTitle(this, title);
31224 * Returns true is this panel was configured to be closable
31225 * @return {Boolean}
31227 isClosable : function(){
31228 return this.closable;
31231 beforeSlide : function(){
31233 this.resizeEl.clip();
31236 afterSlide : function(){
31238 this.resizeEl.unclip();
31242 * Force a content refresh from the URL specified in the {@link #setUrl} method.
31243 * Will fail silently if the {@link #setUrl} method has not been called.
31244 * This does not activate the panel, just updates its content.
31246 refresh : function(){
31247 if(this.refreshDelegate){
31248 this.loaded = false;
31249 this.refreshDelegate();
31254 * Destroys this panel
31256 destroy : function(){
31257 this.el.removeAllListeners();
31258 var tempEl = document.createElement("span");
31259 tempEl.appendChild(this.el.dom);
31260 tempEl.innerHTML = "";
31266 * form - if the content panel contains a form - this is a reference to it.
31267 * @type {Roo.form.Form}
31271 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31272 * This contains a reference to it.
31278 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31288 * @param {Object} cfg Xtype definition of item to add.
31291 addxtype : function(cfg) {
31293 if (cfg.xtype.match(/^Form$/)) {
31296 //if (this.footer) {
31297 // el = this.footer.container.insertSibling(false, 'before');
31299 el = this.el.createChild();
31302 this.form = new Roo.form.Form(cfg);
31305 if ( this.form.allItems.length) {
31306 this.form.render(el.dom);
31310 // should only have one of theses..
31311 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31312 // views.. should not be just added - used named prop 'view''
31314 cfg.el = this.el.appendChild(document.createElement("div"));
31317 var ret = new Roo.factory(cfg);
31319 ret.render && ret.render(false, ''); // render blank..
31328 * @class Roo.GridPanel
31329 * @extends Roo.ContentPanel
31331 * Create a new GridPanel.
31332 * @param {Roo.grid.Grid} grid The grid for this panel
31333 * @param {String/Object} config A string to set only the panel's title, or a config object
31335 Roo.GridPanel = function(grid, config){
31338 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31339 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31341 this.wrapper.dom.appendChild(grid.getGridEl().dom);
31343 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31346 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31348 // xtype created footer. - not sure if will work as we normally have to render first..
31349 if (this.footer && !this.footer.el && this.footer.xtype) {
31351 this.footer.container = this.grid.getView().getFooterPanel(true);
31352 this.footer.dataSource = this.grid.dataSource;
31353 this.footer = Roo.factory(this.footer, Roo);
31357 grid.monitorWindowResize = false; // turn off autosizing
31358 grid.autoHeight = false;
31359 grid.autoWidth = false;
31361 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31364 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31365 getId : function(){
31366 return this.grid.id;
31370 * Returns the grid for this panel
31371 * @return {Roo.grid.Grid}
31373 getGrid : function(){
31377 setSize : function(width, height){
31378 if(!this.ignoreResize(width, height)){
31379 var grid = this.grid;
31380 var size = this.adjustForComponents(width, height);
31381 grid.getGridEl().setSize(size.width, size.height);
31386 beforeSlide : function(){
31387 this.grid.getView().scroller.clip();
31390 afterSlide : function(){
31391 this.grid.getView().scroller.unclip();
31394 destroy : function(){
31395 this.grid.destroy();
31397 Roo.GridPanel.superclass.destroy.call(this);
31403 * @class Roo.NestedLayoutPanel
31404 * @extends Roo.ContentPanel
31406 * Create a new NestedLayoutPanel.
31409 * @param {Roo.BorderLayout} layout The layout for this panel
31410 * @param {String/Object} config A string to set only the title or a config object
31412 Roo.NestedLayoutPanel = function(layout, config)
31414 // construct with only one argument..
31415 /* FIXME - implement nicer consturctors
31416 if (layout.layout) {
31418 layout = config.layout;
31419 delete config.layout;
31421 if (layout.xtype && !layout.getEl) {
31422 // then layout needs constructing..
31423 layout = Roo.factory(layout, Roo);
31428 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31430 layout.monitorWindowResize = false; // turn off autosizing
31431 this.layout = layout;
31432 this.layout.getEl().addClass("x-layout-nested-layout");
31439 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31441 setSize : function(width, height){
31442 if(!this.ignoreResize(width, height)){
31443 var size = this.adjustForComponents(width, height);
31444 var el = this.layout.getEl();
31445 el.setSize(size.width, size.height);
31446 var touch = el.dom.offsetWidth;
31447 this.layout.layout();
31448 // ie requires a double layout on the first pass
31449 if(Roo.isIE && !this.initialized){
31450 this.initialized = true;
31451 this.layout.layout();
31456 // activate all subpanels if not currently active..
31458 setActiveState : function(active){
31459 this.active = active;
31461 this.fireEvent("deactivate", this);
31465 this.fireEvent("activate", this);
31466 // not sure if this should happen before or after..
31467 if (!this.layout) {
31468 return; // should not happen..
31471 for (var r in this.layout.regions) {
31472 reg = this.layout.getRegion(r);
31473 if (reg.getActivePanel()) {
31474 //reg.showPanel(reg.getActivePanel()); // force it to activate..
31475 reg.setActivePanel(reg.getActivePanel());
31478 if (!reg.panels.length) {
31481 reg.showPanel(reg.getPanel(0));
31490 * Returns the nested BorderLayout for this panel
31491 * @return {Roo.BorderLayout}
31493 getLayout : function(){
31494 return this.layout;
31498 * Adds a xtype elements to the layout of the nested panel
31502 xtype : 'ContentPanel',
31509 xtype : 'NestedLayoutPanel',
31515 items : [ ... list of content panels or nested layout panels.. ]
31519 * @param {Object} cfg Xtype definition of item to add.
31521 addxtype : function(cfg) {
31522 return this.layout.addxtype(cfg);
31527 Roo.ScrollPanel = function(el, config, content){
31528 config = config || {};
31529 config.fitToFrame = true;
31530 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31532 this.el.dom.style.overflow = "hidden";
31533 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31534 this.el.removeClass("x-layout-inactive-content");
31535 this.el.on("mousewheel", this.onWheel, this);
31537 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
31538 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
31539 up.unselectable(); down.unselectable();
31540 up.on("click", this.scrollUp, this);
31541 down.on("click", this.scrollDown, this);
31542 up.addClassOnOver("x-scroller-btn-over");
31543 down.addClassOnOver("x-scroller-btn-over");
31544 up.addClassOnClick("x-scroller-btn-click");
31545 down.addClassOnClick("x-scroller-btn-click");
31546 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31548 this.resizeEl = this.el;
31549 this.el = wrap; this.up = up; this.down = down;
31552 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31554 wheelIncrement : 5,
31555 scrollUp : function(){
31556 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31559 scrollDown : function(){
31560 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31563 afterScroll : function(){
31564 var el = this.resizeEl;
31565 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31566 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31567 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31570 setSize : function(){
31571 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31572 this.afterScroll();
31575 onWheel : function(e){
31576 var d = e.getWheelDelta();
31577 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31578 this.afterScroll();
31582 setContent : function(content, loadScripts){
31583 this.resizeEl.update(content, loadScripts);
31597 * @class Roo.TreePanel
31598 * @extends Roo.ContentPanel
31600 * Create a new TreePanel. - defaults to fit/scoll contents.
31601 * @param {String/Object} config A string to set only the panel's title, or a config object
31602 * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31604 Roo.TreePanel = function(config){
31605 var el = config.el;
31606 var tree = config.tree;
31607 delete config.tree;
31608 delete config.el; // hopefull!
31610 // wrapper for IE7 strict & safari scroll issue
31612 var treeEl = el.createChild();
31613 config.resizeEl = treeEl;
31617 Roo.TreePanel.superclass.constructor.call(this, el, config);
31620 this.tree = new Roo.tree.TreePanel(treeEl , tree);
31621 //console.log(tree);
31622 this.on('activate', function()
31624 if (this.tree.rendered) {
31627 //console.log('render tree');
31628 this.tree.render();
31630 // this should not be needed.. - it's actually the 'el' that resizes?
31631 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31633 //this.on('resize', function (cp, w, h) {
31634 // this.tree.innerCt.setWidth(w);
31635 // this.tree.innerCt.setHeight(h);
31636 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
31643 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
31660 * Ext JS Library 1.1.1
31661 * Copyright(c) 2006-2007, Ext JS, LLC.
31663 * Originally Released Under LGPL - original licence link has changed is not relivant.
31666 * <script type="text/javascript">
31671 * @class Roo.ReaderLayout
31672 * @extends Roo.BorderLayout
31673 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
31674 * center region containing two nested regions (a top one for a list view and one for item preview below),
31675 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31676 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31677 * expedites the setup of the overall layout and regions for this common application style.
31680 var reader = new Roo.ReaderLayout();
31681 var CP = Roo.ContentPanel; // shortcut for adding
31683 reader.beginUpdate();
31684 reader.add("north", new CP("north", "North"));
31685 reader.add("west", new CP("west", {title: "West"}));
31686 reader.add("east", new CP("east", {title: "East"}));
31688 reader.regions.listView.add(new CP("listView", "List"));
31689 reader.regions.preview.add(new CP("preview", "Preview"));
31690 reader.endUpdate();
31693 * Create a new ReaderLayout
31694 * @param {Object} config Configuration options
31695 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31696 * document.body if omitted)
31698 Roo.ReaderLayout = function(config, renderTo){
31699 var c = config || {size:{}};
31700 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31701 north: c.north !== false ? Roo.apply({
31705 }, c.north) : false,
31706 west: c.west !== false ? Roo.apply({
31714 margins:{left:5,right:0,bottom:5,top:5},
31715 cmargins:{left:5,right:5,bottom:5,top:5}
31716 }, c.west) : false,
31717 east: c.east !== false ? Roo.apply({
31725 margins:{left:0,right:5,bottom:5,top:5},
31726 cmargins:{left:5,right:5,bottom:5,top:5}
31727 }, c.east) : false,
31728 center: Roo.apply({
31729 tabPosition: 'top',
31733 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31737 this.el.addClass('x-reader');
31739 this.beginUpdate();
31741 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31742 south: c.preview !== false ? Roo.apply({
31749 cmargins:{top:5,left:0, right:0, bottom:0}
31750 }, c.preview) : false,
31751 center: Roo.apply({
31757 this.add('center', new Roo.NestedLayoutPanel(inner,
31758 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31762 this.regions.preview = inner.getRegion('south');
31763 this.regions.listView = inner.getRegion('center');
31766 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31768 * Ext JS Library 1.1.1
31769 * Copyright(c) 2006-2007, Ext JS, LLC.
31771 * Originally Released Under LGPL - original licence link has changed is not relivant.
31774 * <script type="text/javascript">
31778 * @class Roo.grid.Grid
31779 * @extends Roo.util.Observable
31780 * This class represents the primary interface of a component based grid control.
31781 * <br><br>Usage:<pre><code>
31782 var grid = new Roo.grid.Grid("my-container-id", {
31785 selModel: mySelectionModel,
31786 autoSizeColumns: true,
31787 monitorWindowResize: false,
31788 trackMouseOver: true
31793 * <b>Common Problems:</b><br/>
31794 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31795 * element will correct this<br/>
31796 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31797 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31798 * are unpredictable.<br/>
31799 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31800 * grid to calculate dimensions/offsets.<br/>
31802 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31803 * The container MUST have some type of size defined for the grid to fill. The container will be
31804 * automatically set to position relative if it isn't already.
31805 * @param {Object} config A config object that sets properties on this grid.
31807 Roo.grid.Grid = function(container, config){
31808 // initialize the container
31809 this.container = Roo.get(container);
31810 this.container.update("");
31811 this.container.setStyle("overflow", "hidden");
31812 this.container.addClass('x-grid-container');
31814 this.id = this.container.id;
31816 Roo.apply(this, config);
31817 // check and correct shorthanded configs
31819 this.dataSource = this.ds;
31823 this.colModel = this.cm;
31827 this.selModel = this.sm;
31831 if (this.selModel) {
31832 this.selModel = Roo.factory(this.selModel, Roo.grid);
31833 this.sm = this.selModel;
31834 this.sm.xmodule = this.xmodule || false;
31836 if (typeof(this.colModel.config) == 'undefined') {
31837 this.colModel = new Roo.grid.ColumnModel(this.colModel);
31838 this.cm = this.colModel;
31839 this.cm.xmodule = this.xmodule || false;
31841 if (this.dataSource) {
31842 this.dataSource= Roo.factory(this.dataSource, Roo.data);
31843 this.ds = this.dataSource;
31844 this.ds.xmodule = this.xmodule || false;
31851 this.container.setWidth(this.width);
31855 this.container.setHeight(this.height);
31862 * The raw click event for the entire grid.
31863 * @param {Roo.EventObject} e
31868 * The raw dblclick event for the entire grid.
31869 * @param {Roo.EventObject} e
31873 * @event contextmenu
31874 * The raw contextmenu event for the entire grid.
31875 * @param {Roo.EventObject} e
31877 "contextmenu" : true,
31880 * The raw mousedown event for the entire grid.
31881 * @param {Roo.EventObject} e
31883 "mousedown" : true,
31886 * The raw mouseup event for the entire grid.
31887 * @param {Roo.EventObject} e
31892 * The raw mouseover event for the entire grid.
31893 * @param {Roo.EventObject} e
31895 "mouseover" : true,
31898 * The raw mouseout event for the entire grid.
31899 * @param {Roo.EventObject} e
31904 * The raw keypress event for the entire grid.
31905 * @param {Roo.EventObject} e
31910 * The raw keydown event for the entire grid.
31911 * @param {Roo.EventObject} e
31919 * Fires when a cell is clicked
31920 * @param {Grid} this
31921 * @param {Number} rowIndex
31922 * @param {Number} columnIndex
31923 * @param {Roo.EventObject} e
31925 "cellclick" : true,
31927 * @event celldblclick
31928 * Fires when a cell is double clicked
31929 * @param {Grid} this
31930 * @param {Number} rowIndex
31931 * @param {Number} columnIndex
31932 * @param {Roo.EventObject} e
31934 "celldblclick" : true,
31937 * Fires when a row is clicked
31938 * @param {Grid} this
31939 * @param {Number} rowIndex
31940 * @param {Roo.EventObject} e
31944 * @event rowdblclick
31945 * Fires when a row is double clicked
31946 * @param {Grid} this
31947 * @param {Number} rowIndex
31948 * @param {Roo.EventObject} e
31950 "rowdblclick" : true,
31952 * @event headerclick
31953 * Fires when a header is clicked
31954 * @param {Grid} this
31955 * @param {Number} columnIndex
31956 * @param {Roo.EventObject} e
31958 "headerclick" : true,
31960 * @event headerdblclick
31961 * Fires when a header cell is double clicked
31962 * @param {Grid} this
31963 * @param {Number} columnIndex
31964 * @param {Roo.EventObject} e
31966 "headerdblclick" : true,
31968 * @event rowcontextmenu
31969 * Fires when a row is right clicked
31970 * @param {Grid} this
31971 * @param {Number} rowIndex
31972 * @param {Roo.EventObject} e
31974 "rowcontextmenu" : true,
31976 * @event cellcontextmenu
31977 * Fires when a cell is right clicked
31978 * @param {Grid} this
31979 * @param {Number} rowIndex
31980 * @param {Number} cellIndex
31981 * @param {Roo.EventObject} e
31983 "cellcontextmenu" : true,
31985 * @event headercontextmenu
31986 * Fires when a header is right clicked
31987 * @param {Grid} this
31988 * @param {Number} columnIndex
31989 * @param {Roo.EventObject} e
31991 "headercontextmenu" : true,
31993 * @event bodyscroll
31994 * Fires when the body element is scrolled
31995 * @param {Number} scrollLeft
31996 * @param {Number} scrollTop
31998 "bodyscroll" : true,
32000 * @event columnresize
32001 * Fires when the user resizes a column
32002 * @param {Number} columnIndex
32003 * @param {Number} newSize
32005 "columnresize" : true,
32007 * @event columnmove
32008 * Fires when the user moves a column
32009 * @param {Number} oldIndex
32010 * @param {Number} newIndex
32012 "columnmove" : true,
32015 * Fires when row(s) start being dragged
32016 * @param {Grid} this
32017 * @param {Roo.GridDD} dd The drag drop object
32018 * @param {event} e The raw browser event
32020 "startdrag" : true,
32023 * Fires when a drag operation is complete
32024 * @param {Grid} this
32025 * @param {Roo.GridDD} dd The drag drop object
32026 * @param {event} e The raw browser event
32031 * Fires when dragged row(s) are dropped on a valid DD target
32032 * @param {Grid} this
32033 * @param {Roo.GridDD} dd The drag drop object
32034 * @param {String} targetId The target drag drop object
32035 * @param {event} e The raw browser event
32040 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32041 * @param {Grid} this
32042 * @param {Roo.GridDD} dd The drag drop object
32043 * @param {String} targetId The target drag drop object
32044 * @param {event} e The raw browser event
32049 * Fires when the dragged row(s) first cross another DD target while being dragged
32050 * @param {Grid} this
32051 * @param {Roo.GridDD} dd The drag drop object
32052 * @param {String} targetId The target drag drop object
32053 * @param {event} e The raw browser event
32055 "dragenter" : true,
32058 * Fires when the dragged row(s) leave another DD target while being dragged
32059 * @param {Grid} this
32060 * @param {Roo.GridDD} dd The drag drop object
32061 * @param {String} targetId The target drag drop object
32062 * @param {event} e The raw browser event
32067 * Fires when a row is rendered, so you can change add a style to it.
32068 * @param {GridView} gridview The grid view
32069 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
32075 * Fires when the grid is rendered
32076 * @param {Grid} grid
32081 Roo.grid.Grid.superclass.constructor.call(this);
32083 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32086 * @cfg {String} ddGroup - drag drop group.
32090 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32092 minColumnWidth : 25,
32095 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32096 * <b>on initial render.</b> It is more efficient to explicitly size the columns
32097 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
32099 autoSizeColumns : false,
32102 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32104 autoSizeHeaders : true,
32107 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32109 monitorWindowResize : true,
32112 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32113 * rows measured to get a columns size. Default is 0 (all rows).
32115 maxRowsToMeasure : 0,
32118 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32120 trackMouseOver : true,
32123 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
32127 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32129 enableDragDrop : false,
32132 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32134 enableColumnMove : true,
32137 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32139 enableColumnHide : true,
32142 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32144 enableRowHeightSync : false,
32147 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
32152 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32154 autoHeight : false,
32157 * @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.
32159 autoExpandColumn : false,
32162 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32165 autoExpandMin : 50,
32168 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32170 autoExpandMax : 1000,
32173 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32178 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32182 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32192 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32193 * of a fixed width. Default is false.
32196 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32199 * Called once after all setup has been completed and the grid is ready to be rendered.
32200 * @return {Roo.grid.Grid} this
32202 render : function()
32204 var c = this.container;
32205 // try to detect autoHeight/width mode
32206 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32207 this.autoHeight = true;
32209 var view = this.getView();
32212 c.on("click", this.onClick, this);
32213 c.on("dblclick", this.onDblClick, this);
32214 c.on("contextmenu", this.onContextMenu, this);
32215 c.on("keydown", this.onKeyDown, this);
32217 c.on("touchstart", this.onTouchStart, this);
32220 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32222 this.getSelectionModel().init(this);
32227 this.loadMask = new Roo.LoadMask(this.container,
32228 Roo.apply({store:this.dataSource}, this.loadMask));
32232 if (this.toolbar && this.toolbar.xtype) {
32233 this.toolbar.container = this.getView().getHeaderPanel(true);
32234 this.toolbar = new Roo.Toolbar(this.toolbar);
32236 if (this.footer && this.footer.xtype) {
32237 this.footer.dataSource = this.getDataSource();
32238 this.footer.container = this.getView().getFooterPanel(true);
32239 this.footer = Roo.factory(this.footer, Roo);
32241 if (this.dropTarget && this.dropTarget.xtype) {
32242 delete this.dropTarget.xtype;
32243 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32247 this.rendered = true;
32248 this.fireEvent('render', this);
32253 * Reconfigures the grid to use a different Store and Column Model.
32254 * The View will be bound to the new objects and refreshed.
32255 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32256 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32258 reconfigure : function(dataSource, colModel){
32260 this.loadMask.destroy();
32261 this.loadMask = new Roo.LoadMask(this.container,
32262 Roo.apply({store:dataSource}, this.loadMask));
32264 this.view.bind(dataSource, colModel);
32265 this.dataSource = dataSource;
32266 this.colModel = colModel;
32267 this.view.refresh(true);
32271 onKeyDown : function(e){
32272 this.fireEvent("keydown", e);
32276 * Destroy this grid.
32277 * @param {Boolean} removeEl True to remove the element
32279 destroy : function(removeEl, keepListeners){
32281 this.loadMask.destroy();
32283 var c = this.container;
32284 c.removeAllListeners();
32285 this.view.destroy();
32286 this.colModel.purgeListeners();
32287 if(!keepListeners){
32288 this.purgeListeners();
32291 if(removeEl === true){
32297 processEvent : function(name, e){
32298 // does this fire select???
32299 //Roo.log('grid:processEvent ' + name);
32301 if (name != 'touchstart' ) {
32302 this.fireEvent(name, e);
32305 var t = e.getTarget();
32307 var header = v.findHeaderIndex(t);
32308 if(header !== false){
32309 var ename = name == 'touchstart' ? 'click' : name;
32311 this.fireEvent("header" + ename, this, header, e);
32313 var row = v.findRowIndex(t);
32314 var cell = v.findCellIndex(t);
32315 if (name == 'touchstart') {
32316 // first touch is always a click.
32317 // hopefull this happens after selection is updated.?
32320 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32321 var cs = this.selModel.getSelectedCell();
32322 if (row == cs[0] && cell == cs[1]){
32326 if (typeof(this.selModel.getSelections) != 'undefined') {
32327 var cs = this.selModel.getSelections();
32328 var ds = this.dataSource;
32329 if (cs.length == 1 && ds.getAt(row) == cs[0]){
32340 this.fireEvent("row" + name, this, row, e);
32341 if(cell !== false){
32342 this.fireEvent("cell" + name, this, row, cell, e);
32349 onClick : function(e){
32350 this.processEvent("click", e);
32353 onTouchStart : function(e){
32354 this.processEvent("touchstart", e);
32358 onContextMenu : function(e, t){
32359 this.processEvent("contextmenu", e);
32363 onDblClick : function(e){
32364 this.processEvent("dblclick", e);
32368 walkCells : function(row, col, step, fn, scope){
32369 var cm = this.colModel, clen = cm.getColumnCount();
32370 var ds = this.dataSource, rlen = ds.getCount(), first = true;
32382 if(fn.call(scope || this, row, col, cm) === true){
32400 if(fn.call(scope || this, row, col, cm) === true){
32412 getSelections : function(){
32413 return this.selModel.getSelections();
32417 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32418 * but if manual update is required this method will initiate it.
32420 autoSize : function(){
32422 this.view.layout();
32423 if(this.view.adjustForScroll){
32424 this.view.adjustForScroll();
32430 * Returns the grid's underlying element.
32431 * @return {Element} The element
32433 getGridEl : function(){
32434 return this.container;
32437 // private for compatibility, overridden by editor grid
32438 stopEditing : function(){},
32441 * Returns the grid's SelectionModel.
32442 * @return {SelectionModel}
32444 getSelectionModel : function(){
32445 if(!this.selModel){
32446 this.selModel = new Roo.grid.RowSelectionModel();
32448 return this.selModel;
32452 * Returns the grid's DataSource.
32453 * @return {DataSource}
32455 getDataSource : function(){
32456 return this.dataSource;
32460 * Returns the grid's ColumnModel.
32461 * @return {ColumnModel}
32463 getColumnModel : function(){
32464 return this.colModel;
32468 * Returns the grid's GridView object.
32469 * @return {GridView}
32471 getView : function(){
32473 this.view = new Roo.grid.GridView(this.viewConfig);
32478 * Called to get grid's drag proxy text, by default returns this.ddText.
32481 getDragDropText : function(){
32482 var count = this.selModel.getCount();
32483 return String.format(this.ddText, count, count == 1 ? '' : 's');
32487 * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32488 * %0 is replaced with the number of selected rows.
32491 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32493 * Ext JS Library 1.1.1
32494 * Copyright(c) 2006-2007, Ext JS, LLC.
32496 * Originally Released Under LGPL - original licence link has changed is not relivant.
32499 * <script type="text/javascript">
32502 Roo.grid.AbstractGridView = function(){
32506 "beforerowremoved" : true,
32507 "beforerowsinserted" : true,
32508 "beforerefresh" : true,
32509 "rowremoved" : true,
32510 "rowsinserted" : true,
32511 "rowupdated" : true,
32514 Roo.grid.AbstractGridView.superclass.constructor.call(this);
32517 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32518 rowClass : "x-grid-row",
32519 cellClass : "x-grid-cell",
32520 tdClass : "x-grid-td",
32521 hdClass : "x-grid-hd",
32522 splitClass : "x-grid-hd-split",
32524 init: function(grid){
32526 var cid = this.grid.getGridEl().id;
32527 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32528 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32529 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32530 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32533 getColumnRenderers : function(){
32534 var renderers = [];
32535 var cm = this.grid.colModel;
32536 var colCount = cm.getColumnCount();
32537 for(var i = 0; i < colCount; i++){
32538 renderers[i] = cm.getRenderer(i);
32543 getColumnIds : function(){
32545 var cm = this.grid.colModel;
32546 var colCount = cm.getColumnCount();
32547 for(var i = 0; i < colCount; i++){
32548 ids[i] = cm.getColumnId(i);
32553 getDataIndexes : function(){
32554 if(!this.indexMap){
32555 this.indexMap = this.buildIndexMap();
32557 return this.indexMap.colToData;
32560 getColumnIndexByDataIndex : function(dataIndex){
32561 if(!this.indexMap){
32562 this.indexMap = this.buildIndexMap();
32564 return this.indexMap.dataToCol[dataIndex];
32568 * Set a css style for a column dynamically.
32569 * @param {Number} colIndex The index of the column
32570 * @param {String} name The css property name
32571 * @param {String} value The css value
32573 setCSSStyle : function(colIndex, name, value){
32574 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32575 Roo.util.CSS.updateRule(selector, name, value);
32578 generateRules : function(cm){
32579 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32580 Roo.util.CSS.removeStyleSheet(rulesId);
32581 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32582 var cid = cm.getColumnId(i);
32583 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32584 this.tdSelector, cid, " {\n}\n",
32585 this.hdSelector, cid, " {\n}\n",
32586 this.splitSelector, cid, " {\n}\n");
32588 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32592 * Ext JS Library 1.1.1
32593 * Copyright(c) 2006-2007, Ext JS, LLC.
32595 * Originally Released Under LGPL - original licence link has changed is not relivant.
32598 * <script type="text/javascript">
32602 // This is a support class used internally by the Grid components
32603 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32605 this.view = grid.getView();
32606 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32607 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32609 this.setHandleElId(Roo.id(hd));
32610 this.setOuterHandleElId(Roo.id(hd2));
32612 this.scroll = false;
32614 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32616 getDragData : function(e){
32617 var t = Roo.lib.Event.getTarget(e);
32618 var h = this.view.findHeaderCell(t);
32620 return {ddel: h.firstChild, header:h};
32625 onInitDrag : function(e){
32626 this.view.headersDisabled = true;
32627 var clone = this.dragData.ddel.cloneNode(true);
32628 clone.id = Roo.id();
32629 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32630 this.proxy.update(clone);
32634 afterValidDrop : function(){
32636 setTimeout(function(){
32637 v.headersDisabled = false;
32641 afterInvalidDrop : function(){
32643 setTimeout(function(){
32644 v.headersDisabled = false;
32650 * Ext JS Library 1.1.1
32651 * Copyright(c) 2006-2007, Ext JS, LLC.
32653 * Originally Released Under LGPL - original licence link has changed is not relivant.
32656 * <script type="text/javascript">
32659 // This is a support class used internally by the Grid components
32660 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32662 this.view = grid.getView();
32663 // split the proxies so they don't interfere with mouse events
32664 this.proxyTop = Roo.DomHelper.append(document.body, {
32665 cls:"col-move-top", html:" "
32667 this.proxyBottom = Roo.DomHelper.append(document.body, {
32668 cls:"col-move-bottom", html:" "
32670 this.proxyTop.hide = this.proxyBottom.hide = function(){
32671 this.setLeftTop(-100,-100);
32672 this.setStyle("visibility", "hidden");
32674 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32675 // temporarily disabled
32676 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32677 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32679 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32680 proxyOffsets : [-4, -9],
32681 fly: Roo.Element.fly,
32683 getTargetFromEvent : function(e){
32684 var t = Roo.lib.Event.getTarget(e);
32685 var cindex = this.view.findCellIndex(t);
32686 if(cindex !== false){
32687 return this.view.getHeaderCell(cindex);
32692 nextVisible : function(h){
32693 var v = this.view, cm = this.grid.colModel;
32696 if(!cm.isHidden(v.getCellIndex(h))){
32704 prevVisible : function(h){
32705 var v = this.view, cm = this.grid.colModel;
32708 if(!cm.isHidden(v.getCellIndex(h))){
32716 positionIndicator : function(h, n, e){
32717 var x = Roo.lib.Event.getPageX(e);
32718 var r = Roo.lib.Dom.getRegion(n.firstChild);
32719 var px, pt, py = r.top + this.proxyOffsets[1];
32720 if((r.right - x) <= (r.right-r.left)/2){
32721 px = r.right+this.view.borderWidth;
32727 var oldIndex = this.view.getCellIndex(h);
32728 var newIndex = this.view.getCellIndex(n);
32730 if(this.grid.colModel.isFixed(newIndex)){
32734 var locked = this.grid.colModel.isLocked(newIndex);
32739 if(oldIndex < newIndex){
32742 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32745 px += this.proxyOffsets[0];
32746 this.proxyTop.setLeftTop(px, py);
32747 this.proxyTop.show();
32748 if(!this.bottomOffset){
32749 this.bottomOffset = this.view.mainHd.getHeight();
32751 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32752 this.proxyBottom.show();
32756 onNodeEnter : function(n, dd, e, data){
32757 if(data.header != n){
32758 this.positionIndicator(data.header, n, e);
32762 onNodeOver : function(n, dd, e, data){
32763 var result = false;
32764 if(data.header != n){
32765 result = this.positionIndicator(data.header, n, e);
32768 this.proxyTop.hide();
32769 this.proxyBottom.hide();
32771 return result ? this.dropAllowed : this.dropNotAllowed;
32774 onNodeOut : function(n, dd, e, data){
32775 this.proxyTop.hide();
32776 this.proxyBottom.hide();
32779 onNodeDrop : function(n, dd, e, data){
32780 var h = data.header;
32782 var cm = this.grid.colModel;
32783 var x = Roo.lib.Event.getPageX(e);
32784 var r = Roo.lib.Dom.getRegion(n.firstChild);
32785 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32786 var oldIndex = this.view.getCellIndex(h);
32787 var newIndex = this.view.getCellIndex(n);
32788 var locked = cm.isLocked(newIndex);
32792 if(oldIndex < newIndex){
32795 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32798 cm.setLocked(oldIndex, locked, true);
32799 cm.moveColumn(oldIndex, newIndex);
32800 this.grid.fireEvent("columnmove", oldIndex, newIndex);
32808 * Ext JS Library 1.1.1
32809 * Copyright(c) 2006-2007, Ext JS, LLC.
32811 * Originally Released Under LGPL - original licence link has changed is not relivant.
32814 * <script type="text/javascript">
32818 * @class Roo.grid.GridView
32819 * @extends Roo.util.Observable
32822 * @param {Object} config
32824 Roo.grid.GridView = function(config){
32825 Roo.grid.GridView.superclass.constructor.call(this);
32828 Roo.apply(this, config);
32831 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32833 unselectable : 'unselectable="on"',
32834 unselectableCls : 'x-unselectable',
32837 rowClass : "x-grid-row",
32839 cellClass : "x-grid-col",
32841 tdClass : "x-grid-td",
32843 hdClass : "x-grid-hd",
32845 splitClass : "x-grid-split",
32847 sortClasses : ["sort-asc", "sort-desc"],
32849 enableMoveAnim : false,
32853 dh : Roo.DomHelper,
32855 fly : Roo.Element.fly,
32857 css : Roo.util.CSS,
32863 scrollIncrement : 22,
32865 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32867 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32869 bind : function(ds, cm){
32871 this.ds.un("load", this.onLoad, this);
32872 this.ds.un("datachanged", this.onDataChange, this);
32873 this.ds.un("add", this.onAdd, this);
32874 this.ds.un("remove", this.onRemove, this);
32875 this.ds.un("update", this.onUpdate, this);
32876 this.ds.un("clear", this.onClear, this);
32879 ds.on("load", this.onLoad, this);
32880 ds.on("datachanged", this.onDataChange, this);
32881 ds.on("add", this.onAdd, this);
32882 ds.on("remove", this.onRemove, this);
32883 ds.on("update", this.onUpdate, this);
32884 ds.on("clear", this.onClear, this);
32889 this.cm.un("widthchange", this.onColWidthChange, this);
32890 this.cm.un("headerchange", this.onHeaderChange, this);
32891 this.cm.un("hiddenchange", this.onHiddenChange, this);
32892 this.cm.un("columnmoved", this.onColumnMove, this);
32893 this.cm.un("columnlockchange", this.onColumnLock, this);
32896 this.generateRules(cm);
32897 cm.on("widthchange", this.onColWidthChange, this);
32898 cm.on("headerchange", this.onHeaderChange, this);
32899 cm.on("hiddenchange", this.onHiddenChange, this);
32900 cm.on("columnmoved", this.onColumnMove, this);
32901 cm.on("columnlockchange", this.onColumnLock, this);
32906 init: function(grid){
32907 Roo.grid.GridView.superclass.init.call(this, grid);
32909 this.bind(grid.dataSource, grid.colModel);
32911 grid.on("headerclick", this.handleHeaderClick, this);
32913 if(grid.trackMouseOver){
32914 grid.on("mouseover", this.onRowOver, this);
32915 grid.on("mouseout", this.onRowOut, this);
32917 grid.cancelTextSelection = function(){};
32918 this.gridId = grid.id;
32920 var tpls = this.templates || {};
32923 tpls.master = new Roo.Template(
32924 '<div class="x-grid" hidefocus="true">',
32925 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32926 '<div class="x-grid-topbar"></div>',
32927 '<div class="x-grid-scroller"><div></div></div>',
32928 '<div class="x-grid-locked">',
32929 '<div class="x-grid-header">{lockedHeader}</div>',
32930 '<div class="x-grid-body">{lockedBody}</div>',
32932 '<div class="x-grid-viewport">',
32933 '<div class="x-grid-header">{header}</div>',
32934 '<div class="x-grid-body">{body}</div>',
32936 '<div class="x-grid-bottombar"></div>',
32938 '<div class="x-grid-resize-proxy"> </div>',
32941 tpls.master.disableformats = true;
32945 tpls.header = new Roo.Template(
32946 '<table border="0" cellspacing="0" cellpadding="0">',
32947 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32950 tpls.header.disableformats = true;
32952 tpls.header.compile();
32955 tpls.hcell = new Roo.Template(
32956 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32957 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32960 tpls.hcell.disableFormats = true;
32962 tpls.hcell.compile();
32965 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
32966 this.unselectableCls + '" ' + this.unselectable +'> </div>');
32967 tpls.hsplit.disableFormats = true;
32969 tpls.hsplit.compile();
32972 tpls.body = new Roo.Template(
32973 '<table border="0" cellspacing="0" cellpadding="0">',
32974 "<tbody>{rows}</tbody>",
32977 tpls.body.disableFormats = true;
32979 tpls.body.compile();
32982 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32983 tpls.row.disableFormats = true;
32985 tpls.row.compile();
32988 tpls.cell = new Roo.Template(
32989 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32990 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
32991 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
32994 tpls.cell.disableFormats = true;
32996 tpls.cell.compile();
32998 this.templates = tpls;
33001 // remap these for backwards compat
33002 onColWidthChange : function(){
33003 this.updateColumns.apply(this, arguments);
33005 onHeaderChange : function(){
33006 this.updateHeaders.apply(this, arguments);
33008 onHiddenChange : function(){
33009 this.handleHiddenChange.apply(this, arguments);
33011 onColumnMove : function(){
33012 this.handleColumnMove.apply(this, arguments);
33014 onColumnLock : function(){
33015 this.handleLockChange.apply(this, arguments);
33018 onDataChange : function(){
33020 this.updateHeaderSortState();
33023 onClear : function(){
33027 onUpdate : function(ds, record){
33028 this.refreshRow(record);
33031 refreshRow : function(record){
33032 var ds = this.ds, index;
33033 if(typeof record == 'number'){
33035 record = ds.getAt(index);
33037 index = ds.indexOf(record);
33039 this.insertRows(ds, index, index, true);
33040 this.onRemove(ds, record, index+1, true);
33041 this.syncRowHeights(index, index);
33043 this.fireEvent("rowupdated", this, index, record);
33046 onAdd : function(ds, records, index){
33047 this.insertRows(ds, index, index + (records.length-1));
33050 onRemove : function(ds, record, index, isUpdate){
33051 if(isUpdate !== true){
33052 this.fireEvent("beforerowremoved", this, index, record);
33054 var bt = this.getBodyTable(), lt = this.getLockedTable();
33055 if(bt.rows[index]){
33056 bt.firstChild.removeChild(bt.rows[index]);
33058 if(lt.rows[index]){
33059 lt.firstChild.removeChild(lt.rows[index]);
33061 if(isUpdate !== true){
33062 this.stripeRows(index);
33063 this.syncRowHeights(index, index);
33065 this.fireEvent("rowremoved", this, index, record);
33069 onLoad : function(){
33070 this.scrollToTop();
33074 * Scrolls the grid to the top
33076 scrollToTop : function(){
33078 this.scroller.dom.scrollTop = 0;
33084 * Gets a panel in the header of the grid that can be used for toolbars etc.
33085 * After modifying the contents of this panel a call to grid.autoSize() may be
33086 * required to register any changes in size.
33087 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33088 * @return Roo.Element
33090 getHeaderPanel : function(doShow){
33092 this.headerPanel.show();
33094 return this.headerPanel;
33098 * Gets a panel in the footer of the grid that can be used for toolbars etc.
33099 * After modifying the contents of this panel a call to grid.autoSize() may be
33100 * required to register any changes in size.
33101 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33102 * @return Roo.Element
33104 getFooterPanel : function(doShow){
33106 this.footerPanel.show();
33108 return this.footerPanel;
33111 initElements : function(){
33112 var E = Roo.Element;
33113 var el = this.grid.getGridEl().dom.firstChild;
33114 var cs = el.childNodes;
33116 this.el = new E(el);
33118 this.focusEl = new E(el.firstChild);
33119 this.focusEl.swallowEvent("click", true);
33121 this.headerPanel = new E(cs[1]);
33122 this.headerPanel.enableDisplayMode("block");
33124 this.scroller = new E(cs[2]);
33125 this.scrollSizer = new E(this.scroller.dom.firstChild);
33127 this.lockedWrap = new E(cs[3]);
33128 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33129 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33131 this.mainWrap = new E(cs[4]);
33132 this.mainHd = new E(this.mainWrap.dom.firstChild);
33133 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33135 this.footerPanel = new E(cs[5]);
33136 this.footerPanel.enableDisplayMode("block");
33138 this.resizeProxy = new E(cs[6]);
33140 this.headerSelector = String.format(
33141 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33142 this.lockedHd.id, this.mainHd.id
33145 this.splitterSelector = String.format(
33146 '#{0} div.x-grid-split, #{1} div.x-grid-split',
33147 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33150 idToCssName : function(s)
33152 return s.replace(/[^a-z0-9]+/ig, '-');
33155 getHeaderCell : function(index){
33156 return Roo.DomQuery.select(this.headerSelector)[index];
33159 getHeaderCellMeasure : function(index){
33160 return this.getHeaderCell(index).firstChild;
33163 getHeaderCellText : function(index){
33164 return this.getHeaderCell(index).firstChild.firstChild;
33167 getLockedTable : function(){
33168 return this.lockedBody.dom.firstChild;
33171 getBodyTable : function(){
33172 return this.mainBody.dom.firstChild;
33175 getLockedRow : function(index){
33176 return this.getLockedTable().rows[index];
33179 getRow : function(index){
33180 return this.getBodyTable().rows[index];
33183 getRowComposite : function(index){
33185 this.rowEl = new Roo.CompositeElementLite();
33187 var els = [], lrow, mrow;
33188 if(lrow = this.getLockedRow(index)){
33191 if(mrow = this.getRow(index)){
33194 this.rowEl.elements = els;
33198 * Gets the 'td' of the cell
33200 * @param {Integer} rowIndex row to select
33201 * @param {Integer} colIndex column to select
33205 getCell : function(rowIndex, colIndex){
33206 var locked = this.cm.getLockedCount();
33208 if(colIndex < locked){
33209 source = this.lockedBody.dom.firstChild;
33211 source = this.mainBody.dom.firstChild;
33212 colIndex -= locked;
33214 return source.rows[rowIndex].childNodes[colIndex];
33217 getCellText : function(rowIndex, colIndex){
33218 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33221 getCellBox : function(cell){
33222 var b = this.fly(cell).getBox();
33223 if(Roo.isOpera){ // opera fails to report the Y
33224 b.y = cell.offsetTop + this.mainBody.getY();
33229 getCellIndex : function(cell){
33230 var id = String(cell.className).match(this.cellRE);
33232 return parseInt(id[1], 10);
33237 findHeaderIndex : function(n){
33238 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33239 return r ? this.getCellIndex(r) : false;
33242 findHeaderCell : function(n){
33243 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33244 return r ? r : false;
33247 findRowIndex : function(n){
33251 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33252 return r ? r.rowIndex : false;
33255 findCellIndex : function(node){
33256 var stop = this.el.dom;
33257 while(node && node != stop){
33258 if(this.findRE.test(node.className)){
33259 return this.getCellIndex(node);
33261 node = node.parentNode;
33266 getColumnId : function(index){
33267 return this.cm.getColumnId(index);
33270 getSplitters : function()
33272 if(this.splitterSelector){
33273 return Roo.DomQuery.select(this.splitterSelector);
33279 getSplitter : function(index){
33280 return this.getSplitters()[index];
33283 onRowOver : function(e, t){
33285 if((row = this.findRowIndex(t)) !== false){
33286 this.getRowComposite(row).addClass("x-grid-row-over");
33290 onRowOut : function(e, t){
33292 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33293 this.getRowComposite(row).removeClass("x-grid-row-over");
33297 renderHeaders : function(){
33299 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33300 var cb = [], lb = [], sb = [], lsb = [], p = {};
33301 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33302 p.cellId = "x-grid-hd-0-" + i;
33303 p.splitId = "x-grid-csplit-0-" + i;
33304 p.id = cm.getColumnId(i);
33305 p.value = cm.getColumnHeader(i) || "";
33306 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
33307 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33308 if(!cm.isLocked(i)){
33309 cb[cb.length] = ct.apply(p);
33310 sb[sb.length] = st.apply(p);
33312 lb[lb.length] = ct.apply(p);
33313 lsb[lsb.length] = st.apply(p);
33316 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33317 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33320 updateHeaders : function(){
33321 var html = this.renderHeaders();
33322 this.lockedHd.update(html[0]);
33323 this.mainHd.update(html[1]);
33327 * Focuses the specified row.
33328 * @param {Number} row The row index
33330 focusRow : function(row)
33332 //Roo.log('GridView.focusRow');
33333 var x = this.scroller.dom.scrollLeft;
33334 this.focusCell(row, 0, false);
33335 this.scroller.dom.scrollLeft = x;
33339 * Focuses the specified cell.
33340 * @param {Number} row The row index
33341 * @param {Number} col The column index
33342 * @param {Boolean} hscroll false to disable horizontal scrolling
33344 focusCell : function(row, col, hscroll)
33346 //Roo.log('GridView.focusCell');
33347 var el = this.ensureVisible(row, col, hscroll);
33348 this.focusEl.alignTo(el, "tl-tl");
33350 this.focusEl.focus();
33352 this.focusEl.focus.defer(1, this.focusEl);
33357 * Scrolls the specified cell into view
33358 * @param {Number} row The row index
33359 * @param {Number} col The column index
33360 * @param {Boolean} hscroll false to disable horizontal scrolling
33362 ensureVisible : function(row, col, hscroll)
33364 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33365 //return null; //disable for testing.
33366 if(typeof row != "number"){
33367 row = row.rowIndex;
33369 if(row < 0 && row >= this.ds.getCount()){
33372 col = (col !== undefined ? col : 0);
33373 var cm = this.grid.colModel;
33374 while(cm.isHidden(col)){
33378 var el = this.getCell(row, col);
33382 var c = this.scroller.dom;
33384 var ctop = parseInt(el.offsetTop, 10);
33385 var cleft = parseInt(el.offsetLeft, 10);
33386 var cbot = ctop + el.offsetHeight;
33387 var cright = cleft + el.offsetWidth;
33389 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33390 var stop = parseInt(c.scrollTop, 10);
33391 var sleft = parseInt(c.scrollLeft, 10);
33392 var sbot = stop + ch;
33393 var sright = sleft + c.clientWidth;
33395 Roo.log('GridView.ensureVisible:' +
33397 ' c.clientHeight:' + c.clientHeight +
33398 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33406 c.scrollTop = ctop;
33407 //Roo.log("set scrolltop to ctop DISABLE?");
33408 }else if(cbot > sbot){
33409 //Roo.log("set scrolltop to cbot-ch");
33410 c.scrollTop = cbot-ch;
33413 if(hscroll !== false){
33415 c.scrollLeft = cleft;
33416 }else if(cright > sright){
33417 c.scrollLeft = cright-c.clientWidth;
33424 updateColumns : function(){
33425 this.grid.stopEditing();
33426 var cm = this.grid.colModel, colIds = this.getColumnIds();
33427 //var totalWidth = cm.getTotalWidth();
33429 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33430 //if(cm.isHidden(i)) continue;
33431 var w = cm.getColumnWidth(i);
33432 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33433 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33435 this.updateSplitters();
33438 generateRules : function(cm){
33439 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33440 Roo.util.CSS.removeStyleSheet(rulesId);
33441 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33442 var cid = cm.getColumnId(i);
33444 if(cm.config[i].align){
33445 align = 'text-align:'+cm.config[i].align+';';
33448 if(cm.isHidden(i)){
33449 hidden = 'display:none;';
33451 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33453 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33454 this.hdSelector, cid, " {\n", align, width, "}\n",
33455 this.tdSelector, cid, " {\n",hidden,"\n}\n",
33456 this.splitSelector, cid, " {\n", hidden , "\n}\n");
33458 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33461 updateSplitters : function(){
33462 var cm = this.cm, s = this.getSplitters();
33463 if(s){ // splitters not created yet
33464 var pos = 0, locked = true;
33465 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33466 if(cm.isHidden(i)) {
33469 var w = cm.getColumnWidth(i); // make sure it's a number
33470 if(!cm.isLocked(i) && locked){
33475 s[i].style.left = (pos-this.splitOffset) + "px";
33480 handleHiddenChange : function(colModel, colIndex, hidden){
33482 this.hideColumn(colIndex);
33484 this.unhideColumn(colIndex);
33488 hideColumn : function(colIndex){
33489 var cid = this.getColumnId(colIndex);
33490 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33491 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33493 this.updateHeaders();
33495 this.updateSplitters();
33499 unhideColumn : function(colIndex){
33500 var cid = this.getColumnId(colIndex);
33501 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33502 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33505 this.updateHeaders();
33507 this.updateSplitters();
33511 insertRows : function(dm, firstRow, lastRow, isUpdate){
33512 if(firstRow == 0 && lastRow == dm.getCount()-1){
33516 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33518 var s = this.getScrollState();
33519 var markup = this.renderRows(firstRow, lastRow);
33520 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33521 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33522 this.restoreScroll(s);
33524 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33525 this.syncRowHeights(firstRow, lastRow);
33526 this.stripeRows(firstRow);
33532 bufferRows : function(markup, target, index){
33533 var before = null, trows = target.rows, tbody = target.tBodies[0];
33534 if(index < trows.length){
33535 before = trows[index];
33537 var b = document.createElement("div");
33538 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33539 var rows = b.firstChild.rows;
33540 for(var i = 0, len = rows.length; i < len; i++){
33542 tbody.insertBefore(rows[0], before);
33544 tbody.appendChild(rows[0]);
33551 deleteRows : function(dm, firstRow, lastRow){
33552 if(dm.getRowCount()<1){
33553 this.fireEvent("beforerefresh", this);
33554 this.mainBody.update("");
33555 this.lockedBody.update("");
33556 this.fireEvent("refresh", this);
33558 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33559 var bt = this.getBodyTable();
33560 var tbody = bt.firstChild;
33561 var rows = bt.rows;
33562 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33563 tbody.removeChild(rows[firstRow]);
33565 this.stripeRows(firstRow);
33566 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33570 updateRows : function(dataSource, firstRow, lastRow){
33571 var s = this.getScrollState();
33573 this.restoreScroll(s);
33576 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33580 this.updateHeaderSortState();
33583 getScrollState : function(){
33585 var sb = this.scroller.dom;
33586 return {left: sb.scrollLeft, top: sb.scrollTop};
33589 stripeRows : function(startRow){
33590 if(!this.grid.stripeRows || this.ds.getCount() < 1){
33593 startRow = startRow || 0;
33594 var rows = this.getBodyTable().rows;
33595 var lrows = this.getLockedTable().rows;
33596 var cls = ' x-grid-row-alt ';
33597 for(var i = startRow, len = rows.length; i < len; i++){
33598 var row = rows[i], lrow = lrows[i];
33599 var isAlt = ((i+1) % 2 == 0);
33600 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33601 if(isAlt == hasAlt){
33605 row.className += " x-grid-row-alt";
33607 row.className = row.className.replace("x-grid-row-alt", "");
33610 lrow.className = row.className;
33615 restoreScroll : function(state){
33616 //Roo.log('GridView.restoreScroll');
33617 var sb = this.scroller.dom;
33618 sb.scrollLeft = state.left;
33619 sb.scrollTop = state.top;
33623 syncScroll : function(){
33624 //Roo.log('GridView.syncScroll');
33625 var sb = this.scroller.dom;
33626 var sh = this.mainHd.dom;
33627 var bs = this.mainBody.dom;
33628 var lv = this.lockedBody.dom;
33629 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33630 lv.scrollTop = bs.scrollTop = sb.scrollTop;
33633 handleScroll : function(e){
33635 var sb = this.scroller.dom;
33636 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33640 handleWheel : function(e){
33641 var d = e.getWheelDelta();
33642 this.scroller.dom.scrollTop -= d*22;
33643 // set this here to prevent jumpy scrolling on large tables
33644 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33648 renderRows : function(startRow, endRow){
33649 // pull in all the crap needed to render rows
33650 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33651 var colCount = cm.getColumnCount();
33653 if(ds.getCount() < 1){
33657 // build a map for all the columns
33659 for(var i = 0; i < colCount; i++){
33660 var name = cm.getDataIndex(i);
33662 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33663 renderer : cm.getRenderer(i),
33664 id : cm.getColumnId(i),
33665 locked : cm.isLocked(i),
33666 has_editor : cm.isCellEditable(i)
33670 startRow = startRow || 0;
33671 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33673 // records to render
33674 var rs = ds.getRange(startRow, endRow);
33676 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33679 // As much as I hate to duplicate code, this was branched because FireFox really hates
33680 // [].join("") on strings. The performance difference was substantial enough to
33681 // branch this function
33682 doRender : Roo.isGecko ?
33683 function(cs, rs, ds, startRow, colCount, stripe){
33684 var ts = this.templates, ct = ts.cell, rt = ts.row;
33686 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33688 var hasListener = this.grid.hasListener('rowclass');
33690 for(var j = 0, len = rs.length; j < len; j++){
33691 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33692 for(var i = 0; i < colCount; i++){
33694 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33696 p.css = p.attr = "";
33697 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33698 if(p.value == undefined || p.value === "") {
33699 p.value = " ";
33702 p.css += ' x-grid-editable-cell';
33704 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
33705 p.css += ' x-grid-dirty-cell';
33707 var markup = ct.apply(p);
33715 if(stripe && ((rowIndex+1) % 2 == 0)){
33716 alt.push("x-grid-row-alt")
33719 alt.push( " x-grid-dirty-row");
33722 if(this.getRowClass){
33723 alt.push(this.getRowClass(r, rowIndex));
33729 rowIndex : rowIndex,
33732 this.grid.fireEvent('rowclass', this, rowcfg);
33733 alt.push(rowcfg.rowClass);
33735 rp.alt = alt.join(" ");
33736 lbuf+= rt.apply(rp);
33738 buf+= rt.apply(rp);
33740 return [lbuf, buf];
33742 function(cs, rs, ds, startRow, colCount, stripe){
33743 var ts = this.templates, ct = ts.cell, rt = ts.row;
33745 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33746 var hasListener = this.grid.hasListener('rowclass');
33749 for(var j = 0, len = rs.length; j < len; j++){
33750 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33751 for(var i = 0; i < colCount; i++){
33753 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33755 p.css = p.attr = "";
33756 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33757 if(p.value == undefined || p.value === "") {
33758 p.value = " ";
33762 p.css += ' x-grid-editable-cell';
33764 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33765 p.css += ' x-grid-dirty-cell'
33768 var markup = ct.apply(p);
33770 cb[cb.length] = markup;
33772 lcb[lcb.length] = markup;
33776 if(stripe && ((rowIndex+1) % 2 == 0)){
33777 alt.push( "x-grid-row-alt");
33780 alt.push(" x-grid-dirty-row");
33783 if(this.getRowClass){
33784 alt.push( this.getRowClass(r, rowIndex));
33790 rowIndex : rowIndex,
33793 this.grid.fireEvent('rowclass', this, rowcfg);
33794 alt.push(rowcfg.rowClass);
33797 rp.alt = alt.join(" ");
33798 rp.cells = lcb.join("");
33799 lbuf[lbuf.length] = rt.apply(rp);
33800 rp.cells = cb.join("");
33801 buf[buf.length] = rt.apply(rp);
33803 return [lbuf.join(""), buf.join("")];
33806 renderBody : function(){
33807 var markup = this.renderRows();
33808 var bt = this.templates.body;
33809 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33813 * Refreshes the grid
33814 * @param {Boolean} headersToo
33816 refresh : function(headersToo){
33817 this.fireEvent("beforerefresh", this);
33818 this.grid.stopEditing();
33819 var result = this.renderBody();
33820 this.lockedBody.update(result[0]);
33821 this.mainBody.update(result[1]);
33822 if(headersToo === true){
33823 this.updateHeaders();
33824 this.updateColumns();
33825 this.updateSplitters();
33826 this.updateHeaderSortState();
33828 this.syncRowHeights();
33830 this.fireEvent("refresh", this);
33833 handleColumnMove : function(cm, oldIndex, newIndex){
33834 this.indexMap = null;
33835 var s = this.getScrollState();
33836 this.refresh(true);
33837 this.restoreScroll(s);
33838 this.afterMove(newIndex);
33841 afterMove : function(colIndex){
33842 if(this.enableMoveAnim && Roo.enableFx){
33843 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33845 // if multisort - fix sortOrder, and reload..
33846 if (this.grid.dataSource.multiSort) {
33847 // the we can call sort again..
33848 var dm = this.grid.dataSource;
33849 var cm = this.grid.colModel;
33851 for(var i = 0; i < cm.config.length; i++ ) {
33853 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
33854 continue; // dont' bother, it's not in sort list or being set.
33857 so.push(cm.config[i].dataIndex);
33860 dm.load(dm.lastOptions);
33867 updateCell : function(dm, rowIndex, dataIndex){
33868 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33869 if(typeof colIndex == "undefined"){ // not present in grid
33872 var cm = this.grid.colModel;
33873 var cell = this.getCell(rowIndex, colIndex);
33874 var cellText = this.getCellText(rowIndex, colIndex);
33877 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33878 id : cm.getColumnId(colIndex),
33879 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33881 var renderer = cm.getRenderer(colIndex);
33882 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33883 if(typeof val == "undefined" || val === "") {
33886 cellText.innerHTML = val;
33887 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33888 this.syncRowHeights(rowIndex, rowIndex);
33891 calcColumnWidth : function(colIndex, maxRowsToMeasure){
33893 if(this.grid.autoSizeHeaders){
33894 var h = this.getHeaderCellMeasure(colIndex);
33895 maxWidth = Math.max(maxWidth, h.scrollWidth);
33898 if(this.cm.isLocked(colIndex)){
33899 tb = this.getLockedTable();
33902 tb = this.getBodyTable();
33903 index = colIndex - this.cm.getLockedCount();
33906 var rows = tb.rows;
33907 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33908 for(var i = 0; i < stopIndex; i++){
33909 var cell = rows[i].childNodes[index].firstChild;
33910 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33913 return maxWidth + /*margin for error in IE*/ 5;
33916 * Autofit a column to its content.
33917 * @param {Number} colIndex
33918 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33920 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33921 if(this.cm.isHidden(colIndex)){
33922 return; // can't calc a hidden column
33925 var cid = this.cm.getColumnId(colIndex);
33926 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33927 if(this.grid.autoSizeHeaders){
33928 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33931 var newWidth = this.calcColumnWidth(colIndex);
33932 this.cm.setColumnWidth(colIndex,
33933 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33934 if(!suppressEvent){
33935 this.grid.fireEvent("columnresize", colIndex, newWidth);
33940 * Autofits all columns to their content and then expands to fit any extra space in the grid
33942 autoSizeColumns : function(){
33943 var cm = this.grid.colModel;
33944 var colCount = cm.getColumnCount();
33945 for(var i = 0; i < colCount; i++){
33946 this.autoSizeColumn(i, true, true);
33948 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33951 this.updateColumns();
33957 * Autofits all columns to the grid's width proportionate with their current size
33958 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33960 fitColumns : function(reserveScrollSpace){
33961 var cm = this.grid.colModel;
33962 var colCount = cm.getColumnCount();
33966 for (i = 0; i < colCount; i++){
33967 if(!cm.isHidden(i) && !cm.isFixed(i)){
33968 w = cm.getColumnWidth(i);
33974 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33975 if(reserveScrollSpace){
33978 var frac = (avail - cm.getTotalWidth())/width;
33979 while (cols.length){
33982 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33984 this.updateColumns();
33988 onRowSelect : function(rowIndex){
33989 var row = this.getRowComposite(rowIndex);
33990 row.addClass("x-grid-row-selected");
33993 onRowDeselect : function(rowIndex){
33994 var row = this.getRowComposite(rowIndex);
33995 row.removeClass("x-grid-row-selected");
33998 onCellSelect : function(row, col){
33999 var cell = this.getCell(row, col);
34001 Roo.fly(cell).addClass("x-grid-cell-selected");
34005 onCellDeselect : function(row, col){
34006 var cell = this.getCell(row, col);
34008 Roo.fly(cell).removeClass("x-grid-cell-selected");
34012 updateHeaderSortState : function(){
34014 // sort state can be single { field: xxx, direction : yyy}
34015 // or { xxx=>ASC , yyy : DESC ..... }
34018 if (!this.ds.multiSort) {
34019 var state = this.ds.getSortState();
34023 mstate[state.field] = state.direction;
34024 // FIXME... - this is not used here.. but might be elsewhere..
34025 this.sortState = state;
34028 mstate = this.ds.sortToggle;
34030 //remove existing sort classes..
34032 var sc = this.sortClasses;
34033 var hds = this.el.select(this.headerSelector).removeClass(sc);
34035 for(var f in mstate) {
34037 var sortColumn = this.cm.findColumnIndex(f);
34039 if(sortColumn != -1){
34040 var sortDir = mstate[f];
34041 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34050 handleHeaderClick : function(g, index,e){
34052 Roo.log("header click");
34055 // touch events on header are handled by context
34056 this.handleHdCtx(g,index,e);
34061 if(this.headersDisabled){
34064 var dm = g.dataSource, cm = g.colModel;
34065 if(!cm.isSortable(index)){
34070 if (dm.multiSort) {
34071 // update the sortOrder
34073 for(var i = 0; i < cm.config.length; i++ ) {
34075 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34076 continue; // dont' bother, it's not in sort list or being set.
34079 so.push(cm.config[i].dataIndex);
34085 dm.sort(cm.getDataIndex(index));
34089 destroy : function(){
34091 this.colMenu.removeAll();
34092 Roo.menu.MenuMgr.unregister(this.colMenu);
34093 this.colMenu.getEl().remove();
34094 delete this.colMenu;
34097 this.hmenu.removeAll();
34098 Roo.menu.MenuMgr.unregister(this.hmenu);
34099 this.hmenu.getEl().remove();
34102 if(this.grid.enableColumnMove){
34103 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34105 for(var dd in dds){
34106 if(!dds[dd].config.isTarget && dds[dd].dragElId){
34107 var elid = dds[dd].dragElId;
34109 Roo.get(elid).remove();
34110 } else if(dds[dd].config.isTarget){
34111 dds[dd].proxyTop.remove();
34112 dds[dd].proxyBottom.remove();
34115 if(Roo.dd.DDM.locationCache[dd]){
34116 delete Roo.dd.DDM.locationCache[dd];
34119 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34122 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34123 this.bind(null, null);
34124 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34127 handleLockChange : function(){
34128 this.refresh(true);
34131 onDenyColumnLock : function(){
34135 onDenyColumnHide : function(){
34139 handleHdMenuClick : function(item){
34140 var index = this.hdCtxIndex;
34141 var cm = this.cm, ds = this.ds;
34144 ds.sort(cm.getDataIndex(index), "ASC");
34147 ds.sort(cm.getDataIndex(index), "DESC");
34150 var lc = cm.getLockedCount();
34151 if(cm.getColumnCount(true) <= lc+1){
34152 this.onDenyColumnLock();
34156 cm.setLocked(index, true, true);
34157 cm.moveColumn(index, lc);
34158 this.grid.fireEvent("columnmove", index, lc);
34160 cm.setLocked(index, true);
34164 var lc = cm.getLockedCount();
34165 if((lc-1) != index){
34166 cm.setLocked(index, false, true);
34167 cm.moveColumn(index, lc-1);
34168 this.grid.fireEvent("columnmove", index, lc-1);
34170 cm.setLocked(index, false);
34173 case 'wider': // used to expand cols on touch..
34175 var cw = cm.getColumnWidth(index);
34176 cw += (item.id == 'wider' ? 1 : -1) * 50;
34177 cw = Math.max(0, cw);
34178 cw = Math.min(cw,4000);
34179 cm.setColumnWidth(index, cw);
34183 index = cm.getIndexById(item.id.substr(4));
34185 if(item.checked && cm.getColumnCount(true) <= 1){
34186 this.onDenyColumnHide();
34189 cm.setHidden(index, item.checked);
34195 beforeColMenuShow : function(){
34196 var cm = this.cm, colCount = cm.getColumnCount();
34197 this.colMenu.removeAll();
34198 for(var i = 0; i < colCount; i++){
34199 this.colMenu.add(new Roo.menu.CheckItem({
34200 id: "col-"+cm.getColumnId(i),
34201 text: cm.getColumnHeader(i),
34202 checked: !cm.isHidden(i),
34208 handleHdCtx : function(g, index, e){
34210 var hd = this.getHeaderCell(index);
34211 this.hdCtxIndex = index;
34212 var ms = this.hmenu.items, cm = this.cm;
34213 ms.get("asc").setDisabled(!cm.isSortable(index));
34214 ms.get("desc").setDisabled(!cm.isSortable(index));
34215 if(this.grid.enableColLock !== false){
34216 ms.get("lock").setDisabled(cm.isLocked(index));
34217 ms.get("unlock").setDisabled(!cm.isLocked(index));
34219 this.hmenu.show(hd, "tl-bl");
34222 handleHdOver : function(e){
34223 var hd = this.findHeaderCell(e.getTarget());
34224 if(hd && !this.headersDisabled){
34225 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34226 this.fly(hd).addClass("x-grid-hd-over");
34231 handleHdOut : function(e){
34232 var hd = this.findHeaderCell(e.getTarget());
34234 this.fly(hd).removeClass("x-grid-hd-over");
34238 handleSplitDblClick : function(e, t){
34239 var i = this.getCellIndex(t);
34240 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34241 this.autoSizeColumn(i, true);
34246 render : function(){
34249 var colCount = cm.getColumnCount();
34251 if(this.grid.monitorWindowResize === true){
34252 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34254 var header = this.renderHeaders();
34255 var body = this.templates.body.apply({rows:""});
34256 var html = this.templates.master.apply({
34259 lockedHeader: header[0],
34263 //this.updateColumns();
34265 this.grid.getGridEl().dom.innerHTML = html;
34267 this.initElements();
34269 // a kludge to fix the random scolling effect in webkit
34270 this.el.on("scroll", function() {
34271 this.el.dom.scrollTop=0; // hopefully not recursive..
34274 this.scroller.on("scroll", this.handleScroll, this);
34275 this.lockedBody.on("mousewheel", this.handleWheel, this);
34276 this.mainBody.on("mousewheel", this.handleWheel, this);
34278 this.mainHd.on("mouseover", this.handleHdOver, this);
34279 this.mainHd.on("mouseout", this.handleHdOut, this);
34280 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34281 {delegate: "."+this.splitClass});
34283 this.lockedHd.on("mouseover", this.handleHdOver, this);
34284 this.lockedHd.on("mouseout", this.handleHdOut, this);
34285 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34286 {delegate: "."+this.splitClass});
34288 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34289 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34292 this.updateSplitters();
34294 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34295 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34296 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34299 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34300 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34302 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34303 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34305 if(this.grid.enableColLock !== false){
34306 this.hmenu.add('-',
34307 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34308 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34312 this.hmenu.add('-',
34313 {id:"wider", text: this.columnsWiderText},
34314 {id:"narrow", text: this.columnsNarrowText }
34320 if(this.grid.enableColumnHide !== false){
34322 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34323 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34324 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34326 this.hmenu.add('-',
34327 {id:"columns", text: this.columnsText, menu: this.colMenu}
34330 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34332 this.grid.on("headercontextmenu", this.handleHdCtx, this);
34335 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34336 this.dd = new Roo.grid.GridDragZone(this.grid, {
34337 ddGroup : this.grid.ddGroup || 'GridDD'
34343 for(var i = 0; i < colCount; i++){
34344 if(cm.isHidden(i)){
34345 this.hideColumn(i);
34347 if(cm.config[i].align){
34348 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34349 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34353 this.updateHeaderSortState();
34355 this.beforeInitialResize();
34358 // two part rendering gives faster view to the user
34359 this.renderPhase2.defer(1, this);
34362 renderPhase2 : function(){
34363 // render the rows now
34365 if(this.grid.autoSizeColumns){
34366 this.autoSizeColumns();
34370 beforeInitialResize : function(){
34374 onColumnSplitterMoved : function(i, w){
34375 this.userResized = true;
34376 var cm = this.grid.colModel;
34377 cm.setColumnWidth(i, w, true);
34378 var cid = cm.getColumnId(i);
34379 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34380 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34381 this.updateSplitters();
34383 this.grid.fireEvent("columnresize", i, w);
34386 syncRowHeights : function(startIndex, endIndex){
34387 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34388 startIndex = startIndex || 0;
34389 var mrows = this.getBodyTable().rows;
34390 var lrows = this.getLockedTable().rows;
34391 var len = mrows.length-1;
34392 endIndex = Math.min(endIndex || len, len);
34393 for(var i = startIndex; i <= endIndex; i++){
34394 var m = mrows[i], l = lrows[i];
34395 var h = Math.max(m.offsetHeight, l.offsetHeight);
34396 m.style.height = l.style.height = h + "px";
34401 layout : function(initialRender, is2ndPass){
34403 var auto = g.autoHeight;
34404 var scrollOffset = 16;
34405 var c = g.getGridEl(), cm = this.cm,
34406 expandCol = g.autoExpandColumn,
34408 //c.beginMeasure();
34410 if(!c.dom.offsetWidth){ // display:none?
34412 this.lockedWrap.show();
34413 this.mainWrap.show();
34418 var hasLock = this.cm.isLocked(0);
34420 var tbh = this.headerPanel.getHeight();
34421 var bbh = this.footerPanel.getHeight();
34424 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34425 var newHeight = ch + c.getBorderWidth("tb");
34427 newHeight = Math.min(g.maxHeight, newHeight);
34429 c.setHeight(newHeight);
34433 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34436 var s = this.scroller;
34438 var csize = c.getSize(true);
34440 this.el.setSize(csize.width, csize.height);
34442 this.headerPanel.setWidth(csize.width);
34443 this.footerPanel.setWidth(csize.width);
34445 var hdHeight = this.mainHd.getHeight();
34446 var vw = csize.width;
34447 var vh = csize.height - (tbh + bbh);
34451 var bt = this.getBodyTable();
34453 if(cm.getLockedCount() == cm.config.length){
34454 bt = this.getLockedTable();
34457 var ltWidth = hasLock ?
34458 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34460 var scrollHeight = bt.offsetHeight;
34461 var scrollWidth = ltWidth + bt.offsetWidth;
34462 var vscroll = false, hscroll = false;
34464 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34466 var lw = this.lockedWrap, mw = this.mainWrap;
34467 var lb = this.lockedBody, mb = this.mainBody;
34469 setTimeout(function(){
34470 var t = s.dom.offsetTop;
34471 var w = s.dom.clientWidth,
34472 h = s.dom.clientHeight;
34475 lw.setSize(ltWidth, h);
34477 mw.setLeftTop(ltWidth, t);
34478 mw.setSize(w-ltWidth, h);
34480 lb.setHeight(h-hdHeight);
34481 mb.setHeight(h-hdHeight);
34483 if(is2ndPass !== true && !gv.userResized && expandCol){
34484 // high speed resize without full column calculation
34486 var ci = cm.getIndexById(expandCol);
34488 ci = cm.findColumnIndex(expandCol);
34490 ci = Math.max(0, ci); // make sure it's got at least the first col.
34491 var expandId = cm.getColumnId(ci);
34492 var tw = cm.getTotalWidth(false);
34493 var currentWidth = cm.getColumnWidth(ci);
34494 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34495 if(currentWidth != cw){
34496 cm.setColumnWidth(ci, cw, true);
34497 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34498 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34499 gv.updateSplitters();
34500 gv.layout(false, true);
34512 onWindowResize : function(){
34513 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34519 appendFooter : function(parentEl){
34523 sortAscText : "Sort Ascending",
34524 sortDescText : "Sort Descending",
34525 lockText : "Lock Column",
34526 unlockText : "Unlock Column",
34527 columnsText : "Columns",
34529 columnsWiderText : "Wider",
34530 columnsNarrowText : "Thinner"
34534 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34535 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34536 this.proxy.el.addClass('x-grid3-col-dd');
34539 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34540 handleMouseDown : function(e){
34544 callHandleMouseDown : function(e){
34545 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34550 * Ext JS Library 1.1.1
34551 * Copyright(c) 2006-2007, Ext JS, LLC.
34553 * Originally Released Under LGPL - original licence link has changed is not relivant.
34556 * <script type="text/javascript">
34560 // This is a support class used internally by the Grid components
34561 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34563 this.view = grid.getView();
34564 this.proxy = this.view.resizeProxy;
34565 Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34566 "gridSplitters" + this.grid.getGridEl().id, {
34567 dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34569 this.setHandleElId(Roo.id(hd));
34570 this.setOuterHandleElId(Roo.id(hd2));
34571 this.scroll = false;
34573 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34574 fly: Roo.Element.fly,
34576 b4StartDrag : function(x, y){
34577 this.view.headersDisabled = true;
34578 this.proxy.setHeight(this.view.mainWrap.getHeight());
34579 var w = this.cm.getColumnWidth(this.cellIndex);
34580 var minw = Math.max(w-this.grid.minColumnWidth, 0);
34581 this.resetConstraints();
34582 this.setXConstraint(minw, 1000);
34583 this.setYConstraint(0, 0);
34584 this.minX = x - minw;
34585 this.maxX = x + 1000;
34587 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34591 handleMouseDown : function(e){
34592 ev = Roo.EventObject.setEvent(e);
34593 var t = this.fly(ev.getTarget());
34594 if(t.hasClass("x-grid-split")){
34595 this.cellIndex = this.view.getCellIndex(t.dom);
34596 this.split = t.dom;
34597 this.cm = this.grid.colModel;
34598 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34599 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34604 endDrag : function(e){
34605 this.view.headersDisabled = false;
34606 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34607 var diff = endX - this.startPos;
34608 this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34611 autoOffset : function(){
34612 this.setDelta(0,0);
34616 * Ext JS Library 1.1.1
34617 * Copyright(c) 2006-2007, Ext JS, LLC.
34619 * Originally Released Under LGPL - original licence link has changed is not relivant.
34622 * <script type="text/javascript">
34626 // This is a support class used internally by the Grid components
34627 Roo.grid.GridDragZone = function(grid, config){
34628 this.view = grid.getView();
34629 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34630 if(this.view.lockedBody){
34631 this.setHandleElId(Roo.id(this.view.mainBody.dom));
34632 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34634 this.scroll = false;
34636 this.ddel = document.createElement('div');
34637 this.ddel.className = 'x-grid-dd-wrap';
34640 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34641 ddGroup : "GridDD",
34643 getDragData : function(e){
34644 var t = Roo.lib.Event.getTarget(e);
34645 var rowIndex = this.view.findRowIndex(t);
34646 var sm = this.grid.selModel;
34648 //Roo.log(rowIndex);
34650 if (sm.getSelectedCell) {
34651 // cell selection..
34652 if (!sm.getSelectedCell()) {
34655 if (rowIndex != sm.getSelectedCell()[0]) {
34661 if(rowIndex !== false){
34666 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
34668 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34671 if (e.hasModifier()){
34672 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34675 Roo.log("getDragData");
34680 rowIndex: rowIndex,
34681 selections:sm.getSelections ? sm.getSelections() : (
34682 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
34689 onInitDrag : function(e){
34690 var data = this.dragData;
34691 this.ddel.innerHTML = this.grid.getDragDropText();
34692 this.proxy.update(this.ddel);
34693 // fire start drag?
34696 afterRepair : function(){
34697 this.dragging = false;
34700 getRepairXY : function(e, data){
34704 onEndDrag : function(data, e){
34708 onValidDrop : function(dd, e, id){
34713 beforeInvalidDrop : function(e, id){
34718 * Ext JS Library 1.1.1
34719 * Copyright(c) 2006-2007, Ext JS, LLC.
34721 * Originally Released Under LGPL - original licence link has changed is not relivant.
34724 * <script type="text/javascript">
34729 * @class Roo.grid.ColumnModel
34730 * @extends Roo.util.Observable
34731 * This is the default implementation of a ColumnModel used by the Grid. It defines
34732 * the columns in the grid.
34735 var colModel = new Roo.grid.ColumnModel([
34736 {header: "Ticker", width: 60, sortable: true, locked: true},
34737 {header: "Company Name", width: 150, sortable: true},
34738 {header: "Market Cap.", width: 100, sortable: true},
34739 {header: "$ Sales", width: 100, sortable: true, renderer: money},
34740 {header: "Employees", width: 100, sortable: true, resizable: false}
34745 * The config options listed for this class are options which may appear in each
34746 * individual column definition.
34747 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34749 * @param {Object} config An Array of column config objects. See this class's
34750 * config objects for details.
34752 Roo.grid.ColumnModel = function(config){
34754 * The config passed into the constructor
34756 this.config = config;
34759 // if no id, create one
34760 // if the column does not have a dataIndex mapping,
34761 // map it to the order it is in the config
34762 for(var i = 0, len = config.length; i < len; i++){
34764 if(typeof c.dataIndex == "undefined"){
34767 if(typeof c.renderer == "string"){
34768 c.renderer = Roo.util.Format[c.renderer];
34770 if(typeof c.id == "undefined"){
34773 if(c.editor && c.editor.xtype){
34774 c.editor = Roo.factory(c.editor, Roo.grid);
34776 if(c.editor && c.editor.isFormField){
34777 c.editor = new Roo.grid.GridEditor(c.editor);
34779 this.lookup[c.id] = c;
34783 * The width of columns which have no width specified (defaults to 100)
34786 this.defaultWidth = 100;
34789 * Default sortable of columns which have no sortable specified (defaults to false)
34792 this.defaultSortable = false;
34796 * @event widthchange
34797 * Fires when the width of a column changes.
34798 * @param {ColumnModel} this
34799 * @param {Number} columnIndex The column index
34800 * @param {Number} newWidth The new width
34802 "widthchange": true,
34804 * @event headerchange
34805 * Fires when the text of a header changes.
34806 * @param {ColumnModel} this
34807 * @param {Number} columnIndex The column index
34808 * @param {Number} newText The new header text
34810 "headerchange": true,
34812 * @event hiddenchange
34813 * Fires when a column is hidden or "unhidden".
34814 * @param {ColumnModel} this
34815 * @param {Number} columnIndex The column index
34816 * @param {Boolean} hidden true if hidden, false otherwise
34818 "hiddenchange": true,
34820 * @event columnmoved
34821 * Fires when a column is moved.
34822 * @param {ColumnModel} this
34823 * @param {Number} oldIndex
34824 * @param {Number} newIndex
34826 "columnmoved" : true,
34828 * @event columlockchange
34829 * Fires when a column's locked state is changed
34830 * @param {ColumnModel} this
34831 * @param {Number} colIndex
34832 * @param {Boolean} locked true if locked
34834 "columnlockchange" : true
34836 Roo.grid.ColumnModel.superclass.constructor.call(this);
34838 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34840 * @cfg {String} header The header text to display in the Grid view.
34843 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34844 * {@link Roo.data.Record} definition from which to draw the column's value. If not
34845 * specified, the column's index is used as an index into the Record's data Array.
34848 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34849 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34852 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34853 * Defaults to the value of the {@link #defaultSortable} property.
34854 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34857 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
34860 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
34863 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34866 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34869 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34870 * given the cell's data value. See {@link #setRenderer}. If not specified, the
34871 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
34872 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
34875 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
34878 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
34881 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
34884 * @cfg {String} cursor (Optional)
34887 * @cfg {String} tooltip (Optional)
34890 * @cfg {Number} xs (Optional)
34893 * @cfg {Number} sm (Optional)
34896 * @cfg {Number} md (Optional)
34899 * @cfg {Number} lg (Optional)
34902 * Returns the id of the column at the specified index.
34903 * @param {Number} index The column index
34904 * @return {String} the id
34906 getColumnId : function(index){
34907 return this.config[index].id;
34911 * Returns the column for a specified id.
34912 * @param {String} id The column id
34913 * @return {Object} the column
34915 getColumnById : function(id){
34916 return this.lookup[id];
34921 * Returns the column for a specified dataIndex.
34922 * @param {String} dataIndex The column dataIndex
34923 * @return {Object|Boolean} the column or false if not found
34925 getColumnByDataIndex: function(dataIndex){
34926 var index = this.findColumnIndex(dataIndex);
34927 return index > -1 ? this.config[index] : false;
34931 * Returns the index for a specified column id.
34932 * @param {String} id The column id
34933 * @return {Number} the index, or -1 if not found
34935 getIndexById : function(id){
34936 for(var i = 0, len = this.config.length; i < len; i++){
34937 if(this.config[i].id == id){
34945 * Returns the index for a specified column dataIndex.
34946 * @param {String} dataIndex The column dataIndex
34947 * @return {Number} the index, or -1 if not found
34950 findColumnIndex : function(dataIndex){
34951 for(var i = 0, len = this.config.length; i < len; i++){
34952 if(this.config[i].dataIndex == dataIndex){
34960 moveColumn : function(oldIndex, newIndex){
34961 var c = this.config[oldIndex];
34962 this.config.splice(oldIndex, 1);
34963 this.config.splice(newIndex, 0, c);
34964 this.dataMap = null;
34965 this.fireEvent("columnmoved", this, oldIndex, newIndex);
34968 isLocked : function(colIndex){
34969 return this.config[colIndex].locked === true;
34972 setLocked : function(colIndex, value, suppressEvent){
34973 if(this.isLocked(colIndex) == value){
34976 this.config[colIndex].locked = value;
34977 if(!suppressEvent){
34978 this.fireEvent("columnlockchange", this, colIndex, value);
34982 getTotalLockedWidth : function(){
34983 var totalWidth = 0;
34984 for(var i = 0; i < this.config.length; i++){
34985 if(this.isLocked(i) && !this.isHidden(i)){
34986 this.totalWidth += this.getColumnWidth(i);
34992 getLockedCount : function(){
34993 for(var i = 0, len = this.config.length; i < len; i++){
34994 if(!this.isLocked(i)){
34999 return this.config.length;
35003 * Returns the number of columns.
35006 getColumnCount : function(visibleOnly){
35007 if(visibleOnly === true){
35009 for(var i = 0, len = this.config.length; i < len; i++){
35010 if(!this.isHidden(i)){
35016 return this.config.length;
35020 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35021 * @param {Function} fn
35022 * @param {Object} scope (optional)
35023 * @return {Array} result
35025 getColumnsBy : function(fn, scope){
35027 for(var i = 0, len = this.config.length; i < len; i++){
35028 var c = this.config[i];
35029 if(fn.call(scope||this, c, i) === true){
35037 * Returns true if the specified column is sortable.
35038 * @param {Number} col The column index
35039 * @return {Boolean}
35041 isSortable : function(col){
35042 if(typeof this.config[col].sortable == "undefined"){
35043 return this.defaultSortable;
35045 return this.config[col].sortable;
35049 * Returns the rendering (formatting) function defined for the column.
35050 * @param {Number} col The column index.
35051 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35053 getRenderer : function(col){
35054 if(!this.config[col].renderer){
35055 return Roo.grid.ColumnModel.defaultRenderer;
35057 return this.config[col].renderer;
35061 * Sets the rendering (formatting) function for a column.
35062 * @param {Number} col The column index
35063 * @param {Function} fn The function to use to process the cell's raw data
35064 * to return HTML markup for the grid view. The render function is called with
35065 * the following parameters:<ul>
35066 * <li>Data value.</li>
35067 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35068 * <li>css A CSS style string to apply to the table cell.</li>
35069 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35070 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35071 * <li>Row index</li>
35072 * <li>Column index</li>
35073 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35075 setRenderer : function(col, fn){
35076 this.config[col].renderer = fn;
35080 * Returns the width for the specified column.
35081 * @param {Number} col The column index
35084 getColumnWidth : function(col){
35085 return this.config[col].width * 1 || this.defaultWidth;
35089 * Sets the width for a column.
35090 * @param {Number} col The column index
35091 * @param {Number} width The new width
35093 setColumnWidth : function(col, width, suppressEvent){
35094 this.config[col].width = width;
35095 this.totalWidth = null;
35096 if(!suppressEvent){
35097 this.fireEvent("widthchange", this, col, width);
35102 * Returns the total width of all columns.
35103 * @param {Boolean} includeHidden True to include hidden column widths
35106 getTotalWidth : function(includeHidden){
35107 if(!this.totalWidth){
35108 this.totalWidth = 0;
35109 for(var i = 0, len = this.config.length; i < len; i++){
35110 if(includeHidden || !this.isHidden(i)){
35111 this.totalWidth += this.getColumnWidth(i);
35115 return this.totalWidth;
35119 * Returns the header for the specified column.
35120 * @param {Number} col The column index
35123 getColumnHeader : function(col){
35124 return this.config[col].header;
35128 * Sets the header for a column.
35129 * @param {Number} col The column index
35130 * @param {String} header The new header
35132 setColumnHeader : function(col, header){
35133 this.config[col].header = header;
35134 this.fireEvent("headerchange", this, col, header);
35138 * Returns the tooltip for the specified column.
35139 * @param {Number} col The column index
35142 getColumnTooltip : function(col){
35143 return this.config[col].tooltip;
35146 * Sets the tooltip for a column.
35147 * @param {Number} col The column index
35148 * @param {String} tooltip The new tooltip
35150 setColumnTooltip : function(col, tooltip){
35151 this.config[col].tooltip = tooltip;
35155 * Returns the dataIndex for the specified column.
35156 * @param {Number} col The column index
35159 getDataIndex : function(col){
35160 return this.config[col].dataIndex;
35164 * Sets the dataIndex for a column.
35165 * @param {Number} col The column index
35166 * @param {Number} dataIndex The new dataIndex
35168 setDataIndex : function(col, dataIndex){
35169 this.config[col].dataIndex = dataIndex;
35175 * Returns true if the cell is editable.
35176 * @param {Number} colIndex The column index
35177 * @param {Number} rowIndex The row index - this is nto actually used..?
35178 * @return {Boolean}
35180 isCellEditable : function(colIndex, rowIndex){
35181 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35185 * Returns the editor defined for the cell/column.
35186 * return false or null to disable editing.
35187 * @param {Number} colIndex The column index
35188 * @param {Number} rowIndex The row index
35191 getCellEditor : function(colIndex, rowIndex){
35192 return this.config[colIndex].editor;
35196 * Sets if a column is editable.
35197 * @param {Number} col The column index
35198 * @param {Boolean} editable True if the column is editable
35200 setEditable : function(col, editable){
35201 this.config[col].editable = editable;
35206 * Returns true if the column is hidden.
35207 * @param {Number} colIndex The column index
35208 * @return {Boolean}
35210 isHidden : function(colIndex){
35211 return this.config[colIndex].hidden;
35216 * Returns true if the column width cannot be changed
35218 isFixed : function(colIndex){
35219 return this.config[colIndex].fixed;
35223 * Returns true if the column can be resized
35224 * @return {Boolean}
35226 isResizable : function(colIndex){
35227 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35230 * Sets if a column is hidden.
35231 * @param {Number} colIndex The column index
35232 * @param {Boolean} hidden True if the column is hidden
35234 setHidden : function(colIndex, hidden){
35235 this.config[colIndex].hidden = hidden;
35236 this.totalWidth = null;
35237 this.fireEvent("hiddenchange", this, colIndex, hidden);
35241 * Sets the editor for a column.
35242 * @param {Number} col The column index
35243 * @param {Object} editor The editor object
35245 setEditor : function(col, editor){
35246 this.config[col].editor = editor;
35250 Roo.grid.ColumnModel.defaultRenderer = function(value)
35252 if(typeof value == "object") {
35255 if(typeof value == "string" && value.length < 1){
35259 return String.format("{0}", value);
35262 // Alias for backwards compatibility
35263 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35266 * Ext JS Library 1.1.1
35267 * Copyright(c) 2006-2007, Ext JS, LLC.
35269 * Originally Released Under LGPL - original licence link has changed is not relivant.
35272 * <script type="text/javascript">
35276 * @class Roo.grid.AbstractSelectionModel
35277 * @extends Roo.util.Observable
35278 * Abstract base class for grid SelectionModels. It provides the interface that should be
35279 * implemented by descendant classes. This class should not be directly instantiated.
35282 Roo.grid.AbstractSelectionModel = function(){
35283 this.locked = false;
35284 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35287 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
35288 /** @ignore Called by the grid automatically. Do not call directly. */
35289 init : function(grid){
35295 * Locks the selections.
35298 this.locked = true;
35302 * Unlocks the selections.
35304 unlock : function(){
35305 this.locked = false;
35309 * Returns true if the selections are locked.
35310 * @return {Boolean}
35312 isLocked : function(){
35313 return this.locked;
35317 * Ext JS Library 1.1.1
35318 * Copyright(c) 2006-2007, Ext JS, LLC.
35320 * Originally Released Under LGPL - original licence link has changed is not relivant.
35323 * <script type="text/javascript">
35326 * @extends Roo.grid.AbstractSelectionModel
35327 * @class Roo.grid.RowSelectionModel
35328 * The default SelectionModel used by {@link Roo.grid.Grid}.
35329 * It supports multiple selections and keyboard selection/navigation.
35331 * @param {Object} config
35333 Roo.grid.RowSelectionModel = function(config){
35334 Roo.apply(this, config);
35335 this.selections = new Roo.util.MixedCollection(false, function(o){
35340 this.lastActive = false;
35344 * @event selectionchange
35345 * Fires when the selection changes
35346 * @param {SelectionModel} this
35348 "selectionchange" : true,
35350 * @event afterselectionchange
35351 * Fires after the selection changes (eg. by key press or clicking)
35352 * @param {SelectionModel} this
35354 "afterselectionchange" : true,
35356 * @event beforerowselect
35357 * Fires when a row is selected being selected, return false to cancel.
35358 * @param {SelectionModel} this
35359 * @param {Number} rowIndex The selected index
35360 * @param {Boolean} keepExisting False if other selections will be cleared
35362 "beforerowselect" : true,
35365 * Fires when a row is selected.
35366 * @param {SelectionModel} this
35367 * @param {Number} rowIndex The selected index
35368 * @param {Roo.data.Record} r The record
35370 "rowselect" : true,
35372 * @event rowdeselect
35373 * Fires when a row is deselected.
35374 * @param {SelectionModel} this
35375 * @param {Number} rowIndex The selected index
35377 "rowdeselect" : true
35379 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35380 this.locked = false;
35383 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
35385 * @cfg {Boolean} singleSelect
35386 * True to allow selection of only one row at a time (defaults to false)
35388 singleSelect : false,
35391 initEvents : function(){
35393 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35394 this.grid.on("mousedown", this.handleMouseDown, this);
35395 }else{ // allow click to work like normal
35396 this.grid.on("rowclick", this.handleDragableRowClick, this);
35399 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35400 "up" : function(e){
35402 this.selectPrevious(e.shiftKey);
35403 }else if(this.last !== false && this.lastActive !== false){
35404 var last = this.last;
35405 this.selectRange(this.last, this.lastActive-1);
35406 this.grid.getView().focusRow(this.lastActive);
35407 if(last !== false){
35411 this.selectFirstRow();
35413 this.fireEvent("afterselectionchange", this);
35415 "down" : function(e){
35417 this.selectNext(e.shiftKey);
35418 }else if(this.last !== false && this.lastActive !== false){
35419 var last = this.last;
35420 this.selectRange(this.last, this.lastActive+1);
35421 this.grid.getView().focusRow(this.lastActive);
35422 if(last !== false){
35426 this.selectFirstRow();
35428 this.fireEvent("afterselectionchange", this);
35433 var view = this.grid.view;
35434 view.on("refresh", this.onRefresh, this);
35435 view.on("rowupdated", this.onRowUpdated, this);
35436 view.on("rowremoved", this.onRemove, this);
35440 onRefresh : function(){
35441 var ds = this.grid.dataSource, i, v = this.grid.view;
35442 var s = this.selections;
35443 s.each(function(r){
35444 if((i = ds.indexOfId(r.id)) != -1){
35446 s.add(ds.getAt(i)); // updating the selection relate data
35454 onRemove : function(v, index, r){
35455 this.selections.remove(r);
35459 onRowUpdated : function(v, index, r){
35460 if(this.isSelected(r)){
35461 v.onRowSelect(index);
35467 * @param {Array} records The records to select
35468 * @param {Boolean} keepExisting (optional) True to keep existing selections
35470 selectRecords : function(records, keepExisting){
35472 this.clearSelections();
35474 var ds = this.grid.dataSource;
35475 for(var i = 0, len = records.length; i < len; i++){
35476 this.selectRow(ds.indexOf(records[i]), true);
35481 * Gets the number of selected rows.
35484 getCount : function(){
35485 return this.selections.length;
35489 * Selects the first row in the grid.
35491 selectFirstRow : function(){
35496 * Select the last row.
35497 * @param {Boolean} keepExisting (optional) True to keep existing selections
35499 selectLastRow : function(keepExisting){
35500 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35504 * Selects the row immediately following the last selected row.
35505 * @param {Boolean} keepExisting (optional) True to keep existing selections
35507 selectNext : function(keepExisting){
35508 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35509 this.selectRow(this.last+1, keepExisting);
35510 this.grid.getView().focusRow(this.last);
35515 * Selects the row that precedes the last selected row.
35516 * @param {Boolean} keepExisting (optional) True to keep existing selections
35518 selectPrevious : function(keepExisting){
35520 this.selectRow(this.last-1, keepExisting);
35521 this.grid.getView().focusRow(this.last);
35526 * Returns the selected records
35527 * @return {Array} Array of selected records
35529 getSelections : function(){
35530 return [].concat(this.selections.items);
35534 * Returns the first selected record.
35537 getSelected : function(){
35538 return this.selections.itemAt(0);
35543 * Clears all selections.
35545 clearSelections : function(fast){
35550 var ds = this.grid.dataSource;
35551 var s = this.selections;
35552 s.each(function(r){
35553 this.deselectRow(ds.indexOfId(r.id));
35557 this.selections.clear();
35564 * Selects all rows.
35566 selectAll : function(){
35570 this.selections.clear();
35571 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35572 this.selectRow(i, true);
35577 * Returns True if there is a selection.
35578 * @return {Boolean}
35580 hasSelection : function(){
35581 return this.selections.length > 0;
35585 * Returns True if the specified row is selected.
35586 * @param {Number/Record} record The record or index of the record to check
35587 * @return {Boolean}
35589 isSelected : function(index){
35590 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35591 return (r && this.selections.key(r.id) ? true : false);
35595 * Returns True if the specified record id is selected.
35596 * @param {String} id The id of record to check
35597 * @return {Boolean}
35599 isIdSelected : function(id){
35600 return (this.selections.key(id) ? true : false);
35604 handleMouseDown : function(e, t){
35605 var view = this.grid.getView(), rowIndex;
35606 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35609 if(e.shiftKey && this.last !== false){
35610 var last = this.last;
35611 this.selectRange(last, rowIndex, e.ctrlKey);
35612 this.last = last; // reset the last
35613 view.focusRow(rowIndex);
35615 var isSelected = this.isSelected(rowIndex);
35616 if(e.button !== 0 && isSelected){
35617 view.focusRow(rowIndex);
35618 }else if(e.ctrlKey && isSelected){
35619 this.deselectRow(rowIndex);
35620 }else if(!isSelected){
35621 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35622 view.focusRow(rowIndex);
35625 this.fireEvent("afterselectionchange", this);
35628 handleDragableRowClick : function(grid, rowIndex, e)
35630 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35631 this.selectRow(rowIndex, false);
35632 grid.view.focusRow(rowIndex);
35633 this.fireEvent("afterselectionchange", this);
35638 * Selects multiple rows.
35639 * @param {Array} rows Array of the indexes of the row to select
35640 * @param {Boolean} keepExisting (optional) True to keep existing selections
35642 selectRows : function(rows, keepExisting){
35644 this.clearSelections();
35646 for(var i = 0, len = rows.length; i < len; i++){
35647 this.selectRow(rows[i], true);
35652 * Selects a range of rows. All rows in between startRow and endRow are also selected.
35653 * @param {Number} startRow The index of the first row in the range
35654 * @param {Number} endRow The index of the last row in the range
35655 * @param {Boolean} keepExisting (optional) True to retain existing selections
35657 selectRange : function(startRow, endRow, keepExisting){
35662 this.clearSelections();
35664 if(startRow <= endRow){
35665 for(var i = startRow; i <= endRow; i++){
35666 this.selectRow(i, true);
35669 for(var i = startRow; i >= endRow; i--){
35670 this.selectRow(i, true);
35676 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35677 * @param {Number} startRow The index of the first row in the range
35678 * @param {Number} endRow The index of the last row in the range
35680 deselectRange : function(startRow, endRow, preventViewNotify){
35684 for(var i = startRow; i <= endRow; i++){
35685 this.deselectRow(i, preventViewNotify);
35691 * @param {Number} row The index of the row to select
35692 * @param {Boolean} keepExisting (optional) True to keep existing selections
35694 selectRow : function(index, keepExisting, preventViewNotify){
35695 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
35698 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35699 if(!keepExisting || this.singleSelect){
35700 this.clearSelections();
35702 var r = this.grid.dataSource.getAt(index);
35703 this.selections.add(r);
35704 this.last = this.lastActive = index;
35705 if(!preventViewNotify){
35706 this.grid.getView().onRowSelect(index);
35708 this.fireEvent("rowselect", this, index, r);
35709 this.fireEvent("selectionchange", this);
35715 * @param {Number} row The index of the row to deselect
35717 deselectRow : function(index, preventViewNotify){
35721 if(this.last == index){
35724 if(this.lastActive == index){
35725 this.lastActive = false;
35727 var r = this.grid.dataSource.getAt(index);
35728 this.selections.remove(r);
35729 if(!preventViewNotify){
35730 this.grid.getView().onRowDeselect(index);
35732 this.fireEvent("rowdeselect", this, index);
35733 this.fireEvent("selectionchange", this);
35737 restoreLast : function(){
35739 this.last = this._last;
35744 acceptsNav : function(row, col, cm){
35745 return !cm.isHidden(col) && cm.isCellEditable(col, row);
35749 onEditorKey : function(field, e){
35750 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35755 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35757 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35759 }else if(k == e.ENTER && !e.ctrlKey){
35763 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35765 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35767 }else if(k == e.ESC){
35771 g.startEditing(newCell[0], newCell[1]);
35776 * Ext JS Library 1.1.1
35777 * Copyright(c) 2006-2007, Ext JS, LLC.
35779 * Originally Released Under LGPL - original licence link has changed is not relivant.
35782 * <script type="text/javascript">
35785 * @class Roo.grid.CellSelectionModel
35786 * @extends Roo.grid.AbstractSelectionModel
35787 * This class provides the basic implementation for cell selection in a grid.
35789 * @param {Object} config The object containing the configuration of this model.
35790 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
35792 Roo.grid.CellSelectionModel = function(config){
35793 Roo.apply(this, config);
35795 this.selection = null;
35799 * @event beforerowselect
35800 * Fires before a cell is selected.
35801 * @param {SelectionModel} this
35802 * @param {Number} rowIndex The selected row index
35803 * @param {Number} colIndex The selected cell index
35805 "beforecellselect" : true,
35807 * @event cellselect
35808 * Fires when a cell is selected.
35809 * @param {SelectionModel} this
35810 * @param {Number} rowIndex The selected row index
35811 * @param {Number} colIndex The selected cell index
35813 "cellselect" : true,
35815 * @event selectionchange
35816 * Fires when the active selection changes.
35817 * @param {SelectionModel} this
35818 * @param {Object} selection null for no selection or an object (o) with two properties
35820 <li>o.record: the record object for the row the selection is in</li>
35821 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35824 "selectionchange" : true,
35827 * Fires when the tab (or enter) was pressed on the last editable cell
35828 * You can use this to trigger add new row.
35829 * @param {SelectionModel} this
35833 * @event beforeeditnext
35834 * Fires before the next editable sell is made active
35835 * You can use this to skip to another cell or fire the tabend
35836 * if you set cell to false
35837 * @param {Object} eventdata object : { cell : [ row, col ] }
35839 "beforeeditnext" : true
35841 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35844 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
35846 enter_is_tab: false,
35849 initEvents : function(){
35850 this.grid.on("mousedown", this.handleMouseDown, this);
35851 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35852 var view = this.grid.view;
35853 view.on("refresh", this.onViewChange, this);
35854 view.on("rowupdated", this.onRowUpdated, this);
35855 view.on("beforerowremoved", this.clearSelections, this);
35856 view.on("beforerowsinserted", this.clearSelections, this);
35857 if(this.grid.isEditor){
35858 this.grid.on("beforeedit", this.beforeEdit, this);
35863 beforeEdit : function(e){
35864 this.select(e.row, e.column, false, true, e.record);
35868 onRowUpdated : function(v, index, r){
35869 if(this.selection && this.selection.record == r){
35870 v.onCellSelect(index, this.selection.cell[1]);
35875 onViewChange : function(){
35876 this.clearSelections(true);
35880 * Returns the currently selected cell,.
35881 * @return {Array} The selected cell (row, column) or null if none selected.
35883 getSelectedCell : function(){
35884 return this.selection ? this.selection.cell : null;
35888 * Clears all selections.
35889 * @param {Boolean} true to prevent the gridview from being notified about the change.
35891 clearSelections : function(preventNotify){
35892 var s = this.selection;
35894 if(preventNotify !== true){
35895 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35897 this.selection = null;
35898 this.fireEvent("selectionchange", this, null);
35903 * Returns true if there is a selection.
35904 * @return {Boolean}
35906 hasSelection : function(){
35907 return this.selection ? true : false;
35911 handleMouseDown : function(e, t){
35912 var v = this.grid.getView();
35913 if(this.isLocked()){
35916 var row = v.findRowIndex(t);
35917 var cell = v.findCellIndex(t);
35918 if(row !== false && cell !== false){
35919 this.select(row, cell);
35925 * @param {Number} rowIndex
35926 * @param {Number} collIndex
35928 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35929 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35930 this.clearSelections();
35931 r = r || this.grid.dataSource.getAt(rowIndex);
35934 cell : [rowIndex, colIndex]
35936 if(!preventViewNotify){
35937 var v = this.grid.getView();
35938 v.onCellSelect(rowIndex, colIndex);
35939 if(preventFocus !== true){
35940 v.focusCell(rowIndex, colIndex);
35943 this.fireEvent("cellselect", this, rowIndex, colIndex);
35944 this.fireEvent("selectionchange", this, this.selection);
35949 isSelectable : function(rowIndex, colIndex, cm){
35950 return !cm.isHidden(colIndex);
35954 handleKeyDown : function(e){
35955 //Roo.log('Cell Sel Model handleKeyDown');
35956 if(!e.isNavKeyPress()){
35959 var g = this.grid, s = this.selection;
35962 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
35964 this.select(cell[0], cell[1]);
35969 var walk = function(row, col, step){
35970 return g.walkCells(row, col, step, sm.isSelectable, sm);
35972 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35979 // handled by onEditorKey
35980 if (g.isEditor && g.editing) {
35984 newCell = walk(r, c-1, -1);
35986 newCell = walk(r, c+1, 1);
35991 newCell = walk(r+1, c, 1);
35995 newCell = walk(r-1, c, -1);
35999 newCell = walk(r, c+1, 1);
36003 newCell = walk(r, c-1, -1);
36008 if(g.isEditor && !g.editing){
36009 g.startEditing(r, c);
36018 this.select(newCell[0], newCell[1]);
36024 acceptsNav : function(row, col, cm){
36025 return !cm.isHidden(col) && cm.isCellEditable(col, row);
36029 * @param {Number} field (not used) - as it's normally used as a listener
36030 * @param {Number} e - event - fake it by using
36032 * var e = Roo.EventObjectImpl.prototype;
36033 * e.keyCode = e.TAB
36037 onEditorKey : function(field, e){
36039 var k = e.getKey(),
36042 ed = g.activeEditor,
36044 ///Roo.log('onEditorKey' + k);
36047 if (this.enter_is_tab && k == e.ENTER) {
36053 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36055 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36061 } else if(k == e.ENTER && !e.ctrlKey){
36064 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36066 } else if(k == e.ESC){
36071 var ecall = { cell : newCell, forward : forward };
36072 this.fireEvent('beforeeditnext', ecall );
36073 newCell = ecall.cell;
36074 forward = ecall.forward;
36078 //Roo.log('next cell after edit');
36079 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36080 } else if (forward) {
36081 // tabbed past last
36082 this.fireEvent.defer(100, this, ['tabend',this]);
36087 * Ext JS Library 1.1.1
36088 * Copyright(c) 2006-2007, Ext JS, LLC.
36090 * Originally Released Under LGPL - original licence link has changed is not relivant.
36093 * <script type="text/javascript">
36097 * @class Roo.grid.EditorGrid
36098 * @extends Roo.grid.Grid
36099 * Class for creating and editable grid.
36100 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36101 * The container MUST have some type of size defined for the grid to fill. The container will be
36102 * automatically set to position relative if it isn't already.
36103 * @param {Object} dataSource The data model to bind to
36104 * @param {Object} colModel The column model with info about this grid's columns
36106 Roo.grid.EditorGrid = function(container, config){
36107 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36108 this.getGridEl().addClass("xedit-grid");
36110 if(!this.selModel){
36111 this.selModel = new Roo.grid.CellSelectionModel();
36114 this.activeEditor = null;
36118 * @event beforeedit
36119 * Fires before cell editing is triggered. The edit event object has the following properties <br />
36120 * <ul style="padding:5px;padding-left:16px;">
36121 * <li>grid - This grid</li>
36122 * <li>record - The record being edited</li>
36123 * <li>field - The field name being edited</li>
36124 * <li>value - The value for the field being edited.</li>
36125 * <li>row - The grid row index</li>
36126 * <li>column - The grid column index</li>
36127 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36129 * @param {Object} e An edit event (see above for description)
36131 "beforeedit" : true,
36134 * Fires after a cell is edited. <br />
36135 * <ul style="padding:5px;padding-left:16px;">
36136 * <li>grid - This grid</li>
36137 * <li>record - The record being edited</li>
36138 * <li>field - The field name being edited</li>
36139 * <li>value - The value being set</li>
36140 * <li>originalValue - The original value for the field, before the edit.</li>
36141 * <li>row - The grid row index</li>
36142 * <li>column - The grid column index</li>
36144 * @param {Object} e An edit event (see above for description)
36146 "afteredit" : true,
36148 * @event validateedit
36149 * Fires after a cell is edited, but before the value is set in the record.
36150 * You can use this to modify the value being set in the field, Return false
36151 * to cancel the change. The edit event object has the following properties <br />
36152 * <ul style="padding:5px;padding-left:16px;">
36153 * <li>editor - This editor</li>
36154 * <li>grid - This grid</li>
36155 * <li>record - The record being edited</li>
36156 * <li>field - The field name being edited</li>
36157 * <li>value - The value being set</li>
36158 * <li>originalValue - The original value for the field, before the edit.</li>
36159 * <li>row - The grid row index</li>
36160 * <li>column - The grid column index</li>
36161 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36163 * @param {Object} e An edit event (see above for description)
36165 "validateedit" : true
36167 this.on("bodyscroll", this.stopEditing, this);
36168 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
36171 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36173 * @cfg {Number} clicksToEdit
36174 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36181 trackMouseOver: false, // causes very odd FF errors
36183 onCellDblClick : function(g, row, col){
36184 this.startEditing(row, col);
36187 onEditComplete : function(ed, value, startValue){
36188 this.editing = false;
36189 this.activeEditor = null;
36190 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36192 var field = this.colModel.getDataIndex(ed.col);
36197 originalValue: startValue,
36204 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36207 if(String(value) !== String(startValue)){
36209 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36210 r.set(field, e.value);
36211 // if we are dealing with a combo box..
36212 // then we also set the 'name' colum to be the displayField
36213 if (ed.field.displayField && ed.field.name) {
36214 r.set(ed.field.name, ed.field.el.dom.value);
36217 delete e.cancel; //?? why!!!
36218 this.fireEvent("afteredit", e);
36221 this.fireEvent("afteredit", e); // always fire it!
36223 this.view.focusCell(ed.row, ed.col);
36227 * Starts editing the specified for the specified row/column
36228 * @param {Number} rowIndex
36229 * @param {Number} colIndex
36231 startEditing : function(row, col){
36232 this.stopEditing();
36233 if(this.colModel.isCellEditable(col, row)){
36234 this.view.ensureVisible(row, col, true);
36236 var r = this.dataSource.getAt(row);
36237 var field = this.colModel.getDataIndex(col);
36238 var cell = Roo.get(this.view.getCell(row,col));
36243 value: r.data[field],
36248 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36249 this.editing = true;
36250 var ed = this.colModel.getCellEditor(col, row);
36256 ed.render(ed.parentEl || document.body);
36262 (function(){ // complex but required for focus issues in safari, ie and opera
36266 ed.on("complete", this.onEditComplete, this, {single: true});
36267 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
36268 this.activeEditor = ed;
36269 var v = r.data[field];
36270 ed.startEdit(this.view.getCell(row, col), v);
36271 // combo's with 'displayField and name set
36272 if (ed.field.displayField && ed.field.name) {
36273 ed.field.el.dom.value = r.data[ed.field.name];
36277 }).defer(50, this);
36283 * Stops any active editing
36285 stopEditing : function(){
36286 if(this.activeEditor){
36287 this.activeEditor.completeEdit();
36289 this.activeEditor = null;
36293 * Called to get grid's drag proxy text, by default returns this.ddText.
36296 getDragDropText : function(){
36297 var count = this.selModel.getSelectedCell() ? 1 : 0;
36298 return String.format(this.ddText, count, count == 1 ? '' : 's');
36303 * Ext JS Library 1.1.1
36304 * Copyright(c) 2006-2007, Ext JS, LLC.
36306 * Originally Released Under LGPL - original licence link has changed is not relivant.
36309 * <script type="text/javascript">
36312 // private - not really -- you end up using it !
36313 // This is a support class used internally by the Grid components
36316 * @class Roo.grid.GridEditor
36317 * @extends Roo.Editor
36318 * Class for creating and editable grid elements.
36319 * @param {Object} config any settings (must include field)
36321 Roo.grid.GridEditor = function(field, config){
36322 if (!config && field.field) {
36324 field = Roo.factory(config.field, Roo.form);
36326 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36327 field.monitorTab = false;
36330 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36333 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36336 alignment: "tl-tl",
36339 cls: "x-small-editor x-grid-editor",
36344 * Ext JS Library 1.1.1
36345 * Copyright(c) 2006-2007, Ext JS, LLC.
36347 * Originally Released Under LGPL - original licence link has changed is not relivant.
36350 * <script type="text/javascript">
36355 Roo.grid.PropertyRecord = Roo.data.Record.create([
36356 {name:'name',type:'string'}, 'value'
36360 Roo.grid.PropertyStore = function(grid, source){
36362 this.store = new Roo.data.Store({
36363 recordType : Roo.grid.PropertyRecord
36365 this.store.on('update', this.onUpdate, this);
36367 this.setSource(source);
36369 Roo.grid.PropertyStore.superclass.constructor.call(this);
36374 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36375 setSource : function(o){
36377 this.store.removeAll();
36380 if(this.isEditableValue(o[k])){
36381 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36384 this.store.loadRecords({records: data}, {}, true);
36387 onUpdate : function(ds, record, type){
36388 if(type == Roo.data.Record.EDIT){
36389 var v = record.data['value'];
36390 var oldValue = record.modified['value'];
36391 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36392 this.source[record.id] = v;
36394 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36401 getProperty : function(row){
36402 return this.store.getAt(row);
36405 isEditableValue: function(val){
36406 if(val && val instanceof Date){
36408 }else if(typeof val == 'object' || typeof val == 'function'){
36414 setValue : function(prop, value){
36415 this.source[prop] = value;
36416 this.store.getById(prop).set('value', value);
36419 getSource : function(){
36420 return this.source;
36424 Roo.grid.PropertyColumnModel = function(grid, store){
36427 g.PropertyColumnModel.superclass.constructor.call(this, [
36428 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36429 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36431 this.store = store;
36432 this.bselect = Roo.DomHelper.append(document.body, {
36433 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36434 {tag: 'option', value: 'true', html: 'true'},
36435 {tag: 'option', value: 'false', html: 'false'}
36438 Roo.id(this.bselect);
36441 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36442 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36443 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36444 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36445 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36447 this.renderCellDelegate = this.renderCell.createDelegate(this);
36448 this.renderPropDelegate = this.renderProp.createDelegate(this);
36451 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36455 valueText : 'Value',
36457 dateFormat : 'm/j/Y',
36460 renderDate : function(dateVal){
36461 return dateVal.dateFormat(this.dateFormat);
36464 renderBool : function(bVal){
36465 return bVal ? 'true' : 'false';
36468 isCellEditable : function(colIndex, rowIndex){
36469 return colIndex == 1;
36472 getRenderer : function(col){
36474 this.renderCellDelegate : this.renderPropDelegate;
36477 renderProp : function(v){
36478 return this.getPropertyName(v);
36481 renderCell : function(val){
36483 if(val instanceof Date){
36484 rv = this.renderDate(val);
36485 }else if(typeof val == 'boolean'){
36486 rv = this.renderBool(val);
36488 return Roo.util.Format.htmlEncode(rv);
36491 getPropertyName : function(name){
36492 var pn = this.grid.propertyNames;
36493 return pn && pn[name] ? pn[name] : name;
36496 getCellEditor : function(colIndex, rowIndex){
36497 var p = this.store.getProperty(rowIndex);
36498 var n = p.data['name'], val = p.data['value'];
36500 if(typeof(this.grid.customEditors[n]) == 'string'){
36501 return this.editors[this.grid.customEditors[n]];
36503 if(typeof(this.grid.customEditors[n]) != 'undefined'){
36504 return this.grid.customEditors[n];
36506 if(val instanceof Date){
36507 return this.editors['date'];
36508 }else if(typeof val == 'number'){
36509 return this.editors['number'];
36510 }else if(typeof val == 'boolean'){
36511 return this.editors['boolean'];
36513 return this.editors['string'];
36519 * @class Roo.grid.PropertyGrid
36520 * @extends Roo.grid.EditorGrid
36521 * This class represents the interface of a component based property grid control.
36522 * <br><br>Usage:<pre><code>
36523 var grid = new Roo.grid.PropertyGrid("my-container-id", {
36531 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36532 * The container MUST have some type of size defined for the grid to fill. The container will be
36533 * automatically set to position relative if it isn't already.
36534 * @param {Object} config A config object that sets properties on this grid.
36536 Roo.grid.PropertyGrid = function(container, config){
36537 config = config || {};
36538 var store = new Roo.grid.PropertyStore(this);
36539 this.store = store;
36540 var cm = new Roo.grid.PropertyColumnModel(this, store);
36541 store.store.sort('name', 'ASC');
36542 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36545 enableColLock:false,
36546 enableColumnMove:false,
36548 trackMouseOver: false,
36551 this.getGridEl().addClass('x-props-grid');
36552 this.lastEditRow = null;
36553 this.on('columnresize', this.onColumnResize, this);
36556 * @event beforepropertychange
36557 * Fires before a property changes (return false to stop?)
36558 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36559 * @param {String} id Record Id
36560 * @param {String} newval New Value
36561 * @param {String} oldval Old Value
36563 "beforepropertychange": true,
36565 * @event propertychange
36566 * Fires after a property changes
36567 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36568 * @param {String} id Record Id
36569 * @param {String} newval New Value
36570 * @param {String} oldval Old Value
36572 "propertychange": true
36574 this.customEditors = this.customEditors || {};
36576 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36579 * @cfg {Object} customEditors map of colnames=> custom editors.
36580 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36581 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36582 * false disables editing of the field.
36586 * @cfg {Object} propertyNames map of property Names to their displayed value
36589 render : function(){
36590 Roo.grid.PropertyGrid.superclass.render.call(this);
36591 this.autoSize.defer(100, this);
36594 autoSize : function(){
36595 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36597 this.view.fitColumns();
36601 onColumnResize : function(){
36602 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36606 * Sets the data for the Grid
36607 * accepts a Key => Value object of all the elements avaiable.
36608 * @param {Object} data to appear in grid.
36610 setSource : function(source){
36611 this.store.setSource(source);
36615 * Gets all the data from the grid.
36616 * @return {Object} data data stored in grid
36618 getSource : function(){
36619 return this.store.getSource();
36628 * @class Roo.grid.Calendar
36629 * @extends Roo.util.Grid
36630 * This class extends the Grid to provide a calendar widget
36631 * <br><br>Usage:<pre><code>
36632 var grid = new Roo.grid.Calendar("my-container-id", {
36635 selModel: mySelectionModel,
36636 autoSizeColumns: true,
36637 monitorWindowResize: false,
36638 trackMouseOver: true
36639 eventstore : real data store..
36645 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36646 * The container MUST have some type of size defined for the grid to fill. The container will be
36647 * automatically set to position relative if it isn't already.
36648 * @param {Object} config A config object that sets properties on this grid.
36650 Roo.grid.Calendar = function(container, config){
36651 // initialize the container
36652 this.container = Roo.get(container);
36653 this.container.update("");
36654 this.container.setStyle("overflow", "hidden");
36655 this.container.addClass('x-grid-container');
36657 this.id = this.container.id;
36659 Roo.apply(this, config);
36660 // check and correct shorthanded configs
36664 for (var r = 0;r < 6;r++) {
36667 for (var c =0;c < 7;c++) {
36671 if (this.eventStore) {
36672 this.eventStore= Roo.factory(this.eventStore, Roo.data);
36673 this.eventStore.on('load',this.onLoad, this);
36674 this.eventStore.on('beforeload',this.clearEvents, this);
36678 this.dataSource = new Roo.data.Store({
36679 proxy: new Roo.data.MemoryProxy(rows),
36680 reader: new Roo.data.ArrayReader({}, [
36681 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
36684 this.dataSource.load();
36685 this.ds = this.dataSource;
36686 this.ds.xmodule = this.xmodule || false;
36689 var cellRender = function(v,x,r)
36691 return String.format(
36692 '<div class="fc-day fc-widget-content"><div>' +
36693 '<div class="fc-event-container"></div>' +
36694 '<div class="fc-day-number">{0}</div>'+
36696 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
36697 '</div></div>', v);
36702 this.colModel = new Roo.grid.ColumnModel( [
36704 xtype: 'ColumnModel',
36706 dataIndex : 'weekday0',
36708 renderer : cellRender
36711 xtype: 'ColumnModel',
36713 dataIndex : 'weekday1',
36715 renderer : cellRender
36718 xtype: 'ColumnModel',
36720 dataIndex : 'weekday2',
36721 header : 'Tuesday',
36722 renderer : cellRender
36725 xtype: 'ColumnModel',
36727 dataIndex : 'weekday3',
36728 header : 'Wednesday',
36729 renderer : cellRender
36732 xtype: 'ColumnModel',
36734 dataIndex : 'weekday4',
36735 header : 'Thursday',
36736 renderer : cellRender
36739 xtype: 'ColumnModel',
36741 dataIndex : 'weekday5',
36743 renderer : cellRender
36746 xtype: 'ColumnModel',
36748 dataIndex : 'weekday6',
36749 header : 'Saturday',
36750 renderer : cellRender
36753 this.cm = this.colModel;
36754 this.cm.xmodule = this.xmodule || false;
36758 //this.selModel = new Roo.grid.CellSelectionModel();
36759 //this.sm = this.selModel;
36760 //this.selModel.init(this);
36764 this.container.setWidth(this.width);
36768 this.container.setHeight(this.height);
36775 * The raw click event for the entire grid.
36776 * @param {Roo.EventObject} e
36781 * The raw dblclick event for the entire grid.
36782 * @param {Roo.EventObject} e
36786 * @event contextmenu
36787 * The raw contextmenu event for the entire grid.
36788 * @param {Roo.EventObject} e
36790 "contextmenu" : true,
36793 * The raw mousedown event for the entire grid.
36794 * @param {Roo.EventObject} e
36796 "mousedown" : true,
36799 * The raw mouseup event for the entire grid.
36800 * @param {Roo.EventObject} e
36805 * The raw mouseover event for the entire grid.
36806 * @param {Roo.EventObject} e
36808 "mouseover" : true,
36811 * The raw mouseout event for the entire grid.
36812 * @param {Roo.EventObject} e
36817 * The raw keypress event for the entire grid.
36818 * @param {Roo.EventObject} e
36823 * The raw keydown event for the entire grid.
36824 * @param {Roo.EventObject} e
36832 * Fires when a cell is clicked
36833 * @param {Grid} this
36834 * @param {Number} rowIndex
36835 * @param {Number} columnIndex
36836 * @param {Roo.EventObject} e
36838 "cellclick" : true,
36840 * @event celldblclick
36841 * Fires when a cell is double clicked
36842 * @param {Grid} this
36843 * @param {Number} rowIndex
36844 * @param {Number} columnIndex
36845 * @param {Roo.EventObject} e
36847 "celldblclick" : true,
36850 * Fires when a row is clicked
36851 * @param {Grid} this
36852 * @param {Number} rowIndex
36853 * @param {Roo.EventObject} e
36857 * @event rowdblclick
36858 * Fires when a row is double clicked
36859 * @param {Grid} this
36860 * @param {Number} rowIndex
36861 * @param {Roo.EventObject} e
36863 "rowdblclick" : true,
36865 * @event headerclick
36866 * Fires when a header is clicked
36867 * @param {Grid} this
36868 * @param {Number} columnIndex
36869 * @param {Roo.EventObject} e
36871 "headerclick" : true,
36873 * @event headerdblclick
36874 * Fires when a header cell is double clicked
36875 * @param {Grid} this
36876 * @param {Number} columnIndex
36877 * @param {Roo.EventObject} e
36879 "headerdblclick" : true,
36881 * @event rowcontextmenu
36882 * Fires when a row is right clicked
36883 * @param {Grid} this
36884 * @param {Number} rowIndex
36885 * @param {Roo.EventObject} e
36887 "rowcontextmenu" : true,
36889 * @event cellcontextmenu
36890 * Fires when a cell is right clicked
36891 * @param {Grid} this
36892 * @param {Number} rowIndex
36893 * @param {Number} cellIndex
36894 * @param {Roo.EventObject} e
36896 "cellcontextmenu" : true,
36898 * @event headercontextmenu
36899 * Fires when a header is right clicked
36900 * @param {Grid} this
36901 * @param {Number} columnIndex
36902 * @param {Roo.EventObject} e
36904 "headercontextmenu" : true,
36906 * @event bodyscroll
36907 * Fires when the body element is scrolled
36908 * @param {Number} scrollLeft
36909 * @param {Number} scrollTop
36911 "bodyscroll" : true,
36913 * @event columnresize
36914 * Fires when the user resizes a column
36915 * @param {Number} columnIndex
36916 * @param {Number} newSize
36918 "columnresize" : true,
36920 * @event columnmove
36921 * Fires when the user moves a column
36922 * @param {Number} oldIndex
36923 * @param {Number} newIndex
36925 "columnmove" : true,
36928 * Fires when row(s) start being dragged
36929 * @param {Grid} this
36930 * @param {Roo.GridDD} dd The drag drop object
36931 * @param {event} e The raw browser event
36933 "startdrag" : true,
36936 * Fires when a drag operation is complete
36937 * @param {Grid} this
36938 * @param {Roo.GridDD} dd The drag drop object
36939 * @param {event} e The raw browser event
36944 * Fires when dragged row(s) are dropped on a valid DD target
36945 * @param {Grid} this
36946 * @param {Roo.GridDD} dd The drag drop object
36947 * @param {String} targetId The target drag drop object
36948 * @param {event} e The raw browser event
36953 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
36954 * @param {Grid} this
36955 * @param {Roo.GridDD} dd The drag drop object
36956 * @param {String} targetId The target drag drop object
36957 * @param {event} e The raw browser event
36962 * Fires when the dragged row(s) first cross another DD target while being dragged
36963 * @param {Grid} this
36964 * @param {Roo.GridDD} dd The drag drop object
36965 * @param {String} targetId The target drag drop object
36966 * @param {event} e The raw browser event
36968 "dragenter" : true,
36971 * Fires when the dragged row(s) leave another DD target while being dragged
36972 * @param {Grid} this
36973 * @param {Roo.GridDD} dd The drag drop object
36974 * @param {String} targetId The target drag drop object
36975 * @param {event} e The raw browser event
36980 * Fires when a row is rendered, so you can change add a style to it.
36981 * @param {GridView} gridview The grid view
36982 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
36988 * Fires when the grid is rendered
36989 * @param {Grid} grid
36994 * Fires when a date is selected
36995 * @param {DatePicker} this
36996 * @param {Date} date The selected date
37000 * @event monthchange
37001 * Fires when the displayed month changes
37002 * @param {DatePicker} this
37003 * @param {Date} date The selected month
37005 'monthchange': true,
37007 * @event evententer
37008 * Fires when mouse over an event
37009 * @param {Calendar} this
37010 * @param {event} Event
37012 'evententer': true,
37014 * @event eventleave
37015 * Fires when the mouse leaves an
37016 * @param {Calendar} this
37019 'eventleave': true,
37021 * @event eventclick
37022 * Fires when the mouse click an
37023 * @param {Calendar} this
37026 'eventclick': true,
37028 * @event eventrender
37029 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37030 * @param {Calendar} this
37031 * @param {data} data to be modified
37033 'eventrender': true
37037 Roo.grid.Grid.superclass.constructor.call(this);
37038 this.on('render', function() {
37039 this.view.el.addClass('x-grid-cal');
37041 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37045 if (!Roo.grid.Calendar.style) {
37046 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37049 '.x-grid-cal .x-grid-col' : {
37050 height: 'auto !important',
37051 'vertical-align': 'top'
37053 '.x-grid-cal .fc-event-hori' : {
37064 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37066 * @cfg {Store} eventStore The store that loads events.
37071 activeDate : false,
37074 monitorWindowResize : false,
37077 resizeColumns : function() {
37078 var col = (this.view.el.getWidth() / 7) - 3;
37079 // loop through cols, and setWidth
37080 for(var i =0 ; i < 7 ; i++){
37081 this.cm.setColumnWidth(i, col);
37084 setDate :function(date) {
37086 Roo.log('setDate?');
37088 this.resizeColumns();
37089 var vd = this.activeDate;
37090 this.activeDate = date;
37091 // if(vd && this.el){
37092 // var t = date.getTime();
37093 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37094 // Roo.log('using add remove');
37096 // this.fireEvent('monthchange', this, date);
37098 // this.cells.removeClass("fc-state-highlight");
37099 // this.cells.each(function(c){
37100 // if(c.dateValue == t){
37101 // c.addClass("fc-state-highlight");
37102 // setTimeout(function(){
37103 // try{c.dom.firstChild.focus();}catch(e){}
37113 var days = date.getDaysInMonth();
37115 var firstOfMonth = date.getFirstDateOfMonth();
37116 var startingPos = firstOfMonth.getDay()-this.startDay;
37118 if(startingPos < this.startDay){
37122 var pm = date.add(Date.MONTH, -1);
37123 var prevStart = pm.getDaysInMonth()-startingPos;
37127 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37129 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37130 //this.cells.addClassOnOver('fc-state-hover');
37132 var cells = this.cells.elements;
37133 var textEls = this.textNodes;
37135 //Roo.each(cells, function(cell){
37136 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37139 days += startingPos;
37141 // convert everything to numbers so it's fast
37142 var day = 86400000;
37143 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37146 //Roo.log(prevStart);
37148 var today = new Date().clearTime().getTime();
37149 var sel = date.clearTime().getTime();
37150 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37151 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37152 var ddMatch = this.disabledDatesRE;
37153 var ddText = this.disabledDatesText;
37154 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37155 var ddaysText = this.disabledDaysText;
37156 var format = this.format;
37158 var setCellClass = function(cal, cell){
37160 //Roo.log('set Cell Class');
37162 var t = d.getTime();
37167 cell.dateValue = t;
37169 cell.className += " fc-today";
37170 cell.className += " fc-state-highlight";
37171 cell.title = cal.todayText;
37174 // disable highlight in other month..
37175 cell.className += " fc-state-highlight";
37180 //cell.className = " fc-state-disabled";
37181 cell.title = cal.minText;
37185 //cell.className = " fc-state-disabled";
37186 cell.title = cal.maxText;
37190 if(ddays.indexOf(d.getDay()) != -1){
37191 // cell.title = ddaysText;
37192 // cell.className = " fc-state-disabled";
37195 if(ddMatch && format){
37196 var fvalue = d.dateFormat(format);
37197 if(ddMatch.test(fvalue)){
37198 cell.title = ddText.replace("%0", fvalue);
37199 cell.className = " fc-state-disabled";
37203 if (!cell.initialClassName) {
37204 cell.initialClassName = cell.dom.className;
37207 cell.dom.className = cell.initialClassName + ' ' + cell.className;
37212 for(; i < startingPos; i++) {
37213 cells[i].dayName = (++prevStart);
37214 Roo.log(textEls[i]);
37215 d.setDate(d.getDate()+1);
37217 //cells[i].className = "fc-past fc-other-month";
37218 setCellClass(this, cells[i]);
37223 for(; i < days; i++){
37224 intDay = i - startingPos + 1;
37225 cells[i].dayName = (intDay);
37226 d.setDate(d.getDate()+1);
37228 cells[i].className = ''; // "x-date-active";
37229 setCellClass(this, cells[i]);
37233 for(; i < 42; i++) {
37234 //textEls[i].innerHTML = (++extraDays);
37236 d.setDate(d.getDate()+1);
37237 cells[i].dayName = (++extraDays);
37238 cells[i].className = "fc-future fc-other-month";
37239 setCellClass(this, cells[i]);
37242 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37244 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37246 // this will cause all the cells to mis
37249 for (var r = 0;r < 6;r++) {
37250 for (var c =0;c < 7;c++) {
37251 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37255 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37256 for(i=0;i<cells.length;i++) {
37258 this.cells.elements[i].dayName = cells[i].dayName ;
37259 this.cells.elements[i].className = cells[i].className;
37260 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37261 this.cells.elements[i].title = cells[i].title ;
37262 this.cells.elements[i].dateValue = cells[i].dateValue ;
37268 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37269 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37271 ////if(totalRows != 6){
37272 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37273 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37276 this.fireEvent('monthchange', this, date);
37281 * Returns the grid's SelectionModel.
37282 * @return {SelectionModel}
37284 getSelectionModel : function(){
37285 if(!this.selModel){
37286 this.selModel = new Roo.grid.CellSelectionModel();
37288 return this.selModel;
37292 this.eventStore.load()
37298 findCell : function(dt) {
37299 dt = dt.clearTime().getTime();
37301 this.cells.each(function(c){
37302 //Roo.log("check " +c.dateValue + '?=' + dt);
37303 if(c.dateValue == dt){
37313 findCells : function(rec) {
37314 var s = rec.data.start_dt.clone().clearTime().getTime();
37316 var e= rec.data.end_dt.clone().clearTime().getTime();
37319 this.cells.each(function(c){
37320 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37322 if(c.dateValue > e){
37325 if(c.dateValue < s){
37334 findBestRow: function(cells)
37338 for (var i =0 ; i < cells.length;i++) {
37339 ret = Math.max(cells[i].rows || 0,ret);
37346 addItem : function(rec)
37348 // look for vertical location slot in
37349 var cells = this.findCells(rec);
37351 rec.row = this.findBestRow(cells);
37353 // work out the location.
37357 for(var i =0; i < cells.length; i++) {
37365 if (crow.start.getY() == cells[i].getY()) {
37367 crow.end = cells[i];
37383 for (var i = 0; i < cells.length;i++) {
37384 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37391 clearEvents: function() {
37393 if (!this.eventStore.getCount()) {
37396 // reset number of rows in cells.
37397 Roo.each(this.cells.elements, function(c){
37401 this.eventStore.each(function(e) {
37402 this.clearEvent(e);
37407 clearEvent : function(ev)
37410 Roo.each(ev.els, function(el) {
37411 el.un('mouseenter' ,this.onEventEnter, this);
37412 el.un('mouseleave' ,this.onEventLeave, this);
37420 renderEvent : function(ev,ctr) {
37422 ctr = this.view.el.select('.fc-event-container',true).first();
37426 this.clearEvent(ev);
37432 var cells = ev.cells;
37433 var rows = ev.rows;
37434 this.fireEvent('eventrender', this, ev);
37436 for(var i =0; i < rows.length; i++) {
37440 cls += ' fc-event-start';
37442 if ((i+1) == rows.length) {
37443 cls += ' fc-event-end';
37446 //Roo.log(ev.data);
37447 // how many rows should it span..
37448 var cg = this.eventTmpl.append(ctr,Roo.apply({
37451 }, ev.data) , true);
37454 cg.on('mouseenter' ,this.onEventEnter, this, ev);
37455 cg.on('mouseleave' ,this.onEventLeave, this, ev);
37456 cg.on('click', this.onEventClick, this, ev);
37460 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37461 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37464 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
37465 cg.setWidth(ebox.right - sbox.x -2);
37469 renderEvents: function()
37471 // first make sure there is enough space..
37473 if (!this.eventTmpl) {
37474 this.eventTmpl = new Roo.Template(
37475 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
37476 '<div class="fc-event-inner">' +
37477 '<span class="fc-event-time">{time}</span>' +
37478 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37480 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
37488 this.cells.each(function(c) {
37489 //Roo.log(c.select('.fc-day-content div',true).first());
37490 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37493 var ctr = this.view.el.select('.fc-event-container',true).first();
37496 this.eventStore.each(function(ev){
37498 this.renderEvent(ev);
37502 this.view.layout();
37506 onEventEnter: function (e, el,event,d) {
37507 this.fireEvent('evententer', this, el, event);
37510 onEventLeave: function (e, el,event,d) {
37511 this.fireEvent('eventleave', this, el, event);
37514 onEventClick: function (e, el,event,d) {
37515 this.fireEvent('eventclick', this, el, event);
37518 onMonthChange: function () {
37522 onLoad: function () {
37524 //Roo.log('calendar onload');
37526 if(this.eventStore.getCount() > 0){
37530 this.eventStore.each(function(d){
37535 if (typeof(add.end_dt) == 'undefined') {
37536 Roo.log("Missing End time in calendar data: ");
37540 if (typeof(add.start_dt) == 'undefined') {
37541 Roo.log("Missing Start time in calendar data: ");
37545 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37546 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37547 add.id = add.id || d.id;
37548 add.title = add.title || '??';
37556 this.renderEvents();
37566 render : function ()
37570 if (!this.view.el.hasClass('course-timesheet')) {
37571 this.view.el.addClass('course-timesheet');
37573 if (this.tsStyle) {
37578 Roo.log(_this.grid.view.el.getWidth());
37581 this.tsStyle = Roo.util.CSS.createStyleSheet({
37582 '.course-timesheet .x-grid-row' : {
37585 '.x-grid-row td' : {
37586 'vertical-align' : 0
37588 '.course-edit-link' : {
37590 'text-overflow' : 'ellipsis',
37591 'overflow' : 'hidden',
37592 'white-space' : 'nowrap',
37593 'cursor' : 'pointer'
37598 '.de-act-sup-link' : {
37599 'color' : 'purple',
37600 'text-decoration' : 'line-through'
37604 'text-decoration' : 'line-through'
37606 '.course-timesheet .course-highlight' : {
37607 'border-top-style': 'dashed !important',
37608 'border-bottom-bottom': 'dashed !important'
37610 '.course-timesheet .course-item' : {
37611 'font-family' : 'tahoma, arial, helvetica',
37612 'font-size' : '11px',
37613 'overflow' : 'hidden',
37614 'padding-left' : '10px',
37615 'padding-right' : '10px',
37616 'padding-top' : '10px'
37624 monitorWindowResize : false,
37625 cellrenderer : function(v,x,r)
37630 xtype: 'CellSelectionModel',
37637 beforeload : function (_self, options)
37639 options.params = options.params || {};
37640 options.params._month = _this.monthField.getValue();
37641 options.params.limit = 9999;
37642 options.params['sort'] = 'when_dt';
37643 options.params['dir'] = 'ASC';
37644 this.proxy.loadResponse = this.loadResponse;
37646 //this.addColumns();
37648 load : function (_self, records, options)
37650 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37651 // if you click on the translation.. you can edit it...
37652 var el = Roo.get(this);
37653 var id = el.dom.getAttribute('data-id');
37654 var d = el.dom.getAttribute('data-date');
37655 var t = el.dom.getAttribute('data-time');
37656 //var id = this.child('span').dom.textContent;
37659 Pman.Dialog.CourseCalendar.show({
37663 productitem_active : id ? 1 : 0
37665 _this.grid.ds.load({});
37670 _this.panel.fireEvent('resize', [ '', '' ]);
37673 loadResponse : function(o, success, response){
37674 // this is overridden on before load..
37676 Roo.log("our code?");
37677 //Roo.log(success);
37678 //Roo.log(response)
37679 delete this.activeRequest;
37681 this.fireEvent("loadexception", this, o, response);
37682 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37687 result = o.reader.read(response);
37689 Roo.log("load exception?");
37690 this.fireEvent("loadexception", this, o, response, e);
37691 o.request.callback.call(o.request.scope, null, o.request.arg, false);
37694 Roo.log("ready...");
37695 // loop through result.records;
37696 // and set this.tdate[date] = [] << array of records..
37698 Roo.each(result.records, function(r){
37700 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
37701 _this.tdata[r.data.when_dt.format('j')] = [];
37703 _this.tdata[r.data.when_dt.format('j')].push(r.data);
37706 //Roo.log(_this.tdata);
37708 result.records = [];
37709 result.totalRecords = 6;
37711 // let's generate some duumy records for the rows.
37712 //var st = _this.dateField.getValue();
37714 // work out monday..
37715 //st = st.add(Date.DAY, -1 * st.format('w'));
37717 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37719 var firstOfMonth = date.getFirstDayOfMonth();
37720 var days = date.getDaysInMonth();
37722 var firstAdded = false;
37723 for (var i = 0; i < result.totalRecords ; i++) {
37724 //var d= st.add(Date.DAY, i);
37727 for(var w = 0 ; w < 7 ; w++){
37728 if(!firstAdded && firstOfMonth != w){
37735 var dd = (d > 0 && d < 10) ? "0"+d : d;
37736 row['weekday'+w] = String.format(
37737 '<span style="font-size: 16px;"><b>{0}</b></span>'+
37738 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
37740 date.format('Y-m-')+dd
37743 if(typeof(_this.tdata[d]) != 'undefined'){
37744 Roo.each(_this.tdata[d], function(r){
37748 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
37749 if(r.parent_id*1>0){
37750 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
37753 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
37754 deactive = 'de-act-link';
37757 row['weekday'+w] += String.format(
37758 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
37760 r.product_id_name, //1
37761 r.when_dt.format('h:ia'), //2
37771 // only do this if something added..
37773 result.records.push(_this.grid.dataSource.reader.newRow(row));
37777 // push it twice. (second one with an hour..
37781 this.fireEvent("load", this, o, o.request.arg);
37782 o.request.callback.call(o.request.scope, result, o.request.arg, true);
37784 sortInfo : {field: 'when_dt', direction : 'ASC' },
37786 xtype: 'HttpProxy',
37789 url : baseURL + '/Roo/Shop_course.php'
37792 xtype: 'JsonReader',
37809 'name': 'parent_id',
37813 'name': 'product_id',
37817 'name': 'productitem_id',
37835 click : function (_self, e)
37837 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37838 sd.setMonth(sd.getMonth()-1);
37839 _this.monthField.setValue(sd.format('Y-m-d'));
37840 _this.grid.ds.load({});
37846 xtype: 'Separator',
37850 xtype: 'MonthField',
37853 render : function (_self)
37855 _this.monthField = _self;
37856 // _this.monthField.set today
37858 select : function (combo, date)
37860 _this.grid.ds.load({});
37863 value : (function() { return new Date(); })()
37866 xtype: 'Separator',
37872 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
37882 click : function (_self, e)
37884 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
37885 sd.setMonth(sd.getMonth()+1);
37886 _this.monthField.setValue(sd.format('Y-m-d'));
37887 _this.grid.ds.load({});
37900 * Ext JS Library 1.1.1
37901 * Copyright(c) 2006-2007, Ext JS, LLC.
37903 * Originally Released Under LGPL - original licence link has changed is not relivant.
37906 * <script type="text/javascript">
37910 * @class Roo.LoadMask
37911 * A simple utility class for generically masking elements while loading data. If the element being masked has
37912 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
37913 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
37914 * element's UpdateManager load indicator and will be destroyed after the initial load.
37916 * Create a new LoadMask
37917 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
37918 * @param {Object} config The config object
37920 Roo.LoadMask = function(el, config){
37921 this.el = Roo.get(el);
37922 Roo.apply(this, config);
37924 this.store.on('beforeload', this.onBeforeLoad, this);
37925 this.store.on('load', this.onLoad, this);
37926 this.store.on('loadexception', this.onLoadException, this);
37927 this.removeMask = false;
37929 var um = this.el.getUpdateManager();
37930 um.showLoadIndicator = false; // disable the default indicator
37931 um.on('beforeupdate', this.onBeforeLoad, this);
37932 um.on('update', this.onLoad, this);
37933 um.on('failure', this.onLoad, this);
37934 this.removeMask = true;
37938 Roo.LoadMask.prototype = {
37940 * @cfg {Boolean} removeMask
37941 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
37942 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
37945 * @cfg {String} msg
37946 * The text to display in a centered loading message box (defaults to 'Loading...')
37948 msg : 'Loading...',
37950 * @cfg {String} msgCls
37951 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
37953 msgCls : 'x-mask-loading',
37956 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
37962 * Disables the mask to prevent it from being displayed
37964 disable : function(){
37965 this.disabled = true;
37969 * Enables the mask so that it can be displayed
37971 enable : function(){
37972 this.disabled = false;
37975 onLoadException : function()
37977 Roo.log(arguments);
37979 if (typeof(arguments[3]) != 'undefined') {
37980 Roo.MessageBox.alert("Error loading",arguments[3]);
37984 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
37985 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
37992 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
37995 onLoad : function()
37997 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38001 onBeforeLoad : function(){
38002 if(!this.disabled){
38003 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38008 destroy : function(){
38010 this.store.un('beforeload', this.onBeforeLoad, this);
38011 this.store.un('load', this.onLoad, this);
38012 this.store.un('loadexception', this.onLoadException, this);
38014 var um = this.el.getUpdateManager();
38015 um.un('beforeupdate', this.onBeforeLoad, this);
38016 um.un('update', this.onLoad, this);
38017 um.un('failure', this.onLoad, this);
38022 * Ext JS Library 1.1.1
38023 * Copyright(c) 2006-2007, Ext JS, LLC.
38025 * Originally Released Under LGPL - original licence link has changed is not relivant.
38028 * <script type="text/javascript">
38033 * @class Roo.XTemplate
38034 * @extends Roo.Template
38035 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38037 var t = new Roo.XTemplate(
38038 '<select name="{name}">',
38039 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
38043 // then append, applying the master template values
38046 * Supported features:
38051 {a_variable} - output encoded.
38052 {a_variable.format:("Y-m-d")} - call a method on the variable
38053 {a_variable:raw} - unencoded output
38054 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38055 {a_variable:this.method_on_template(...)} - call a method on the template object.
38060 <tpl for="a_variable or condition.."></tpl>
38061 <tpl if="a_variable or condition"></tpl>
38062 <tpl exec="some javascript"></tpl>
38063 <tpl name="named_template"></tpl> (experimental)
38065 <tpl for="."></tpl> - just iterate the property..
38066 <tpl for=".."></tpl> - iterates with the parent (probably the template)
38070 Roo.XTemplate = function()
38072 Roo.XTemplate.superclass.constructor.apply(this, arguments);
38079 Roo.extend(Roo.XTemplate, Roo.Template, {
38082 * The various sub templates
38087 * basic tag replacing syntax
38090 * // you can fake an object call by doing this
38094 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38097 * compile the template
38099 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38102 compile: function()
38106 s = ['<tpl>', s, '</tpl>'].join('');
38108 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38109 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38110 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
38111 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38112 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
38117 while(true == !!(m = s.match(re))){
38118 var forMatch = m[0].match(nameRe),
38119 ifMatch = m[0].match(ifRe),
38120 execMatch = m[0].match(execRe),
38121 namedMatch = m[0].match(namedRe),
38126 name = forMatch && forMatch[1] ? forMatch[1] : '';
38129 // if - puts fn into test..
38130 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38132 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38137 // exec - calls a function... returns empty if true is returned.
38138 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38140 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38148 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38149 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38150 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38153 var uid = namedMatch ? namedMatch[1] : id;
38157 id: namedMatch ? namedMatch[1] : id,
38164 s = s.replace(m[0], '');
38166 s = s.replace(m[0], '{xtpl'+ id + '}');
38171 for(var i = tpls.length-1; i >= 0; --i){
38172 this.compileTpl(tpls[i]);
38173 this.tpls[tpls[i].id] = tpls[i];
38175 this.master = tpls[tpls.length-1];
38179 * same as applyTemplate, except it's done to one of the subTemplates
38180 * when using named templates, you can do:
38182 * var str = pl.applySubTemplate('your-name', values);
38185 * @param {Number} id of the template
38186 * @param {Object} values to apply to template
38187 * @param {Object} parent (normaly the instance of this object)
38189 applySubTemplate : function(id, values, parent)
38193 var t = this.tpls[id];
38197 if(t.test && !t.test.call(this, values, parent)){
38201 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38202 Roo.log(e.toString());
38208 if(t.exec && t.exec.call(this, values, parent)){
38212 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38213 Roo.log(e.toString());
38218 var vs = t.target ? t.target.call(this, values, parent) : values;
38219 parent = t.target ? values : parent;
38220 if(t.target && vs instanceof Array){
38222 for(var i = 0, len = vs.length; i < len; i++){
38223 buf[buf.length] = t.compiled.call(this, vs[i], parent);
38225 return buf.join('');
38227 return t.compiled.call(this, vs, parent);
38229 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38230 Roo.log(e.toString());
38231 Roo.log(t.compiled);
38236 compileTpl : function(tpl)
38238 var fm = Roo.util.Format;
38239 var useF = this.disableFormats !== true;
38240 var sep = Roo.isGecko ? "+" : ",";
38241 var undef = function(str) {
38242 Roo.log("Property not found :" + str);
38246 var fn = function(m, name, format, args)
38248 //Roo.log(arguments);
38249 args = args ? args.replace(/\\'/g,"'") : args;
38250 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38251 if (typeof(format) == 'undefined') {
38252 format= 'htmlEncode';
38254 if (format == 'raw' ) {
38258 if(name.substr(0, 4) == 'xtpl'){
38259 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38262 // build an array of options to determine if value is undefined..
38264 // basically get 'xxxx.yyyy' then do
38265 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38266 // (function () { Roo.log("Property not found"); return ''; })() :
38271 Roo.each(name.split('.'), function(st) {
38272 lookfor += (lookfor.length ? '.': '') + st;
38273 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
38276 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38279 if(format && useF){
38281 args = args ? ',' + args : "";
38283 if(format.substr(0, 5) != "this."){
38284 format = "fm." + format + '(';
38286 format = 'this.call("'+ format.substr(5) + '", ';
38290 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
38294 // called with xxyx.yuu:(test,test)
38296 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
38298 // raw.. - :raw modifier..
38299 return "'"+ sep + udef_st + name + ")"+sep+"'";
38303 // branched to use + in gecko and [].join() in others
38305 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
38306 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38309 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
38310 body.push(tpl.body.replace(/(\r\n|\n)/g,
38311 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38312 body.push("'].join('');};};");
38313 body = body.join('');
38316 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38318 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
38324 applyTemplate : function(values){
38325 return this.master.compiled.call(this, values, {});
38326 //var s = this.subs;
38329 apply : function(){
38330 return this.applyTemplate.apply(this, arguments);
38335 Roo.XTemplate.from = function(el){
38336 el = Roo.getDom(el);
38337 return new Roo.XTemplate(el.value || el.innerHTML);