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 * using 'cn' the nested child reader read the child array into it's child stores.
790 * @param {Object} rec The record with a 'children array
792 loadDataFromChildren : function(rec)
799 * Gets the number of cached records.
801 * <em>If using paging, this may not be the total size of the dataset. If the data object
802 * used by the Reader contains the dataset size, then the getTotalCount() function returns
803 * the data set size</em>
805 getCount : function(){
806 return this.data.length || 0;
810 * Gets the total number of records in the dataset as returned by the server.
812 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
813 * the dataset size</em>
815 getTotalCount : function(){
816 return this.totalLength || 0;
820 * Returns the sort state of the Store as an object with two properties:
822 field {String} The name of the field by which the Records are sorted
823 direction {String} The sort order, "ASC" or "DESC"
826 getSortState : function(){
827 return this.sortInfo;
831 applySort : function(){
832 if(this.sortInfo && !this.remoteSort){
833 var s = this.sortInfo, f = s.field;
834 var st = this.fields.get(f).sortType;
835 var fn = function(r1, r2){
836 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
837 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
839 this.data.sort(s.direction, fn);
840 if(this.snapshot && this.snapshot != this.data){
841 this.snapshot.sort(s.direction, fn);
847 * Sets the default sort column and order to be used by the next load operation.
848 * @param {String} fieldName The name of the field to sort by.
849 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
851 setDefaultSort : function(field, dir){
852 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
857 * If remote sorting is used, the sort is performed on the server, and the cache is
858 * reloaded. If local sorting is used, the cache is sorted internally.
859 * @param {String} fieldName The name of the field to sort by.
860 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
862 sort : function(fieldName, dir){
863 var f = this.fields.get(fieldName);
865 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
867 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
868 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
873 this.sortToggle[f.name] = dir;
874 this.sortInfo = {field: f.name, direction: dir};
875 if(!this.remoteSort){
877 this.fireEvent("datachanged", this);
879 this.load(this.lastOptions);
884 * Calls the specified function for each of the Records in the cache.
885 * @param {Function} fn The function to call. The Record is passed as the first parameter.
886 * Returning <em>false</em> aborts and exits the iteration.
887 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
889 each : function(fn, scope){
890 this.data.each(fn, scope);
894 * Gets all records modified since the last commit. Modified records are persisted across load operations
895 * (e.g., during paging).
896 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
898 getModifiedRecords : function(){
899 return this.modified;
903 createFilterFn : function(property, value, anyMatch){
904 if(!value.exec){ // not a regex
905 value = String(value);
906 if(value.length == 0){
909 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
912 return value.test(r.data[property]);
917 * Sums the value of <i>property</i> for each record between start and end and returns the result.
918 * @param {String} property A field on your records
919 * @param {Number} start The record index to start at (defaults to 0)
920 * @param {Number} end The last record index to include (defaults to length - 1)
921 * @return {Number} The sum
923 sum : function(property, start, end){
924 var rs = this.data.items, v = 0;
926 end = (end || end === 0) ? end : rs.length-1;
928 for(var i = start; i <= end; i++){
929 v += (rs[i].data[property] || 0);
935 * Filter the records by a specified property.
936 * @param {String} field A field on your records
937 * @param {String/RegExp} value Either a string that the field
938 * should start with or a RegExp to test against the field
939 * @param {Boolean} anyMatch True to match any part not just the beginning
941 filter : function(property, value, anyMatch){
942 var fn = this.createFilterFn(property, value, anyMatch);
943 return fn ? this.filterBy(fn) : this.clearFilter();
947 * Filter by a function. The specified function will be called with each
948 * record in this data source. If the function returns true the record is included,
949 * otherwise it is filtered.
950 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
951 * @param {Object} scope (optional) The scope of the function (defaults to this)
953 filterBy : function(fn, scope){
954 this.snapshot = this.snapshot || this.data;
955 this.data = this.queryBy(fn, scope||this);
956 this.fireEvent("datachanged", this);
960 * Query the records by a specified property.
961 * @param {String} field A field on your records
962 * @param {String/RegExp} value Either a string that the field
963 * should start with or a RegExp to test against the field
964 * @param {Boolean} anyMatch True to match any part not just the beginning
965 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
967 query : function(property, value, anyMatch){
968 var fn = this.createFilterFn(property, value, anyMatch);
969 return fn ? this.queryBy(fn) : this.data.clone();
973 * Query by a function. The specified function will be called with each
974 * record in this data source. If the function returns true the record is included
976 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
977 * @param {Object} scope (optional) The scope of the function (defaults to this)
978 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
980 queryBy : function(fn, scope){
981 var data = this.snapshot || this.data;
982 return data.filterBy(fn, scope||this);
986 * Collects unique values for a particular dataIndex from this store.
987 * @param {String} dataIndex The property to collect
988 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
989 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
990 * @return {Array} An array of the unique values
992 collect : function(dataIndex, allowNull, bypassFilter){
993 var d = (bypassFilter === true && this.snapshot) ?
994 this.snapshot.items : this.data.items;
995 var v, sv, r = [], l = {};
996 for(var i = 0, len = d.length; i < len; i++){
997 v = d[i].data[dataIndex];
999 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1008 * Revert to a view of the Record cache with no filtering applied.
1009 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1011 clearFilter : function(suppressEvent){
1012 if(this.snapshot && this.snapshot != this.data){
1013 this.data = this.snapshot;
1014 delete this.snapshot;
1015 if(suppressEvent !== true){
1016 this.fireEvent("datachanged", this);
1022 afterEdit : function(record){
1023 if(this.modified.indexOf(record) == -1){
1024 this.modified.push(record);
1026 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1030 afterReject : function(record){
1031 this.modified.remove(record);
1032 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1036 afterCommit : function(record){
1037 this.modified.remove(record);
1038 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1042 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1043 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1045 commitChanges : function(){
1046 var m = this.modified.slice(0);
1048 for(var i = 0, len = m.length; i < len; i++){
1054 * Cancel outstanding changes on all changed records.
1056 rejectChanges : function(){
1057 var m = this.modified.slice(0);
1059 for(var i = 0, len = m.length; i < len; i++){
1064 onMetaChange : function(meta, rtype, o){
1065 this.recordType = rtype;
1066 this.fields = rtype.prototype.fields;
1067 delete this.snapshot;
1068 this.sortInfo = meta.sortInfo || this.sortInfo;
1070 this.fireEvent('metachange', this, this.reader.meta);
1073 moveIndex : function(data, type)
1075 var index = this.indexOf(data);
1077 var newIndex = index + type;
1081 this.insert(newIndex, data);
1086 * Ext JS Library 1.1.1
1087 * Copyright(c) 2006-2007, Ext JS, LLC.
1089 * Originally Released Under LGPL - original licence link has changed is not relivant.
1092 * <script type="text/javascript">
1096 * @class Roo.data.SimpleStore
1097 * @extends Roo.data.Store
1098 * Small helper class to make creating Stores from Array data easier.
1099 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1100 * @cfg {Array} fields An array of field definition objects, or field name strings.
1101 * @cfg {Object} an existing reader (eg. copied from another store)
1102 * @cfg {Array} data The multi-dimensional array of data
1104 * @param {Object} config
1106 Roo.data.SimpleStore = function(config)
1108 Roo.data.SimpleStore.superclass.constructor.call(this, {
1110 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1113 Roo.data.Record.create(config.fields)
1115 proxy : new Roo.data.MemoryProxy(config.data)
1119 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1121 * Ext JS Library 1.1.1
1122 * Copyright(c) 2006-2007, Ext JS, LLC.
1124 * Originally Released Under LGPL - original licence link has changed is not relivant.
1127 * <script type="text/javascript">
1132 * @extends Roo.data.Store
1133 * @class Roo.data.JsonStore
1134 * Small helper class to make creating Stores for JSON data easier. <br/>
1136 var store = new Roo.data.JsonStore({
1137 url: 'get-images.php',
1139 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1142 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1143 * JsonReader and HttpProxy (unless inline data is provided).</b>
1144 * @cfg {Array} fields An array of field definition objects, or field name strings.
1146 * @param {Object} config
1148 Roo.data.JsonStore = function(c){
1149 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1150 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1151 reader: new Roo.data.JsonReader(c, c.fields)
1154 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1156 * Ext JS Library 1.1.1
1157 * Copyright(c) 2006-2007, Ext JS, LLC.
1159 * Originally Released Under LGPL - original licence link has changed is not relivant.
1162 * <script type="text/javascript">
1166 Roo.data.Field = function(config){
1167 if(typeof config == "string"){
1168 config = {name: config};
1170 Roo.apply(this, config);
1176 var st = Roo.data.SortTypes;
1177 // named sortTypes are supported, here we look them up
1178 if(typeof this.sortType == "string"){
1179 this.sortType = st[this.sortType];
1182 // set default sortType for strings and dates
1186 this.sortType = st.asUCString;
1189 this.sortType = st.asDate;
1192 this.sortType = st.none;
1197 var stripRe = /[\$,%]/g;
1199 // prebuilt conversion function for this field, instead of
1200 // switching every time we're reading a value
1202 var cv, dateFormat = this.dateFormat;
1207 cv = function(v){ return v; };
1210 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1214 return v !== undefined && v !== null && v !== '' ?
1215 parseInt(String(v).replace(stripRe, ""), 10) : '';
1220 return v !== undefined && v !== null && v !== '' ?
1221 parseFloat(String(v).replace(stripRe, ""), 10) : '';
1226 cv = function(v){ return v === true || v === "true" || v == 1; };
1233 if(v instanceof Date){
1237 if(dateFormat == "timestamp"){
1238 return new Date(v*1000);
1240 return Date.parseDate(v, dateFormat);
1242 var parsed = Date.parse(v);
1243 return parsed ? new Date(parsed) : null;
1252 Roo.data.Field.prototype = {
1260 * Ext JS Library 1.1.1
1261 * Copyright(c) 2006-2007, Ext JS, LLC.
1263 * Originally Released Under LGPL - original licence link has changed is not relivant.
1266 * <script type="text/javascript">
1269 // Base class for reading structured data from a data source. This class is intended to be
1270 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1273 * @class Roo.data.DataReader
1274 * Base class for reading structured data from a data source. This class is intended to be
1275 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1278 Roo.data.DataReader = function(meta, recordType){
1282 this.recordType = recordType instanceof Array ?
1283 Roo.data.Record.create(recordType) : recordType;
1286 Roo.data.DataReader.prototype = {
1289 readerType : 'Data',
1291 * Create an empty record
1292 * @param {Object} data (optional) - overlay some values
1293 * @return {Roo.data.Record} record created.
1295 newRow : function(d) {
1297 this.recordType.prototype.fields.each(function(c) {
1299 case 'int' : da[c.name] = 0; break;
1300 case 'date' : da[c.name] = new Date(); break;
1301 case 'float' : da[c.name] = 0.0; break;
1302 case 'boolean' : da[c.name] = false; break;
1303 default : da[c.name] = ""; break;
1307 return new this.recordType(Roo.apply(da, d));
1313 * Ext JS Library 1.1.1
1314 * Copyright(c) 2006-2007, Ext JS, LLC.
1316 * Originally Released Under LGPL - original licence link has changed is not relivant.
1319 * <script type="text/javascript">
1323 * @class Roo.data.DataProxy
1324 * @extends Roo.data.Observable
1325 * This class is an abstract base class for implementations which provide retrieval of
1326 * unformatted data objects.<br>
1328 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1329 * (of the appropriate type which knows how to parse the data object) to provide a block of
1330 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1332 * Custom implementations must implement the load method as described in
1333 * {@link Roo.data.HttpProxy#load}.
1335 Roo.data.DataProxy = function(){
1339 * Fires before a network request is made to retrieve a data object.
1340 * @param {Object} This DataProxy object.
1341 * @param {Object} params The params parameter to the load function.
1346 * Fires before the load method's callback is called.
1347 * @param {Object} This DataProxy object.
1348 * @param {Object} o The data object.
1349 * @param {Object} arg The callback argument object passed to the load function.
1353 * @event loadexception
1354 * Fires if an Exception occurs during data retrieval.
1355 * @param {Object} This DataProxy object.
1356 * @param {Object} o The data object.
1357 * @param {Object} arg The callback argument object passed to the load function.
1358 * @param {Object} e The Exception.
1360 loadexception : true
1362 Roo.data.DataProxy.superclass.constructor.call(this);
1365 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1368 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1372 * Ext JS Library 1.1.1
1373 * Copyright(c) 2006-2007, Ext JS, LLC.
1375 * Originally Released Under LGPL - original licence link has changed is not relivant.
1378 * <script type="text/javascript">
1381 * @class Roo.data.MemoryProxy
1382 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1383 * to the Reader when its load method is called.
1385 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1387 Roo.data.MemoryProxy = function(data){
1391 Roo.data.MemoryProxy.superclass.constructor.call(this);
1395 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1398 * Load data from the requested source (in this case an in-memory
1399 * data object passed to the constructor), read the data object into
1400 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1401 * process that block using the passed callback.
1402 * @param {Object} params This parameter is not used by the MemoryProxy class.
1403 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1404 * object into a block of Roo.data.Records.
1405 * @param {Function} callback The function into which to pass the block of Roo.data.records.
1406 * The function must be passed <ul>
1407 * <li>The Record block object</li>
1408 * <li>The "arg" argument from the load function</li>
1409 * <li>A boolean success indicator</li>
1411 * @param {Object} scope The scope in which to call the callback
1412 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1414 load : function(params, reader, callback, scope, arg){
1415 params = params || {};
1418 result = reader.readRecords(params.data ? params.data :this.data);
1420 this.fireEvent("loadexception", this, arg, null, e);
1421 callback.call(scope, null, arg, false);
1424 callback.call(scope, result, arg, true);
1428 update : function(params, records){
1433 * Ext JS Library 1.1.1
1434 * Copyright(c) 2006-2007, Ext JS, LLC.
1436 * Originally Released Under LGPL - original licence link has changed is not relivant.
1439 * <script type="text/javascript">
1442 * @class Roo.data.HttpProxy
1443 * @extends Roo.data.DataProxy
1444 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1445 * configured to reference a certain URL.<br><br>
1447 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1448 * from which the running page was served.<br><br>
1450 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1452 * Be aware that to enable the browser to parse an XML document, the server must set
1453 * the Content-Type header in the HTTP response to "text/xml".
1455 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1456 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
1457 * will be used to make the request.
1459 Roo.data.HttpProxy = function(conn){
1460 Roo.data.HttpProxy.superclass.constructor.call(this);
1461 // is conn a conn config or a real conn?
1463 this.useAjax = !conn || !conn.events;
1467 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1468 // thse are take from connection...
1471 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1474 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1475 * extra parameters to each request made by this object. (defaults to undefined)
1478 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1479 * to each request made by this object. (defaults to undefined)
1482 * @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)
1485 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1488 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1494 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1498 * Return the {@link Roo.data.Connection} object being used by this Proxy.
1499 * @return {Connection} The Connection object. This object may be used to subscribe to events on
1500 * a finer-grained basis than the DataProxy events.
1502 getConnection : function(){
1503 return this.useAjax ? Roo.Ajax : this.conn;
1507 * Load data from the configured {@link Roo.data.Connection}, read the data object into
1508 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1509 * process that block using the passed callback.
1510 * @param {Object} params An object containing properties which are to be used as HTTP parameters
1511 * for the request to the remote server.
1512 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1513 * object into a block of Roo.data.Records.
1514 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1515 * The function must be passed <ul>
1516 * <li>The Record block object</li>
1517 * <li>The "arg" argument from the load function</li>
1518 * <li>A boolean success indicator</li>
1520 * @param {Object} scope The scope in which to call the callback
1521 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1523 load : function(params, reader, callback, scope, arg){
1524 if(this.fireEvent("beforeload", this, params) !== false){
1526 params : params || {},
1528 callback : callback,
1533 callback : this.loadResponse,
1537 Roo.applyIf(o, this.conn);
1538 if(this.activeRequest){
1539 Roo.Ajax.abort(this.activeRequest);
1541 this.activeRequest = Roo.Ajax.request(o);
1543 this.conn.request(o);
1546 callback.call(scope||this, null, arg, false);
1551 loadResponse : function(o, success, response){
1552 delete this.activeRequest;
1554 this.fireEvent("loadexception", this, o, response);
1555 o.request.callback.call(o.request.scope, null, o.request.arg, false);
1560 result = o.reader.read(response);
1562 this.fireEvent("loadexception", this, o, response, e);
1563 o.request.callback.call(o.request.scope, null, o.request.arg, false);
1567 this.fireEvent("load", this, o, o.request.arg);
1568 o.request.callback.call(o.request.scope, result, o.request.arg, true);
1572 update : function(dataSet){
1577 updateResponse : function(dataSet){
1582 * Ext JS Library 1.1.1
1583 * Copyright(c) 2006-2007, Ext JS, LLC.
1585 * Originally Released Under LGPL - original licence link has changed is not relivant.
1588 * <script type="text/javascript">
1592 * @class Roo.data.ScriptTagProxy
1593 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1594 * other than the originating domain of the running page.<br><br>
1596 * <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
1597 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1599 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1600 * source code that is used as the source inside a <script> tag.<br><br>
1602 * In order for the browser to process the returned data, the server must wrap the data object
1603 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1604 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1605 * depending on whether the callback name was passed:
1608 boolean scriptTag = false;
1609 String cb = request.getParameter("callback");
1612 response.setContentType("text/javascript");
1614 response.setContentType("application/x-json");
1616 Writer out = response.getWriter();
1618 out.write(cb + "(");
1620 out.print(dataBlock.toJsonString());
1627 * @param {Object} config A configuration object.
1629 Roo.data.ScriptTagProxy = function(config){
1630 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1631 Roo.apply(this, config);
1632 this.head = document.getElementsByTagName("head")[0];
1635 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1637 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1639 * @cfg {String} url The URL from which to request the data object.
1642 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1646 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1647 * the server the name of the callback function set up by the load call to process the returned data object.
1648 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1649 * javascript output which calls this named function passing the data object as its only parameter.
1651 callbackParam : "callback",
1653 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1654 * name to the request.
1659 * Load data from the configured URL, read the data object into
1660 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1661 * process that block using the passed callback.
1662 * @param {Object} params An object containing properties which are to be used as HTTP parameters
1663 * for the request to the remote server.
1664 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1665 * object into a block of Roo.data.Records.
1666 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1667 * The function must be passed <ul>
1668 * <li>The Record block object</li>
1669 * <li>The "arg" argument from the load function</li>
1670 * <li>A boolean success indicator</li>
1672 * @param {Object} scope The scope in which to call the callback
1673 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1675 load : function(params, reader, callback, scope, arg){
1676 if(this.fireEvent("beforeload", this, params) !== false){
1678 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1681 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1683 url += "&_dc=" + (new Date().getTime());
1685 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1688 cb : "stcCallback"+transId,
1689 scriptId : "stcScript"+transId,
1693 callback : callback,
1699 window[trans.cb] = function(o){
1700 conn.handleResponse(o, trans);
1703 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1705 if(this.autoAbort !== false){
1709 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1711 var script = document.createElement("script");
1712 script.setAttribute("src", url);
1713 script.setAttribute("type", "text/javascript");
1714 script.setAttribute("id", trans.scriptId);
1715 this.head.appendChild(script);
1719 callback.call(scope||this, null, arg, false);
1724 isLoading : function(){
1725 return this.trans ? true : false;
1729 * Abort the current server request.
1732 if(this.isLoading()){
1733 this.destroyTrans(this.trans);
1738 destroyTrans : function(trans, isLoaded){
1739 this.head.removeChild(document.getElementById(trans.scriptId));
1740 clearTimeout(trans.timeoutId);
1742 window[trans.cb] = undefined;
1744 delete window[trans.cb];
1747 // if hasn't been loaded, wait for load to remove it to prevent script error
1748 window[trans.cb] = function(){
1749 window[trans.cb] = undefined;
1751 delete window[trans.cb];
1758 handleResponse : function(o, trans){
1760 this.destroyTrans(trans, true);
1763 result = trans.reader.readRecords(o);
1765 this.fireEvent("loadexception", this, o, trans.arg, e);
1766 trans.callback.call(trans.scope||window, null, trans.arg, false);
1769 this.fireEvent("load", this, o, trans.arg);
1770 trans.callback.call(trans.scope||window, result, trans.arg, true);
1774 handleFailure : function(trans){
1776 this.destroyTrans(trans, false);
1777 this.fireEvent("loadexception", this, null, trans.arg);
1778 trans.callback.call(trans.scope||window, null, trans.arg, false);
1782 * Ext JS Library 1.1.1
1783 * Copyright(c) 2006-2007, Ext JS, LLC.
1785 * Originally Released Under LGPL - original licence link has changed is not relivant.
1788 * <script type="text/javascript">
1792 * @class Roo.data.JsonReader
1793 * @extends Roo.data.DataReader
1794 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1795 * based on mappings in a provided Roo.data.Record constructor.
1797 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1798 * in the reply previously.
1803 var RecordDef = Roo.data.Record.create([
1804 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
1805 {name: 'occupation'} // This field will use "occupation" as the mapping.
1807 var myReader = new Roo.data.JsonReader({
1808 totalProperty: "results", // The property which contains the total dataset size (optional)
1809 root: "rows", // The property which contains an Array of row objects
1810 id: "id" // The property within each row object that provides an ID for the record (optional)
1814 * This would consume a JSON file like this:
1816 { 'results': 2, 'rows': [
1817 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1818 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1821 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1822 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1823 * paged from the remote server.
1824 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1825 * @cfg {String} root name of the property which contains the Array of row objects.
1826 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1827 * @cfg {Array} fields Array of field definition objects
1829 * Create a new JsonReader
1830 * @param {Object} meta Metadata configuration options
1831 * @param {Object} recordType Either an Array of field definition objects,
1832 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1834 Roo.data.JsonReader = function(meta, recordType){
1837 // set some defaults:
1839 totalProperty: 'total',
1840 successProperty : 'success',
1845 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1847 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1849 readerType : 'Json',
1852 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
1853 * Used by Store query builder to append _requestMeta to params.
1856 metaFromRemote : false,
1858 * This method is only used by a DataProxy which has retrieved data from a remote server.
1859 * @param {Object} response The XHR object which contains the JSON data in its responseText.
1860 * @return {Object} data A data block which is used by an Roo.data.Store object as
1861 * a cache of Roo.data.Records.
1863 read : function(response){
1864 var json = response.responseText;
1866 var o = /* eval:var:o */ eval("("+json+")");
1868 throw {message: "JsonReader.read: Json object not found"};
1874 this.metaFromRemote = true;
1875 this.meta = o.metaData;
1876 this.recordType = Roo.data.Record.create(o.metaData.fields);
1877 this.onMetaChange(this.meta, this.recordType, o);
1879 return this.readRecords(o);
1882 // private function a store will implement
1883 onMetaChange : function(meta, recordType, o){
1890 simpleAccess: function(obj, subsc) {
1897 getJsonAccessor: function(){
1899 return function(expr) {
1901 return(re.test(expr))
1902 ? new Function("obj", "return obj." + expr)
1912 * Create a data block containing Roo.data.Records from an XML document.
1913 * @param {Object} o An object which contains an Array of row objects in the property specified
1914 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1915 * which contains the total size of the dataset.
1916 * @return {Object} data A data block which is used by an Roo.data.Store object as
1917 * a cache of Roo.data.Records.
1919 readRecords : function(o){
1921 * After any data loads, the raw JSON data is available for further custom processing.
1925 var s = this.meta, Record = this.recordType,
1926 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1928 // Generate extraction functions for the totalProperty, the root, the id, and for each field
1930 if(s.totalProperty) {
1931 this.getTotal = this.getJsonAccessor(s.totalProperty);
1933 if(s.successProperty) {
1934 this.getSuccess = this.getJsonAccessor(s.successProperty);
1936 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1938 var g = this.getJsonAccessor(s.id);
1939 this.getId = function(rec) {
1941 return (r === undefined || r === "") ? null : r;
1944 this.getId = function(){return null;};
1947 for(var jj = 0; jj < fl; jj++){
1949 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1950 this.ef[jj] = this.getJsonAccessor(map);
1954 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1955 if(s.totalProperty){
1956 var vt = parseInt(this.getTotal(o), 10);
1961 if(s.successProperty){
1962 var vs = this.getSuccess(o);
1963 if(vs === false || vs === 'false'){
1968 for(var i = 0; i < c; i++){
1971 var id = this.getId(n);
1972 for(var j = 0; j < fl; j++){
1974 var v = this.ef[j](n);
1976 Roo.log('missing convert for ' + f.name);
1980 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1982 var record = new Record(values, id);
1984 records[i] = record;
1990 totalRecords : totalRecords
1994 * using 'cn' the nested child reader read the child array into it's child stores.
1995 * @param {Object} rec The record with a 'children array
1997 loadDataFromChildren: function(rec)
1999 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2000 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2001 return this.loadData({ data : data, total : data.length });
2006 * Ext JS Library 1.1.1
2007 * Copyright(c) 2006-2007, Ext JS, LLC.
2009 * Originally Released Under LGPL - original licence link has changed is not relivant.
2012 * <script type="text/javascript">
2016 * @class Roo.data.XmlReader
2017 * @extends Roo.data.DataReader
2018 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2019 * based on mappings in a provided Roo.data.Record constructor.<br><br>
2021 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2022 * header in the HTTP response must be set to "text/xml".</em>
2026 var RecordDef = Roo.data.Record.create([
2027 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
2028 {name: 'occupation'} // This field will use "occupation" as the mapping.
2030 var myReader = new Roo.data.XmlReader({
2031 totalRecords: "results", // The element which contains the total dataset size (optional)
2032 record: "row", // The repeated element which contains row information
2033 id: "id" // The element within the row that provides an ID for the record (optional)
2037 * This would consume an XML file like this:
2041 <results>2</results>
2044 <name>Bill</name>
2045 <occupation>Gardener</occupation>
2049 <name>Ben</name>
2050 <occupation>Horticulturalist</occupation>
2054 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2055 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2056 * paged from the remote server.
2057 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2058 * @cfg {String} success The DomQuery path to the success attribute used by forms.
2059 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2060 * a record identifier value.
2062 * Create a new XmlReader
2063 * @param {Object} meta Metadata configuration options
2064 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
2065 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2066 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
2068 Roo.data.XmlReader = function(meta, recordType){
2070 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2072 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2077 * This method is only used by a DataProxy which has retrieved data from a remote server.
2078 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
2079 * to contain a method called 'responseXML' that returns an XML document object.
2080 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2081 * a cache of Roo.data.Records.
2083 read : function(response){
2084 var doc = response.responseXML;
2086 throw {message: "XmlReader.read: XML Document not available"};
2088 return this.readRecords(doc);
2092 * Create a data block containing Roo.data.Records from an XML document.
2093 * @param {Object} doc A parsed XML document.
2094 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2095 * a cache of Roo.data.Records.
2097 readRecords : function(doc){
2099 * After any data loads/reads, the raw XML Document is available for further custom processing.
2103 var root = doc.documentElement || doc;
2104 var q = Roo.DomQuery;
2105 var recordType = this.recordType, fields = recordType.prototype.fields;
2106 var sid = this.meta.id;
2107 var totalRecords = 0, success = true;
2108 if(this.meta.totalRecords){
2109 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2112 if(this.meta.success){
2113 var sv = q.selectValue(this.meta.success, root, true);
2114 success = sv !== false && sv !== 'false';
2117 var ns = q.select(this.meta.record, root);
2118 for(var i = 0, len = ns.length; i < len; i++) {
2121 var id = sid ? q.selectValue(sid, n) : undefined;
2122 for(var j = 0, jlen = fields.length; j < jlen; j++){
2123 var f = fields.items[j];
2124 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2128 var record = new recordType(values, id);
2130 records[records.length] = record;
2136 totalRecords : totalRecords || records.length
2141 * Ext JS Library 1.1.1
2142 * Copyright(c) 2006-2007, Ext JS, LLC.
2144 * Originally Released Under LGPL - original licence link has changed is not relivant.
2147 * <script type="text/javascript">
2151 * @class Roo.data.ArrayReader
2152 * @extends Roo.data.DataReader
2153 * Data reader class to create an Array of Roo.data.Record objects from an Array.
2154 * Each element of that Array represents a row of data fields. The
2155 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2156 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2160 var RecordDef = Roo.data.Record.create([
2161 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
2162 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
2164 var myReader = new Roo.data.ArrayReader({
2165 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
2169 * This would consume an Array like this:
2171 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2175 * Create a new JsonReader
2176 * @param {Object} meta Metadata configuration options.
2177 * @param {Object|Array} recordType Either an Array of field definition objects
2179 * @cfg {Array} fields Array of field definition objects
2180 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2181 * as specified to {@link Roo.data.Record#create},
2182 * or an {@link Roo.data.Record} object
2185 * created using {@link Roo.data.Record#create}.
2187 Roo.data.ArrayReader = function(meta, recordType)
2189 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2192 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2195 * Create a data block containing Roo.data.Records from an XML document.
2196 * @param {Object} o An Array of row objects which represents the dataset.
2197 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2198 * a cache of Roo.data.Records.
2200 readRecords : function(o)
2202 var sid = this.meta ? this.meta.id : null;
2203 var recordType = this.recordType, fields = recordType.prototype.fields;
2206 for(var i = 0; i < root.length; i++){
2209 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2210 for(var j = 0, jlen = fields.length; j < jlen; j++){
2211 var f = fields.items[j];
2212 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2213 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2217 var record = new recordType(values, id);
2219 records[records.length] = record;
2223 totalRecords : records.length
2227 * using 'cn' the nested child reader read the child array into it's child stores.
2228 * @param {Object} rec The record with a 'children array
2230 loadDataFromChildren: function(rec)
2232 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2233 return this.loadData(typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn);
2240 * Ext JS Library 1.1.1
2241 * Copyright(c) 2006-2007, Ext JS, LLC.
2243 * Originally Released Under LGPL - original licence link has changed is not relivant.
2246 * <script type="text/javascript">
2251 * @class Roo.data.Tree
2252 * @extends Roo.util.Observable
2253 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2254 * in the tree have most standard DOM functionality.
2256 * @param {Node} root (optional) The root node
2258 Roo.data.Tree = function(root){
2261 * The root node for this tree
2266 this.setRootNode(root);
2271 * Fires when a new child node is appended to a node in this tree.
2272 * @param {Tree} tree The owner tree
2273 * @param {Node} parent The parent node
2274 * @param {Node} node The newly appended node
2275 * @param {Number} index The index of the newly appended node
2280 * Fires when a child node is removed from a node in this tree.
2281 * @param {Tree} tree The owner tree
2282 * @param {Node} parent The parent node
2283 * @param {Node} node The child node removed
2288 * Fires when a node is moved to a new location in the tree
2289 * @param {Tree} tree The owner tree
2290 * @param {Node} node The node moved
2291 * @param {Node} oldParent The old parent of this node
2292 * @param {Node} newParent The new parent of this node
2293 * @param {Number} index The index it was moved to
2298 * Fires when a new child node is inserted in a node in this tree.
2299 * @param {Tree} tree The owner tree
2300 * @param {Node} parent The parent node
2301 * @param {Node} node The child node inserted
2302 * @param {Node} refNode The child node the node was inserted before
2306 * @event beforeappend
2307 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2308 * @param {Tree} tree The owner tree
2309 * @param {Node} parent The parent node
2310 * @param {Node} node The child node to be appended
2312 "beforeappend" : true,
2314 * @event beforeremove
2315 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2316 * @param {Tree} tree The owner tree
2317 * @param {Node} parent The parent node
2318 * @param {Node} node The child node to be removed
2320 "beforeremove" : true,
2323 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2324 * @param {Tree} tree The owner tree
2325 * @param {Node} node The node being moved
2326 * @param {Node} oldParent The parent of the node
2327 * @param {Node} newParent The new parent the node is moving to
2328 * @param {Number} index The index it is being moved to
2330 "beforemove" : true,
2332 * @event beforeinsert
2333 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2334 * @param {Tree} tree The owner tree
2335 * @param {Node} parent The parent node
2336 * @param {Node} node The child node to be inserted
2337 * @param {Node} refNode The child node the node is being inserted before
2339 "beforeinsert" : true
2342 Roo.data.Tree.superclass.constructor.call(this);
2345 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2348 proxyNodeEvent : function(){
2349 return this.fireEvent.apply(this, arguments);
2353 * Returns the root node for this tree.
2356 getRootNode : function(){
2361 * Sets the root node for this tree.
2362 * @param {Node} node
2365 setRootNode : function(node){
2367 node.ownerTree = this;
2369 this.registerNode(node);
2374 * Gets a node in this tree by its id.
2375 * @param {String} id
2378 getNodeById : function(id){
2379 return this.nodeHash[id];
2382 registerNode : function(node){
2383 this.nodeHash[node.id] = node;
2386 unregisterNode : function(node){
2387 delete this.nodeHash[node.id];
2390 toString : function(){
2391 return "[Tree"+(this.id?" "+this.id:"")+"]";
2396 * @class Roo.data.Node
2397 * @extends Roo.util.Observable
2398 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2399 * @cfg {String} id The id for this node. If one is not specified, one is generated.
2401 * @param {Object} attributes The attributes/config for the node
2403 Roo.data.Node = function(attributes){
2405 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2408 this.attributes = attributes || {};
2409 this.leaf = this.attributes.leaf;
2411 * The node id. @type String
2413 this.id = this.attributes.id;
2415 this.id = Roo.id(null, "ynode-");
2416 this.attributes.id = this.id;
2421 * All child nodes of this node. @type Array
2423 this.childNodes = [];
2424 if(!this.childNodes.indexOf){ // indexOf is a must
2425 this.childNodes.indexOf = function(o){
2426 for(var i = 0, len = this.length; i < len; i++){
2435 * The parent node for this node. @type Node
2437 this.parentNode = null;
2439 * The first direct child node of this node, or null if this node has no child nodes. @type Node
2441 this.firstChild = null;
2443 * The last direct child node of this node, or null if this node has no child nodes. @type Node
2445 this.lastChild = null;
2447 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2449 this.previousSibling = null;
2451 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2453 this.nextSibling = null;
2458 * Fires when a new child node is appended
2459 * @param {Tree} tree The owner tree
2460 * @param {Node} this This node
2461 * @param {Node} node The newly appended node
2462 * @param {Number} index The index of the newly appended node
2467 * Fires when a child node is removed
2468 * @param {Tree} tree The owner tree
2469 * @param {Node} this This node
2470 * @param {Node} node The removed node
2475 * Fires when this node is moved to a new location in the tree
2476 * @param {Tree} tree The owner tree
2477 * @param {Node} this This node
2478 * @param {Node} oldParent The old parent of this node
2479 * @param {Node} newParent The new parent of this node
2480 * @param {Number} index The index it was moved to
2485 * Fires when a new child node is inserted.
2486 * @param {Tree} tree The owner tree
2487 * @param {Node} this This node
2488 * @param {Node} node The child node inserted
2489 * @param {Node} refNode The child node the node was inserted before
2493 * @event beforeappend
2494 * Fires before a new child is appended, return false to cancel the append.
2495 * @param {Tree} tree The owner tree
2496 * @param {Node} this This node
2497 * @param {Node} node The child node to be appended
2499 "beforeappend" : true,
2501 * @event beforeremove
2502 * Fires before a child is removed, return false to cancel the remove.
2503 * @param {Tree} tree The owner tree
2504 * @param {Node} this This node
2505 * @param {Node} node The child node to be removed
2507 "beforeremove" : true,
2510 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2511 * @param {Tree} tree The owner tree
2512 * @param {Node} this This node
2513 * @param {Node} oldParent The parent of this node
2514 * @param {Node} newParent The new parent this node is moving to
2515 * @param {Number} index The index it is being moved to
2517 "beforemove" : true,
2519 * @event beforeinsert
2520 * Fires before a new child is inserted, return false to cancel the insert.
2521 * @param {Tree} tree The owner tree
2522 * @param {Node} this This node
2523 * @param {Node} node The child node to be inserted
2524 * @param {Node} refNode The child node the node is being inserted before
2526 "beforeinsert" : true
2528 this.listeners = this.attributes.listeners;
2529 Roo.data.Node.superclass.constructor.call(this);
2532 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2533 fireEvent : function(evtName){
2534 // first do standard event for this node
2535 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2538 // then bubble it up to the tree if the event wasn't cancelled
2539 var ot = this.getOwnerTree();
2541 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2549 * Returns true if this node is a leaf
2552 isLeaf : function(){
2553 return this.leaf === true;
2557 setFirstChild : function(node){
2558 this.firstChild = node;
2562 setLastChild : function(node){
2563 this.lastChild = node;
2568 * Returns true if this node is the last child of its parent
2571 isLast : function(){
2572 return (!this.parentNode ? true : this.parentNode.lastChild == this);
2576 * Returns true if this node is the first child of its parent
2579 isFirst : function(){
2580 return (!this.parentNode ? true : this.parentNode.firstChild == this);
2583 hasChildNodes : function(){
2584 return !this.isLeaf() && this.childNodes.length > 0;
2588 * Insert node(s) as the last child node of this node.
2589 * @param {Node/Array} node The node or Array of nodes to append
2590 * @return {Node} The appended node if single append, or null if an array was passed
2592 appendChild : function(node){
2594 if(node instanceof Array){
2596 }else if(arguments.length > 1){
2600 // if passed an array or multiple args do them one by one
2602 for(var i = 0, len = multi.length; i < len; i++) {
2603 this.appendChild(multi[i]);
2606 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2609 var index = this.childNodes.length;
2610 var oldParent = node.parentNode;
2611 // it's a move, make sure we move it cleanly
2613 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2616 oldParent.removeChild(node);
2619 index = this.childNodes.length;
2621 this.setFirstChild(node);
2623 this.childNodes.push(node);
2624 node.parentNode = this;
2625 var ps = this.childNodes[index-1];
2627 node.previousSibling = ps;
2628 ps.nextSibling = node;
2630 node.previousSibling = null;
2632 node.nextSibling = null;
2633 this.setLastChild(node);
2634 node.setOwnerTree(this.getOwnerTree());
2635 this.fireEvent("append", this.ownerTree, this, node, index);
2636 if(this.ownerTree) {
2637 this.ownerTree.fireEvent("appendnode", this, node, index);
2640 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2647 * Removes a child node from this node.
2648 * @param {Node} node The node to remove
2649 * @return {Node} The removed node
2651 removeChild : function(node){
2652 var index = this.childNodes.indexOf(node);
2656 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2660 // remove it from childNodes collection
2661 this.childNodes.splice(index, 1);
2664 if(node.previousSibling){
2665 node.previousSibling.nextSibling = node.nextSibling;
2667 if(node.nextSibling){
2668 node.nextSibling.previousSibling = node.previousSibling;
2671 // update child refs
2672 if(this.firstChild == node){
2673 this.setFirstChild(node.nextSibling);
2675 if(this.lastChild == node){
2676 this.setLastChild(node.previousSibling);
2679 node.setOwnerTree(null);
2680 // clear any references from the node
2681 node.parentNode = null;
2682 node.previousSibling = null;
2683 node.nextSibling = null;
2684 this.fireEvent("remove", this.ownerTree, this, node);
2689 * Inserts the first node before the second node in this nodes childNodes collection.
2690 * @param {Node} node The node to insert
2691 * @param {Node} refNode The node to insert before (if null the node is appended)
2692 * @return {Node} The inserted node
2694 insertBefore : function(node, refNode){
2695 if(!refNode){ // like standard Dom, refNode can be null for append
2696 return this.appendChild(node);
2699 if(node == refNode){
2703 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2706 var index = this.childNodes.indexOf(refNode);
2707 var oldParent = node.parentNode;
2708 var refIndex = index;
2710 // when moving internally, indexes will change after remove
2711 if(oldParent == this && this.childNodes.indexOf(node) < index){
2715 // it's a move, make sure we move it cleanly
2717 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2720 oldParent.removeChild(node);
2723 this.setFirstChild(node);
2725 this.childNodes.splice(refIndex, 0, node);
2726 node.parentNode = this;
2727 var ps = this.childNodes[refIndex-1];
2729 node.previousSibling = ps;
2730 ps.nextSibling = node;
2732 node.previousSibling = null;
2734 node.nextSibling = refNode;
2735 refNode.previousSibling = node;
2736 node.setOwnerTree(this.getOwnerTree());
2737 this.fireEvent("insert", this.ownerTree, this, node, refNode);
2739 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2745 * Returns the child node at the specified index.
2746 * @param {Number} index
2749 item : function(index){
2750 return this.childNodes[index];
2754 * Replaces one child node in this node with another.
2755 * @param {Node} newChild The replacement node
2756 * @param {Node} oldChild The node to replace
2757 * @return {Node} The replaced node
2759 replaceChild : function(newChild, oldChild){
2760 this.insertBefore(newChild, oldChild);
2761 this.removeChild(oldChild);
2766 * Returns the index of a child node
2767 * @param {Node} node
2768 * @return {Number} The index of the node or -1 if it was not found
2770 indexOf : function(child){
2771 return this.childNodes.indexOf(child);
2775 * Returns the tree this node is in.
2778 getOwnerTree : function(){
2779 // if it doesn't have one, look for one
2780 if(!this.ownerTree){
2784 this.ownerTree = p.ownerTree;
2790 return this.ownerTree;
2794 * Returns depth of this node (the root node has a depth of 0)
2797 getDepth : function(){
2800 while(p.parentNode){
2808 setOwnerTree : function(tree){
2809 // if it's move, we need to update everyone
2810 if(tree != this.ownerTree){
2812 this.ownerTree.unregisterNode(this);
2814 this.ownerTree = tree;
2815 var cs = this.childNodes;
2816 for(var i = 0, len = cs.length; i < len; i++) {
2817 cs[i].setOwnerTree(tree);
2820 tree.registerNode(this);
2826 * Returns the path for this node. The path can be used to expand or select this node programmatically.
2827 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2828 * @return {String} The path
2830 getPath : function(attr){
2831 attr = attr || "id";
2832 var p = this.parentNode;
2833 var b = [this.attributes[attr]];
2835 b.unshift(p.attributes[attr]);
2838 var sep = this.getOwnerTree().pathSeparator;
2839 return sep + b.join(sep);
2843 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2844 * function call will be the scope provided or the current node. The arguments to the function
2845 * will be the args provided or the current node. If the function returns false at any point,
2846 * the bubble is stopped.
2847 * @param {Function} fn The function to call
2848 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2849 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2851 bubble : function(fn, scope, args){
2854 if(fn.call(scope || p, args || p) === false){
2862 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2863 * function call will be the scope provided or the current node. The arguments to the function
2864 * will be the args provided or the current node. If the function returns false at any point,
2865 * the cascade is stopped on that branch.
2866 * @param {Function} fn The function to call
2867 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2868 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2870 cascade : function(fn, scope, args){
2871 if(fn.call(scope || this, args || this) !== false){
2872 var cs = this.childNodes;
2873 for(var i = 0, len = cs.length; i < len; i++) {
2874 cs[i].cascade(fn, scope, args);
2880 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2881 * function call will be the scope provided or the current node. The arguments to the function
2882 * will be the args provided or the current node. If the function returns false at any point,
2883 * the iteration stops.
2884 * @param {Function} fn The function to call
2885 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2886 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2888 eachChild : function(fn, scope, args){
2889 var cs = this.childNodes;
2890 for(var i = 0, len = cs.length; i < len; i++) {
2891 if(fn.call(scope || this, args || cs[i]) === false){
2898 * Finds the first child that has the attribute with the specified value.
2899 * @param {String} attribute The attribute name
2900 * @param {Mixed} value The value to search for
2901 * @return {Node} The found child or null if none was found
2903 findChild : function(attribute, value){
2904 var cs = this.childNodes;
2905 for(var i = 0, len = cs.length; i < len; i++) {
2906 if(cs[i].attributes[attribute] == value){
2914 * Finds the first child by a custom function. The child matches if the function passed
2916 * @param {Function} fn
2917 * @param {Object} scope (optional)
2918 * @return {Node} The found child or null if none was found
2920 findChildBy : function(fn, scope){
2921 var cs = this.childNodes;
2922 for(var i = 0, len = cs.length; i < len; i++) {
2923 if(fn.call(scope||cs[i], cs[i]) === true){
2931 * Sorts this nodes children using the supplied sort function
2932 * @param {Function} fn
2933 * @param {Object} scope (optional)
2935 sort : function(fn, scope){
2936 var cs = this.childNodes;
2937 var len = cs.length;
2939 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2941 for(var i = 0; i < len; i++){
2943 n.previousSibling = cs[i-1];
2944 n.nextSibling = cs[i+1];
2946 this.setFirstChild(n);
2949 this.setLastChild(n);
2956 * Returns true if this node is an ancestor (at any point) of the passed node.
2957 * @param {Node} node
2960 contains : function(node){
2961 return node.isAncestor(this);
2965 * Returns true if the passed node is an ancestor (at any point) of this node.
2966 * @param {Node} node
2969 isAncestor : function(node){
2970 var p = this.parentNode;
2980 toString : function(){
2981 return "[Node"+(this.id?" "+this.id:"")+"]";
2985 * Ext JS Library 1.1.1
2986 * Copyright(c) 2006-2007, Ext JS, LLC.
2988 * Originally Released Under LGPL - original licence link has changed is not relivant.
2991 * <script type="text/javascript">
2996 * @extends Roo.Element
2997 * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2998 * automatic maintaining of shadow/shim positions.
2999 * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
3000 * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
3001 * you can pass a string with a CSS class name. False turns off the shadow.
3002 * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
3003 * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
3004 * @cfg {String} cls CSS class to add to the element
3005 * @cfg {Number} zindex Starting z-index (defaults to 11000)
3006 * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
3008 * @param {Object} config An object with config options.
3009 * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
3012 Roo.Layer = function(config, existingEl){
3013 config = config || {};
3014 var dh = Roo.DomHelper;
3015 var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
3017 this.dom = Roo.getDom(existingEl);
3020 var o = config.dh || {tag: "div", cls: "x-layer"};
3021 this.dom = dh.append(pel, o);
3024 this.addClass(config.cls);
3026 this.constrain = config.constrain !== false;
3027 this.visibilityMode = Roo.Element.VISIBILITY;
3029 this.id = this.dom.id = config.id;
3031 this.id = Roo.id(this.dom);
3033 this.zindex = config.zindex || this.getZIndex();
3034 this.position("absolute", this.zindex);
3036 this.shadowOffset = config.shadowOffset || 4;
3037 this.shadow = new Roo.Shadow({
3038 offset : this.shadowOffset,
3039 mode : config.shadow
3042 this.shadowOffset = 0;
3044 this.useShim = config.shim !== false && Roo.useShims;
3045 this.useDisplay = config.useDisplay;
3049 var supr = Roo.Element.prototype;
3051 // shims are shared among layer to keep from having 100 iframes
3054 Roo.extend(Roo.Layer, Roo.Element, {
3056 getZIndex : function(){
3057 return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3060 getShim : function(){
3067 var shim = shims.shift();
3069 shim = this.createShim();
3070 shim.enableDisplayMode('block');
3071 shim.dom.style.display = 'none';
3072 shim.dom.style.visibility = 'visible';
3074 var pn = this.dom.parentNode;
3075 if(shim.dom.parentNode != pn){
3076 pn.insertBefore(shim.dom, this.dom);
3078 shim.setStyle('z-index', this.getZIndex()-2);
3083 hideShim : function(){
3085 this.shim.setDisplayed(false);
3086 shims.push(this.shim);
3091 disableShadow : function(){
3093 this.shadowDisabled = true;
3095 this.lastShadowOffset = this.shadowOffset;
3096 this.shadowOffset = 0;
3100 enableShadow : function(show){
3102 this.shadowDisabled = false;
3103 this.shadowOffset = this.lastShadowOffset;
3104 delete this.lastShadowOffset;
3112 // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3113 // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3114 sync : function(doShow){
3115 var sw = this.shadow;
3116 if(!this.updating && this.isVisible() && (sw || this.useShim)){
3117 var sh = this.getShim();
3119 var w = this.getWidth(),
3120 h = this.getHeight();
3122 var l = this.getLeft(true),
3123 t = this.getTop(true);
3125 if(sw && !this.shadowDisabled){
3126 if(doShow && !sw.isVisible()){
3129 sw.realign(l, t, w, h);
3135 // fit the shim behind the shadow, so it is shimmed too
3136 var a = sw.adjusts, s = sh.dom.style;
3137 s.left = (Math.min(l, l+a.l))+"px";
3138 s.top = (Math.min(t, t+a.t))+"px";
3139 s.width = (w+a.w)+"px";
3140 s.height = (h+a.h)+"px";
3147 sh.setLeftTop(l, t);
3154 destroy : function(){
3159 this.removeAllListeners();
3160 var pn = this.dom.parentNode;
3162 pn.removeChild(this.dom);
3164 Roo.Element.uncache(this.id);
3167 remove : function(){
3172 beginUpdate : function(){
3173 this.updating = true;
3177 endUpdate : function(){
3178 this.updating = false;
3183 hideUnders : function(negOffset){
3191 constrainXY : function(){
3193 var vw = Roo.lib.Dom.getViewWidth(),
3194 vh = Roo.lib.Dom.getViewHeight();
3195 var s = Roo.get(document).getScroll();
3197 var xy = this.getXY();
3198 var x = xy[0], y = xy[1];
3199 var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3200 // only move it if it needs it
3202 // first validate right/bottom
3203 if((x + w) > vw+s.left){
3204 x = vw - w - this.shadowOffset;
3207 if((y + h) > vh+s.top){
3208 y = vh - h - this.shadowOffset;
3211 // then make sure top/left isn't negative
3222 var ay = this.avoidY;
3223 if(y <= ay && (y+h) >= ay){
3229 supr.setXY.call(this, xy);
3235 isVisible : function(){
3236 return this.visible;
3240 showAction : function(){
3241 this.visible = true; // track visibility to prevent getStyle calls
3242 if(this.useDisplay === true){
3243 this.setDisplayed("");
3244 }else if(this.lastXY){
3245 supr.setXY.call(this, this.lastXY);
3246 }else if(this.lastLT){
3247 supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3252 hideAction : function(){
3253 this.visible = false;
3254 if(this.useDisplay === true){
3255 this.setDisplayed(false);
3257 this.setLeftTop(-10000,-10000);
3261 // overridden Element method
3262 setVisible : function(v, a, d, c, e){
3267 var cb = function(){
3272 }.createDelegate(this);
3273 supr.setVisible.call(this, true, true, d, cb, e);
3276 this.hideUnders(true);
3285 }.createDelegate(this);
3287 supr.setVisible.call(this, v, a, d, cb, e);
3296 storeXY : function(xy){
3301 storeLeftTop : function(left, top){
3303 this.lastLT = [left, top];
3307 beforeFx : function(){
3308 this.beforeAction();
3309 return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3313 afterFx : function(){
3314 Roo.Layer.superclass.afterFx.apply(this, arguments);
3315 this.sync(this.isVisible());
3319 beforeAction : function(){
3320 if(!this.updating && this.shadow){
3325 // overridden Element method
3326 setLeft : function(left){
3327 this.storeLeftTop(left, this.getTop(true));
3328 supr.setLeft.apply(this, arguments);
3332 setTop : function(top){
3333 this.storeLeftTop(this.getLeft(true), top);
3334 supr.setTop.apply(this, arguments);
3338 setLeftTop : function(left, top){
3339 this.storeLeftTop(left, top);
3340 supr.setLeftTop.apply(this, arguments);
3344 setXY : function(xy, a, d, c, e){
3346 this.beforeAction();
3348 var cb = this.createCB(c);
3349 supr.setXY.call(this, xy, a, d, cb, e);
3356 createCB : function(c){
3367 // overridden Element method
3368 setX : function(x, a, d, c, e){
3369 this.setXY([x, this.getY()], a, d, c, e);
3372 // overridden Element method
3373 setY : function(y, a, d, c, e){
3374 this.setXY([this.getX(), y], a, d, c, e);
3377 // overridden Element method
3378 setSize : function(w, h, a, d, c, e){
3379 this.beforeAction();
3380 var cb = this.createCB(c);
3381 supr.setSize.call(this, w, h, a, d, cb, e);
3387 // overridden Element method
3388 setWidth : function(w, a, d, c, e){
3389 this.beforeAction();
3390 var cb = this.createCB(c);
3391 supr.setWidth.call(this, w, a, d, cb, e);
3397 // overridden Element method
3398 setHeight : function(h, a, d, c, e){
3399 this.beforeAction();
3400 var cb = this.createCB(c);
3401 supr.setHeight.call(this, h, a, d, cb, e);
3407 // overridden Element method
3408 setBounds : function(x, y, w, h, a, d, c, e){
3409 this.beforeAction();
3410 var cb = this.createCB(c);
3412 this.storeXY([x, y]);
3413 supr.setXY.call(this, [x, y]);
3414 supr.setSize.call(this, w, h, a, d, cb, e);
3417 supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3423 * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3424 * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3425 * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3426 * @param {Number} zindex The new z-index to set
3427 * @return {this} The Layer
3429 setZIndex : function(zindex){
3430 this.zindex = zindex;
3431 this.setStyle("z-index", zindex + 2);
3433 this.shadow.setZIndex(zindex + 1);
3436 this.shim.setStyle("z-index", zindex);
3442 * Ext JS Library 1.1.1
3443 * Copyright(c) 2006-2007, Ext JS, LLC.
3445 * Originally Released Under LGPL - original licence link has changed is not relivant.
3448 * <script type="text/javascript">
3454 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
3455 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
3456 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3458 * Create a new Shadow
3459 * @param {Object} config The config object
3461 Roo.Shadow = function(config){
3462 Roo.apply(this, config);
3463 if(typeof this.mode != "string"){
3464 this.mode = this.defaultMode;
3466 var o = this.offset, a = {h: 0};
3467 var rad = Math.floor(this.offset/2);
3468 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3474 a.l -= this.offset + rad;
3475 a.t -= this.offset + rad;
3486 a.l -= (this.offset - rad);
3487 a.t -= this.offset + rad;
3489 a.w -= (this.offset - rad)*2;
3500 a.l -= (this.offset - rad);
3501 a.t -= (this.offset - rad);
3503 a.w -= (this.offset + rad + 1);
3504 a.h -= (this.offset + rad);
3513 Roo.Shadow.prototype = {
3515 * @cfg {String} mode
3516 * The shadow display mode. Supports the following options:<br />
3517 * sides: Shadow displays on both sides and bottom only<br />
3518 * frame: Shadow displays equally on all four sides<br />
3519 * drop: Traditional bottom-right drop shadow (default)
3522 * @cfg {String} offset
3523 * The number of pixels to offset the shadow from the element (defaults to 4)
3528 defaultMode: "drop",
3531 * Displays the shadow under the target element
3532 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3534 show : function(target){
3535 target = Roo.get(target);
3537 this.el = Roo.Shadow.Pool.pull();
3538 if(this.el.dom.nextSibling != target.dom){
3539 this.el.insertBefore(target);
3542 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3544 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3547 target.getLeft(true),
3548 target.getTop(true),
3552 this.el.dom.style.display = "block";
3556 * Returns true if the shadow is visible, else false
3558 isVisible : function(){
3559 return this.el ? true : false;
3563 * Direct alignment when values are already available. Show must be called at least once before
3564 * calling this method to ensure it is initialized.
3565 * @param {Number} left The target element left position
3566 * @param {Number} top The target element top position
3567 * @param {Number} width The target element width
3568 * @param {Number} height The target element height
3570 realign : function(l, t, w, h){
3574 var a = this.adjusts, d = this.el.dom, s = d.style;
3576 s.left = (l+a.l)+"px";
3577 s.top = (t+a.t)+"px";
3578 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3580 if(s.width != sws || s.height != shs){
3584 var cn = d.childNodes;
3585 var sww = Math.max(0, (sw-12))+"px";
3586 cn[0].childNodes[1].style.width = sww;
3587 cn[1].childNodes[1].style.width = sww;
3588 cn[2].childNodes[1].style.width = sww;
3589 cn[1].style.height = Math.max(0, (sh-12))+"px";
3599 this.el.dom.style.display = "none";
3600 Roo.Shadow.Pool.push(this.el);
3606 * Adjust the z-index of this shadow
3607 * @param {Number} zindex The new z-index
3609 setZIndex : function(z){
3612 this.el.setStyle("z-index", z);
3617 // Private utility class that manages the internal Shadow cache
3618 Roo.Shadow.Pool = function(){
3620 var markup = Roo.isIE ?
3621 '<div class="x-ie-shadow"></div>' :
3622 '<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>';
3627 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3628 sh.autoBoxAdjust = false;
3633 push : function(sh){
3639 * Ext JS Library 1.1.1
3640 * Copyright(c) 2006-2007, Ext JS, LLC.
3642 * Originally Released Under LGPL - original licence link has changed is not relivant.
3645 * <script type="text/javascript">
3650 * @class Roo.SplitBar
3651 * @extends Roo.util.Observable
3652 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3656 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3657 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3658 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3659 split.minSize = 100;
3660 split.maxSize = 600;
3661 split.animate = true;
3662 split.on('moved', splitterMoved);
3665 * Create a new SplitBar
3666 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
3667 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
3668 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3669 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
3670 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3671 position of the SplitBar).
3673 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3676 this.el = Roo.get(dragElement, true);
3677 this.el.dom.unselectable = "on";
3679 this.resizingEl = Roo.get(resizingElement, true);
3683 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3684 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3687 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3690 * The minimum size of the resizing element. (Defaults to 0)
3696 * The maximum size of the resizing element. (Defaults to 2000)
3699 this.maxSize = 2000;
3702 * Whether to animate the transition to the new size
3705 this.animate = false;
3708 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3711 this.useShim = false;
3718 this.proxy = Roo.SplitBar.createProxy(this.orientation);
3720 this.proxy = Roo.get(existingProxy).dom;
3723 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3726 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3729 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3732 this.dragSpecs = {};
3735 * @private The adapter to use to positon and resize elements
3737 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3738 this.adapter.init(this);
3740 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3742 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3743 this.el.addClass("x-splitbar-h");
3746 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3747 this.el.addClass("x-splitbar-v");
3753 * Fires when the splitter is moved (alias for {@link #event-moved})
3754 * @param {Roo.SplitBar} this
3755 * @param {Number} newSize the new width or height
3760 * Fires when the splitter is moved
3761 * @param {Roo.SplitBar} this
3762 * @param {Number} newSize the new width or height
3766 * @event beforeresize
3767 * Fires before the splitter is dragged
3768 * @param {Roo.SplitBar} this
3770 "beforeresize" : true,
3772 "beforeapply" : true
3775 Roo.util.Observable.call(this);
3778 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3779 onStartProxyDrag : function(x, y){
3780 this.fireEvent("beforeresize", this);
3782 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
3784 o.enableDisplayMode("block");
3785 // all splitbars share the same overlay
3786 Roo.SplitBar.prototype.overlay = o;
3788 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3789 this.overlay.show();
3790 Roo.get(this.proxy).setDisplayed("block");
3791 var size = this.adapter.getElementSize(this);
3792 this.activeMinSize = this.getMinimumSize();;
3793 this.activeMaxSize = this.getMaximumSize();;
3794 var c1 = size - this.activeMinSize;
3795 var c2 = Math.max(this.activeMaxSize - size, 0);
3796 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3797 this.dd.resetConstraints();
3798 this.dd.setXConstraint(
3799 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
3800 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3802 this.dd.setYConstraint(0, 0);
3804 this.dd.resetConstraints();
3805 this.dd.setXConstraint(0, 0);
3806 this.dd.setYConstraint(
3807 this.placement == Roo.SplitBar.TOP ? c1 : c2,
3808 this.placement == Roo.SplitBar.TOP ? c2 : c1
3811 this.dragSpecs.startSize = size;
3812 this.dragSpecs.startPoint = [x, y];
3813 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3817 * @private Called after the drag operation by the DDProxy
3819 onEndProxyDrag : function(e){
3820 Roo.get(this.proxy).setDisplayed(false);
3821 var endPoint = Roo.lib.Event.getXY(e);
3823 this.overlay.hide();
3826 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3827 newSize = this.dragSpecs.startSize +
3828 (this.placement == Roo.SplitBar.LEFT ?
3829 endPoint[0] - this.dragSpecs.startPoint[0] :
3830 this.dragSpecs.startPoint[0] - endPoint[0]
3833 newSize = this.dragSpecs.startSize +
3834 (this.placement == Roo.SplitBar.TOP ?
3835 endPoint[1] - this.dragSpecs.startPoint[1] :
3836 this.dragSpecs.startPoint[1] - endPoint[1]
3839 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3840 if(newSize != this.dragSpecs.startSize){
3841 if(this.fireEvent('beforeapply', this, newSize) !== false){
3842 this.adapter.setElementSize(this, newSize);
3843 this.fireEvent("moved", this, newSize);
3844 this.fireEvent("resize", this, newSize);
3850 * Get the adapter this SplitBar uses
3851 * @return The adapter object
3853 getAdapter : function(){
3854 return this.adapter;
3858 * Set the adapter this SplitBar uses
3859 * @param {Object} adapter A SplitBar adapter object
3861 setAdapter : function(adapter){
3862 this.adapter = adapter;
3863 this.adapter.init(this);
3867 * Gets the minimum size for the resizing element
3868 * @return {Number} The minimum size
3870 getMinimumSize : function(){
3871 return this.minSize;
3875 * Sets the minimum size for the resizing element
3876 * @param {Number} minSize The minimum size
3878 setMinimumSize : function(minSize){
3879 this.minSize = minSize;
3883 * Gets the maximum size for the resizing element
3884 * @return {Number} The maximum size
3886 getMaximumSize : function(){
3887 return this.maxSize;
3891 * Sets the maximum size for the resizing element
3892 * @param {Number} maxSize The maximum size
3894 setMaximumSize : function(maxSize){
3895 this.maxSize = maxSize;
3899 * Sets the initialize size for the resizing element
3900 * @param {Number} size The initial size
3902 setCurrentSize : function(size){
3903 var oldAnimate = this.animate;
3904 this.animate = false;
3905 this.adapter.setElementSize(this, size);
3906 this.animate = oldAnimate;
3910 * Destroy this splitbar.
3911 * @param {Boolean} removeEl True to remove the element
3913 destroy : function(removeEl){
3918 this.proxy.parentNode.removeChild(this.proxy);
3926 * @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.
3928 Roo.SplitBar.createProxy = function(dir){
3929 var proxy = new Roo.Element(document.createElement("div"));
3930 proxy.unselectable();
3931 var cls = 'x-splitbar-proxy';
3932 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3933 document.body.appendChild(proxy.dom);
3938 * @class Roo.SplitBar.BasicLayoutAdapter
3939 * Default Adapter. It assumes the splitter and resizing element are not positioned
3940 * elements and only gets/sets the width of the element. Generally used for table based layouts.
3942 Roo.SplitBar.BasicLayoutAdapter = function(){
3945 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3946 // do nothing for now
3951 * Called before drag operations to get the current size of the resizing element.
3952 * @param {Roo.SplitBar} s The SplitBar using this adapter
3954 getElementSize : function(s){
3955 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3956 return s.resizingEl.getWidth();
3958 return s.resizingEl.getHeight();
3963 * Called after drag operations to set the size of the resizing element.
3964 * @param {Roo.SplitBar} s The SplitBar using this adapter
3965 * @param {Number} newSize The new size to set
3966 * @param {Function} onComplete A function to be invoked when resizing is complete
3968 setElementSize : function(s, newSize, onComplete){
3969 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3971 s.resizingEl.setWidth(newSize);
3973 onComplete(s, newSize);
3976 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3981 s.resizingEl.setHeight(newSize);
3983 onComplete(s, newSize);
3986 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3993 *@class Roo.SplitBar.AbsoluteLayoutAdapter
3994 * @extends Roo.SplitBar.BasicLayoutAdapter
3995 * Adapter that moves the splitter element to align with the resized sizing element.
3996 * Used with an absolute positioned SplitBar.
3997 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3998 * document.body, make sure you assign an id to the body element.
4000 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
4001 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
4002 this.container = Roo.get(container);
4005 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
4010 getElementSize : function(s){
4011 return this.basic.getElementSize(s);
4014 setElementSize : function(s, newSize, onComplete){
4015 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
4018 moveSplitter : function(s){
4019 var yes = Roo.SplitBar;
4020 switch(s.placement){
4022 s.el.setX(s.resizingEl.getRight());
4025 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
4028 s.el.setY(s.resizingEl.getBottom());
4031 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
4038 * Orientation constant - Create a vertical SplitBar
4042 Roo.SplitBar.VERTICAL = 1;
4045 * Orientation constant - Create a horizontal SplitBar
4049 Roo.SplitBar.HORIZONTAL = 2;
4052 * Placement constant - The resizing element is to the left of the splitter element
4056 Roo.SplitBar.LEFT = 1;
4059 * Placement constant - The resizing element is to the right of the splitter element
4063 Roo.SplitBar.RIGHT = 2;
4066 * Placement constant - The resizing element is positioned above the splitter element
4070 Roo.SplitBar.TOP = 3;
4073 * Placement constant - The resizing element is positioned under splitter element
4077 Roo.SplitBar.BOTTOM = 4;
4080 * Ext JS Library 1.1.1
4081 * Copyright(c) 2006-2007, Ext JS, LLC.
4083 * Originally Released Under LGPL - original licence link has changed is not relivant.
4086 * <script type="text/javascript">
4091 * @extends Roo.util.Observable
4092 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
4093 * This class also supports single and multi selection modes. <br>
4094 * Create a data model bound view:
4096 var store = new Roo.data.Store(...);
4098 var view = new Roo.View({
4100 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
4103 selectedClass: "ydataview-selected",
4107 // listen for node click?
4108 view.on("click", function(vw, index, node, e){
4109 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4113 dataModel.load("foobar.xml");
4115 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4117 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4118 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4120 * Note: old style constructor is still suported (container, template, config)
4124 * @param {Object} config The config object
4127 Roo.View = function(config, depreciated_tpl, depreciated_config){
4129 this.parent = false;
4131 if (typeof(depreciated_tpl) == 'undefined') {
4132 // new way.. - universal constructor.
4133 Roo.apply(this, config);
4134 this.el = Roo.get(this.el);
4137 this.el = Roo.get(config);
4138 this.tpl = depreciated_tpl;
4139 Roo.apply(this, depreciated_config);
4141 this.wrapEl = this.el.wrap().wrap();
4142 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4145 if(typeof(this.tpl) == "string"){
4146 this.tpl = new Roo.Template(this.tpl);
4148 // support xtype ctors..
4149 this.tpl = new Roo.factory(this.tpl, Roo);
4158 * @event beforeclick
4159 * Fires before a click is processed. Returns false to cancel the default action.
4160 * @param {Roo.View} this
4161 * @param {Number} index The index of the target node
4162 * @param {HTMLElement} node The target node
4163 * @param {Roo.EventObject} e The raw event object
4165 "beforeclick" : true,
4168 * Fires when a template node is clicked.
4169 * @param {Roo.View} this
4170 * @param {Number} index The index of the target node
4171 * @param {HTMLElement} node The target node
4172 * @param {Roo.EventObject} e The raw event object
4177 * Fires when a template node is double clicked.
4178 * @param {Roo.View} this
4179 * @param {Number} index The index of the target node
4180 * @param {HTMLElement} node The target node
4181 * @param {Roo.EventObject} e The raw event object
4185 * @event contextmenu
4186 * Fires when a template node is right clicked.
4187 * @param {Roo.View} this
4188 * @param {Number} index The index of the target node
4189 * @param {HTMLElement} node The target node
4190 * @param {Roo.EventObject} e The raw event object
4192 "contextmenu" : true,
4194 * @event selectionchange
4195 * Fires when the selected nodes change.
4196 * @param {Roo.View} this
4197 * @param {Array} selections Array of the selected nodes
4199 "selectionchange" : true,
4202 * @event beforeselect
4203 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4204 * @param {Roo.View} this
4205 * @param {HTMLElement} node The node to be selected
4206 * @param {Array} selections Array of currently selected nodes
4208 "beforeselect" : true,
4210 * @event preparedata
4211 * Fires on every row to render, to allow you to change the data.
4212 * @param {Roo.View} this
4213 * @param {Object} data to be rendered (change this)
4215 "preparedata" : true
4223 "click": this.onClick,
4224 "dblclick": this.onDblClick,
4225 "contextmenu": this.onContextMenu,
4229 this.selections = [];
4231 this.cmp = new Roo.CompositeElementLite([]);
4233 this.store = Roo.factory(this.store, Roo.data);
4234 this.setStore(this.store, true);
4237 if ( this.footer && this.footer.xtype) {
4239 var fctr = this.wrapEl.appendChild(document.createElement("div"));
4241 this.footer.dataSource = this.store;
4242 this.footer.container = fctr;
4243 this.footer = Roo.factory(this.footer, Roo);
4244 fctr.insertFirst(this.el);
4246 // this is a bit insane - as the paging toolbar seems to detach the el..
4247 // dom.parentNode.parentNode.parentNode
4248 // they get detached?
4252 Roo.View.superclass.constructor.call(this);
4257 Roo.extend(Roo.View, Roo.util.Observable, {
4260 * @cfg {Roo.data.Store} store Data store to load data from.
4265 * @cfg {String|Roo.Element} el The container element.
4270 * @cfg {String|Roo.Template} tpl The template used by this View
4274 * @cfg {String} dataName the named area of the template to use as the data area
4275 * Works with domtemplates roo-name="name"
4279 * @cfg {String} selectedClass The css class to add to selected nodes
4281 selectedClass : "x-view-selected",
4283 * @cfg {String} emptyText The empty text to show when nothing is loaded.
4288 * @cfg {String} text to display on mask (default Loading)
4292 * @cfg {Boolean} multiSelect Allow multiple selection
4294 multiSelect : false,
4296 * @cfg {Boolean} singleSelect Allow single selection
4298 singleSelect: false,
4301 * @cfg {Boolean} toggleSelect - selecting
4303 toggleSelect : false,
4306 * @cfg {Boolean} tickable - selecting
4311 * Returns the element this view is bound to.
4312 * @return {Roo.Element}
4321 * Refreshes the view. - called by datachanged on the store. - do not call directly.
4323 refresh : function(){
4324 //Roo.log('refresh');
4327 // if we are using something like 'domtemplate', then
4328 // the what gets used is:
4329 // t.applySubtemplate(NAME, data, wrapping data..)
4330 // the outer template then get' applied with
4331 // the store 'extra data'
4332 // and the body get's added to the
4333 // roo-name="data" node?
4334 // <span class='roo-tpl-{name}'></span> ?????
4338 this.clearSelections();
4341 var records = this.store.getRange();
4342 if(records.length < 1) {
4344 // is this valid?? = should it render a template??
4346 this.el.update(this.emptyText);
4350 if (this.dataName) {
4351 this.el.update(t.apply(this.store.meta)); //????
4352 el = this.el.child('.roo-tpl-' + this.dataName);
4355 for(var i = 0, len = records.length; i < len; i++){
4356 var data = this.prepareData(records[i].data, i, records[i]);
4357 this.fireEvent("preparedata", this, data, i, records[i]);
4359 var d = Roo.apply({}, data);
4362 Roo.apply(d, {'roo-id' : Roo.id()});
4366 Roo.each(this.parent.item, function(item){
4367 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4370 Roo.apply(d, {'roo-data-checked' : 'checked'});
4374 html[html.length] = Roo.util.Format.trim(
4376 t.applySubtemplate(this.dataName, d, this.store.meta) :
4383 el.update(html.join(""));
4384 this.nodes = el.dom.childNodes;
4385 this.updateIndexes(0);
4390 * Function to override to reformat the data that is sent to
4391 * the template for each node.
4392 * DEPRICATED - use the preparedata event handler.
4393 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4394 * a JSON object for an UpdateManager bound view).
4396 prepareData : function(data, index, record)
4398 this.fireEvent("preparedata", this, data, index, record);
4402 onUpdate : function(ds, record){
4403 // Roo.log('on update');
4404 this.clearSelections();
4405 var index = this.store.indexOf(record);
4406 var n = this.nodes[index];
4407 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4408 n.parentNode.removeChild(n);
4409 this.updateIndexes(index, index);
4415 onAdd : function(ds, records, index)
4417 //Roo.log(['on Add', ds, records, index] );
4418 this.clearSelections();
4419 if(this.nodes.length == 0){
4423 var n = this.nodes[index];
4424 for(var i = 0, len = records.length; i < len; i++){
4425 var d = this.prepareData(records[i].data, i, records[i]);
4427 this.tpl.insertBefore(n, d);
4430 this.tpl.append(this.el, d);
4433 this.updateIndexes(index);
4436 onRemove : function(ds, record, index){
4437 // Roo.log('onRemove');
4438 this.clearSelections();
4439 var el = this.dataName ?
4440 this.el.child('.roo-tpl-' + this.dataName) :
4443 el.dom.removeChild(this.nodes[index]);
4444 this.updateIndexes(index);
4448 * Refresh an individual node.
4449 * @param {Number} index
4451 refreshNode : function(index){
4452 this.onUpdate(this.store, this.store.getAt(index));
4455 updateIndexes : function(startIndex, endIndex){
4456 var ns = this.nodes;
4457 startIndex = startIndex || 0;
4458 endIndex = endIndex || ns.length - 1;
4459 for(var i = startIndex; i <= endIndex; i++){
4460 ns[i].nodeIndex = i;
4465 * Changes the data store this view uses and refresh the view.
4466 * @param {Store} store
4468 setStore : function(store, initial){
4469 if(!initial && this.store){
4470 this.store.un("datachanged", this.refresh);
4471 this.store.un("add", this.onAdd);
4472 this.store.un("remove", this.onRemove);
4473 this.store.un("update", this.onUpdate);
4474 this.store.un("clear", this.refresh);
4475 this.store.un("beforeload", this.onBeforeLoad);
4476 this.store.un("load", this.onLoad);
4477 this.store.un("loadexception", this.onLoad);
4481 store.on("datachanged", this.refresh, this);
4482 store.on("add", this.onAdd, this);
4483 store.on("remove", this.onRemove, this);
4484 store.on("update", this.onUpdate, this);
4485 store.on("clear", this.refresh, this);
4486 store.on("beforeload", this.onBeforeLoad, this);
4487 store.on("load", this.onLoad, this);
4488 store.on("loadexception", this.onLoad, this);
4496 * onbeforeLoad - masks the loading area.
4499 onBeforeLoad : function(store,opts)
4501 //Roo.log('onBeforeLoad');
4505 this.el.mask(this.mask ? this.mask : "Loading" );
4507 onLoad : function ()
4514 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4515 * @param {HTMLElement} node
4516 * @return {HTMLElement} The template node
4518 findItemFromChild : function(node){
4519 var el = this.dataName ?
4520 this.el.child('.roo-tpl-' + this.dataName,true) :
4523 if(!node || node.parentNode == el){
4526 var p = node.parentNode;
4527 while(p && p != el){
4528 if(p.parentNode == el){
4537 onClick : function(e){
4538 var item = this.findItemFromChild(e.getTarget());
4540 var index = this.indexOf(item);
4541 if(this.onItemClick(item, index, e) !== false){
4542 this.fireEvent("click", this, index, item, e);
4545 this.clearSelections();
4550 onContextMenu : function(e){
4551 var item = this.findItemFromChild(e.getTarget());
4553 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4558 onDblClick : function(e){
4559 var item = this.findItemFromChild(e.getTarget());
4561 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4565 onItemClick : function(item, index, e)
4567 if(this.fireEvent("beforeclick", this, index, item, e) === false){
4570 if (this.toggleSelect) {
4571 var m = this.isSelected(item) ? 'unselect' : 'select';
4574 _t[m](item, true, false);
4577 if(this.multiSelect || this.singleSelect){
4578 if(this.multiSelect && e.shiftKey && this.lastSelection){
4579 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4581 this.select(item, this.multiSelect && e.ctrlKey);
4582 this.lastSelection = item;
4594 * Get the number of selected nodes.
4597 getSelectionCount : function(){
4598 return this.selections.length;
4602 * Get the currently selected nodes.
4603 * @return {Array} An array of HTMLElements
4605 getSelectedNodes : function(){
4606 return this.selections;
4610 * Get the indexes of the selected nodes.
4613 getSelectedIndexes : function(){
4614 var indexes = [], s = this.selections;
4615 for(var i = 0, len = s.length; i < len; i++){
4616 indexes.push(s[i].nodeIndex);
4622 * Clear all selections
4623 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4625 clearSelections : function(suppressEvent){
4626 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4627 this.cmp.elements = this.selections;
4628 this.cmp.removeClass(this.selectedClass);
4629 this.selections = [];
4631 this.fireEvent("selectionchange", this, this.selections);
4637 * Returns true if the passed node is selected
4638 * @param {HTMLElement/Number} node The node or node index
4641 isSelected : function(node){
4642 var s = this.selections;
4646 node = this.getNode(node);
4647 return s.indexOf(node) !== -1;
4652 * @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
4653 * @param {Boolean} keepExisting (optional) true to keep existing selections
4654 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4656 select : function(nodeInfo, keepExisting, suppressEvent){
4657 if(nodeInfo instanceof Array){
4659 this.clearSelections(true);
4661 for(var i = 0, len = nodeInfo.length; i < len; i++){
4662 this.select(nodeInfo[i], true, true);
4666 var node = this.getNode(nodeInfo);
4667 if(!node || this.isSelected(node)){
4668 return; // already selected.
4671 this.clearSelections(true);
4674 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4675 Roo.fly(node).addClass(this.selectedClass);
4676 this.selections.push(node);
4678 this.fireEvent("selectionchange", this, this.selections);
4686 * @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
4687 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4688 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4690 unselect : function(nodeInfo, keepExisting, suppressEvent)
4692 if(nodeInfo instanceof Array){
4693 Roo.each(this.selections, function(s) {
4694 this.unselect(s, nodeInfo);
4698 var node = this.getNode(nodeInfo);
4699 if(!node || !this.isSelected(node)){
4700 //Roo.log("not selected");
4701 return; // not selected.
4705 Roo.each(this.selections, function(s) {
4707 Roo.fly(node).removeClass(this.selectedClass);
4714 this.selections= ns;
4715 this.fireEvent("selectionchange", this, this.selections);
4719 * Gets a template node.
4720 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4721 * @return {HTMLElement} The node or null if it wasn't found
4723 getNode : function(nodeInfo){
4724 if(typeof nodeInfo == "string"){
4725 return document.getElementById(nodeInfo);
4726 }else if(typeof nodeInfo == "number"){
4727 return this.nodes[nodeInfo];
4733 * Gets a range template nodes.
4734 * @param {Number} startIndex
4735 * @param {Number} endIndex
4736 * @return {Array} An array of nodes
4738 getNodes : function(start, end){
4739 var ns = this.nodes;
4741 end = typeof end == "undefined" ? ns.length - 1 : end;
4744 for(var i = start; i <= end; i++){
4748 for(var i = start; i >= end; i--){
4756 * Finds the index of the passed node
4757 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4758 * @return {Number} The index of the node or -1
4760 indexOf : function(node){
4761 node = this.getNode(node);
4762 if(typeof node.nodeIndex == "number"){
4763 return node.nodeIndex;
4765 var ns = this.nodes;
4766 for(var i = 0, len = ns.length; i < len; i++){
4776 * Ext JS Library 1.1.1
4777 * Copyright(c) 2006-2007, Ext JS, LLC.
4779 * Originally Released Under LGPL - original licence link has changed is not relivant.
4782 * <script type="text/javascript">
4786 * @class Roo.JsonView
4788 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4790 var view = new Roo.JsonView({
4791 container: "my-element",
4792 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
4797 // listen for node click?
4798 view.on("click", function(vw, index, node, e){
4799 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4802 // direct load of JSON data
4803 view.load("foobar.php");
4805 // Example from my blog list
4806 var tpl = new Roo.Template(
4807 '<div class="entry">' +
4808 '<a class="entry-title" href="{link}">{title}</a>' +
4809 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
4810 "</div><hr />"
4813 var moreView = new Roo.JsonView({
4814 container : "entry-list",
4818 moreView.on("beforerender", this.sortEntries, this);
4820 url: "/blog/get-posts.php",
4821 params: "allposts=true",
4822 text: "Loading Blog Entries..."
4826 * Note: old code is supported with arguments : (container, template, config)
4830 * Create a new JsonView
4832 * @param {Object} config The config object
4835 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4838 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4840 var um = this.el.getUpdateManager();
4841 um.setRenderer(this);
4842 um.on("update", this.onLoad, this);
4843 um.on("failure", this.onLoadException, this);
4846 * @event beforerender
4847 * Fires before rendering of the downloaded JSON data.
4848 * @param {Roo.JsonView} this
4849 * @param {Object} data The JSON data loaded
4853 * Fires when data is loaded.
4854 * @param {Roo.JsonView} this
4855 * @param {Object} data The JSON data loaded
4856 * @param {Object} response The raw Connect response object
4859 * @event loadexception
4860 * Fires when loading fails.
4861 * @param {Roo.JsonView} this
4862 * @param {Object} response The raw Connect response object
4865 'beforerender' : true,
4867 'loadexception' : true
4870 Roo.extend(Roo.JsonView, Roo.View, {
4872 * @type {String} The root property in the loaded JSON object that contains the data
4877 * Refreshes the view.
4879 refresh : function(){
4880 this.clearSelections();
4883 var o = this.jsonData;
4884 if(o && o.length > 0){
4885 for(var i = 0, len = o.length; i < len; i++){
4886 var data = this.prepareData(o[i], i, o);
4887 html[html.length] = this.tpl.apply(data);
4890 html.push(this.emptyText);
4892 this.el.update(html.join(""));
4893 this.nodes = this.el.dom.childNodes;
4894 this.updateIndexes(0);
4898 * 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.
4899 * @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:
4902 url: "your-url.php",
4903 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4904 callback: yourFunction,
4905 scope: yourObject, //(optional scope)
4913 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4914 * 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.
4915 * @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}
4916 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4917 * @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.
4920 var um = this.el.getUpdateManager();
4921 um.update.apply(um, arguments);
4924 // note - render is a standard framework call...
4925 // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4926 render : function(el, response){
4928 this.clearSelections();
4932 if (response != '') {
4933 o = Roo.util.JSON.decode(response.responseText);
4936 o = o[this.jsonRoot];
4942 * The current JSON data or null
4945 this.beforeRender();
4950 * Get the number of records in the current JSON dataset
4953 getCount : function(){
4954 return this.jsonData ? this.jsonData.length : 0;
4958 * Returns the JSON object for the specified node(s)
4959 * @param {HTMLElement/Array} node The node or an array of nodes
4960 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4961 * you get the JSON object for the node
4963 getNodeData : function(node){
4964 if(node instanceof Array){
4966 for(var i = 0, len = node.length; i < len; i++){
4967 data.push(this.getNodeData(node[i]));
4971 return this.jsonData[this.indexOf(node)] || null;
4974 beforeRender : function(){
4975 this.snapshot = this.jsonData;
4977 this.sort.apply(this, this.sortInfo);
4979 this.fireEvent("beforerender", this, this.jsonData);
4982 onLoad : function(el, o){
4983 this.fireEvent("load", this, this.jsonData, o);
4986 onLoadException : function(el, o){
4987 this.fireEvent("loadexception", this, o);
4991 * Filter the data by a specific property.
4992 * @param {String} property A property on your JSON objects
4993 * @param {String/RegExp} value Either string that the property values
4994 * should start with, or a RegExp to test against the property
4996 filter : function(property, value){
4999 var ss = this.snapshot;
5000 if(typeof value == "string"){
5001 var vlen = value.length;
5006 value = value.toLowerCase();
5007 for(var i = 0, len = ss.length; i < len; i++){
5009 if(o[property].substr(0, vlen).toLowerCase() == value){
5013 } else if(value.exec){ // regex?
5014 for(var i = 0, len = ss.length; i < len; i++){
5016 if(value.test(o[property])){
5023 this.jsonData = data;
5029 * Filter by a function. The passed function will be called with each
5030 * object in the current dataset. If the function returns true the value is kept,
5031 * otherwise it is filtered.
5032 * @param {Function} fn
5033 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
5035 filterBy : function(fn, scope){
5038 var ss = this.snapshot;
5039 for(var i = 0, len = ss.length; i < len; i++){
5041 if(fn.call(scope || this, o)){
5045 this.jsonData = data;
5051 * Clears the current filter.
5053 clearFilter : function(){
5054 if(this.snapshot && this.jsonData != this.snapshot){
5055 this.jsonData = this.snapshot;
5062 * Sorts the data for this view and refreshes it.
5063 * @param {String} property A property on your JSON objects to sort on
5064 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5065 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5067 sort : function(property, dir, sortType){
5068 this.sortInfo = Array.prototype.slice.call(arguments, 0);
5071 var dsc = dir && dir.toLowerCase() == "desc";
5072 var f = function(o1, o2){
5073 var v1 = sortType ? sortType(o1[p]) : o1[p];
5074 var v2 = sortType ? sortType(o2[p]) : o2[p];
5077 return dsc ? +1 : -1;
5079 return dsc ? -1 : +1;
5084 this.jsonData.sort(f);
5086 if(this.jsonData != this.snapshot){
5087 this.snapshot.sort(f);
5093 * Ext JS Library 1.1.1
5094 * Copyright(c) 2006-2007, Ext JS, LLC.
5096 * Originally Released Under LGPL - original licence link has changed is not relivant.
5099 * <script type="text/javascript">
5104 * @class Roo.ColorPalette
5105 * @extends Roo.Component
5106 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
5107 * Here's an example of typical usage:
5109 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
5110 cp.render('my-div');
5112 cp.on('select', function(palette, selColor){
5113 // do something with selColor
5117 * Create a new ColorPalette
5118 * @param {Object} config The config object
5120 Roo.ColorPalette = function(config){
5121 Roo.ColorPalette.superclass.constructor.call(this, config);
5125 * Fires when a color is selected
5126 * @param {ColorPalette} this
5127 * @param {String} color The 6-digit color hex code (without the # symbol)
5133 this.on("select", this.handler, this.scope, true);
5136 Roo.extend(Roo.ColorPalette, Roo.Component, {
5138 * @cfg {String} itemCls
5139 * The CSS class to apply to the containing element (defaults to "x-color-palette")
5141 itemCls : "x-color-palette",
5143 * @cfg {String} value
5144 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
5145 * the hex codes are case-sensitive.
5150 ctype: "Roo.ColorPalette",
5153 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5155 allowReselect : false,
5158 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
5159 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
5160 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5161 * of colors with the width setting until the box is symmetrical.</p>
5162 * <p>You can override individual colors if needed:</p>
5164 var cp = new Roo.ColorPalette();
5165 cp.colors[0] = "FF0000"; // change the first box to red
5168 Or you can provide a custom array of your own for complete control:
5170 var cp = new Roo.ColorPalette();
5171 cp.colors = ["000000", "993300", "333300"];
5176 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5177 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5178 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5179 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5180 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5184 onRender : function(container, position){
5185 var t = new Roo.MasterTemplate(
5186 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
5188 var c = this.colors;
5189 for(var i = 0, len = c.length; i < len; i++){
5192 var el = document.createElement("div");
5193 el.className = this.itemCls;
5195 container.dom.insertBefore(el, position);
5196 this.el = Roo.get(el);
5197 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
5198 if(this.clickEvent != 'click'){
5199 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
5204 afterRender : function(){
5205 Roo.ColorPalette.superclass.afterRender.call(this);
5214 handleClick : function(e, t){
5217 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5218 this.select(c.toUpperCase());
5223 * Selects the specified color in the palette (fires the select event)
5224 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5226 select : function(color){
5227 color = color.replace("#", "");
5228 if(color != this.value || this.allowReselect){
5231 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5233 el.child("a.color-"+color).addClass("x-color-palette-sel");
5235 this.fireEvent("select", this, color);
5240 * Ext JS Library 1.1.1
5241 * Copyright(c) 2006-2007, Ext JS, LLC.
5243 * Originally Released Under LGPL - original licence link has changed is not relivant.
5246 * <script type="text/javascript">
5250 * @class Roo.DatePicker
5251 * @extends Roo.Component
5252 * Simple date picker class.
5254 * Create a new DatePicker
5255 * @param {Object} config The config object
5257 Roo.DatePicker = function(config){
5258 Roo.DatePicker.superclass.constructor.call(this, config);
5260 this.value = config && config.value ?
5261 config.value.clearTime() : new Date().clearTime();
5266 * Fires when a date is selected
5267 * @param {DatePicker} this
5268 * @param {Date} date The selected date
5272 * @event monthchange
5273 * Fires when the displayed month changes
5274 * @param {DatePicker} this
5275 * @param {Date} date The selected month
5281 this.on("select", this.handler, this.scope || this);
5283 // build the disabledDatesRE
5284 if(!this.disabledDatesRE && this.disabledDates){
5285 var dd = this.disabledDates;
5287 for(var i = 0; i < dd.length; i++){
5289 if(i != dd.length-1) {
5293 this.disabledDatesRE = new RegExp(re + ")");
5297 Roo.extend(Roo.DatePicker, Roo.Component, {
5299 * @cfg {String} todayText
5300 * The text to display on the button that selects the current date (defaults to "Today")
5302 todayText : "Today",
5304 * @cfg {String} okText
5305 * The text to display on the ok button
5307 okText : " OK ", //   to give the user extra clicking room
5309 * @cfg {String} cancelText
5310 * The text to display on the cancel button
5312 cancelText : "Cancel",
5314 * @cfg {String} todayTip
5315 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5317 todayTip : "{0} (Spacebar)",
5319 * @cfg {Date} minDate
5320 * Minimum allowable date (JavaScript date object, defaults to null)
5324 * @cfg {Date} maxDate
5325 * Maximum allowable date (JavaScript date object, defaults to null)
5329 * @cfg {String} minText
5330 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5332 minText : "This date is before the minimum date",
5334 * @cfg {String} maxText
5335 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5337 maxText : "This date is after the maximum date",
5339 * @cfg {String} format
5340 * The default date format string which can be overriden for localization support. The format must be
5341 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5345 * @cfg {Array} disabledDays
5346 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5348 disabledDays : null,
5350 * @cfg {String} disabledDaysText
5351 * The tooltip to display when the date falls on a disabled day (defaults to "")
5353 disabledDaysText : "",
5355 * @cfg {RegExp} disabledDatesRE
5356 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5358 disabledDatesRE : null,
5360 * @cfg {String} disabledDatesText
5361 * The tooltip text to display when the date falls on a disabled date (defaults to "")
5363 disabledDatesText : "",
5365 * @cfg {Boolean} constrainToViewport
5366 * True to constrain the date picker to the viewport (defaults to true)
5368 constrainToViewport : true,
5370 * @cfg {Array} monthNames
5371 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5373 monthNames : Date.monthNames,
5375 * @cfg {Array} dayNames
5376 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5378 dayNames : Date.dayNames,
5380 * @cfg {String} nextText
5381 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5383 nextText: 'Next Month (Control+Right)',
5385 * @cfg {String} prevText
5386 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5388 prevText: 'Previous Month (Control+Left)',
5390 * @cfg {String} monthYearText
5391 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5393 monthYearText: 'Choose a month (Control+Up/Down to move years)',
5395 * @cfg {Number} startDay
5396 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5400 * @cfg {Bool} showClear
5401 * Show a clear button (usefull for date form elements that can be blank.)
5407 * Sets the value of the date field
5408 * @param {Date} value The date to set
5410 setValue : function(value){
5411 var old = this.value;
5413 if (typeof(value) == 'string') {
5415 value = Date.parseDate(value, this.format);
5421 this.value = value.clearTime(true);
5423 this.update(this.value);
5428 * Gets the current selected value of the date field
5429 * @return {Date} The selected date
5431 getValue : function(){
5438 this.update(this.activeDate);
5443 onRender : function(container, position){
5446 '<table cellspacing="0">',
5447 '<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>',
5448 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5449 var dn = this.dayNames;
5450 for(var i = 0; i < 7; i++){
5451 var d = this.startDay+i;
5455 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5457 m[m.length] = "</tr></thead><tbody><tr>";
5458 for(var i = 0; i < 42; i++) {
5459 if(i % 7 == 0 && i != 0){
5460 m[m.length] = "</tr><tr>";
5462 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5464 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5465 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5467 var el = document.createElement("div");
5468 el.className = "x-date-picker";
5469 el.innerHTML = m.join("");
5471 container.dom.insertBefore(el, position);
5473 this.el = Roo.get(el);
5474 this.eventEl = Roo.get(el.firstChild);
5476 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5477 handler: this.showPrevMonth,
5479 preventDefault:true,
5483 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5484 handler: this.showNextMonth,
5486 preventDefault:true,
5490 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
5492 this.monthPicker = this.el.down('div.x-date-mp');
5493 this.monthPicker.enableDisplayMode('block');
5495 var kn = new Roo.KeyNav(this.eventEl, {
5496 "left" : function(e){
5498 this.showPrevMonth() :
5499 this.update(this.activeDate.add("d", -1));
5502 "right" : function(e){
5504 this.showNextMonth() :
5505 this.update(this.activeDate.add("d", 1));
5510 this.showNextYear() :
5511 this.update(this.activeDate.add("d", -7));
5514 "down" : function(e){
5516 this.showPrevYear() :
5517 this.update(this.activeDate.add("d", 7));
5520 "pageUp" : function(e){
5521 this.showNextMonth();
5524 "pageDown" : function(e){
5525 this.showPrevMonth();
5528 "enter" : function(e){
5529 e.stopPropagation();
5536 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
5538 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
5540 this.el.unselectable();
5542 this.cells = this.el.select("table.x-date-inner tbody td");
5543 this.textNodes = this.el.query("table.x-date-inner tbody span");
5545 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5547 tooltip: this.monthYearText
5550 this.mbtn.on('click', this.showMonthPicker, this);
5551 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5554 var today = (new Date()).dateFormat(this.format);
5556 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5557 if (this.showClear) {
5558 baseTb.add( new Roo.Toolbar.Fill());
5561 text: String.format(this.todayText, today),
5562 tooltip: String.format(this.todayTip, today),
5563 handler: this.selectToday,
5567 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5570 if (this.showClear) {
5572 baseTb.add( new Roo.Toolbar.Fill());
5575 cls: 'x-btn-icon x-btn-clear',
5576 handler: function() {
5578 this.fireEvent("select", this, '');
5588 this.update(this.value);
5591 createMonthPicker : function(){
5592 if(!this.monthPicker.dom.firstChild){
5593 var buf = ['<table border="0" cellspacing="0">'];
5594 for(var i = 0; i < 6; i++){
5596 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5597 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5599 '<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>' :
5600 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5604 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5606 '</button><button type="button" class="x-date-mp-cancel">',
5608 '</button></td></tr>',
5611 this.monthPicker.update(buf.join(''));
5612 this.monthPicker.on('click', this.onMonthClick, this);
5613 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5615 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5616 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5618 this.mpMonths.each(function(m, a, i){
5621 m.dom.xmonth = 5 + Math.round(i * .5);
5623 m.dom.xmonth = Math.round((i-1) * .5);
5629 showMonthPicker : function(){
5630 this.createMonthPicker();
5631 var size = this.el.getSize();
5632 this.monthPicker.setSize(size);
5633 this.monthPicker.child('table').setSize(size);
5635 this.mpSelMonth = (this.activeDate || this.value).getMonth();
5636 this.updateMPMonth(this.mpSelMonth);
5637 this.mpSelYear = (this.activeDate || this.value).getFullYear();
5638 this.updateMPYear(this.mpSelYear);
5640 this.monthPicker.slideIn('t', {duration:.2});
5643 updateMPYear : function(y){
5645 var ys = this.mpYears.elements;
5646 for(var i = 1; i <= 10; i++){
5647 var td = ys[i-1], y2;
5649 y2 = y + Math.round(i * .5);
5650 td.firstChild.innerHTML = y2;
5653 y2 = y - (5-Math.round(i * .5));
5654 td.firstChild.innerHTML = y2;
5657 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5661 updateMPMonth : function(sm){
5662 this.mpMonths.each(function(m, a, i){
5663 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5667 selectMPMonth: function(m){
5671 onMonthClick : function(e, t){
5673 var el = new Roo.Element(t), pn;
5674 if(el.is('button.x-date-mp-cancel')){
5675 this.hideMonthPicker();
5677 else if(el.is('button.x-date-mp-ok')){
5678 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5679 this.hideMonthPicker();
5681 else if(pn = el.up('td.x-date-mp-month', 2)){
5682 this.mpMonths.removeClass('x-date-mp-sel');
5683 pn.addClass('x-date-mp-sel');
5684 this.mpSelMonth = pn.dom.xmonth;
5686 else if(pn = el.up('td.x-date-mp-year', 2)){
5687 this.mpYears.removeClass('x-date-mp-sel');
5688 pn.addClass('x-date-mp-sel');
5689 this.mpSelYear = pn.dom.xyear;
5691 else if(el.is('a.x-date-mp-prev')){
5692 this.updateMPYear(this.mpyear-10);
5694 else if(el.is('a.x-date-mp-next')){
5695 this.updateMPYear(this.mpyear+10);
5699 onMonthDblClick : function(e, t){
5701 var el = new Roo.Element(t), pn;
5702 if(pn = el.up('td.x-date-mp-month', 2)){
5703 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5704 this.hideMonthPicker();
5706 else if(pn = el.up('td.x-date-mp-year', 2)){
5707 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5708 this.hideMonthPicker();
5712 hideMonthPicker : function(disableAnim){
5713 if(this.monthPicker){
5714 if(disableAnim === true){
5715 this.monthPicker.hide();
5717 this.monthPicker.slideOut('t', {duration:.2});
5723 showPrevMonth : function(e){
5724 this.update(this.activeDate.add("mo", -1));
5728 showNextMonth : function(e){
5729 this.update(this.activeDate.add("mo", 1));
5733 showPrevYear : function(){
5734 this.update(this.activeDate.add("y", -1));
5738 showNextYear : function(){
5739 this.update(this.activeDate.add("y", 1));
5743 handleMouseWheel : function(e){
5744 var delta = e.getWheelDelta();
5746 this.showPrevMonth();
5748 } else if(delta < 0){
5749 this.showNextMonth();
5755 handleDateClick : function(e, t){
5757 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5758 this.setValue(new Date(t.dateValue));
5759 this.fireEvent("select", this, this.value);
5764 selectToday : function(){
5765 this.setValue(new Date().clearTime());
5766 this.fireEvent("select", this, this.value);
5770 update : function(date)
5772 var vd = this.activeDate;
5773 this.activeDate = date;
5775 var t = date.getTime();
5776 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5777 this.cells.removeClass("x-date-selected");
5778 this.cells.each(function(c){
5779 if(c.dom.firstChild.dateValue == t){
5780 c.addClass("x-date-selected");
5781 setTimeout(function(){
5782 try{c.dom.firstChild.focus();}catch(e){}
5791 var days = date.getDaysInMonth();
5792 var firstOfMonth = date.getFirstDateOfMonth();
5793 var startingPos = firstOfMonth.getDay()-this.startDay;
5795 if(startingPos <= this.startDay){
5799 var pm = date.add("mo", -1);
5800 var prevStart = pm.getDaysInMonth()-startingPos;
5802 var cells = this.cells.elements;
5803 var textEls = this.textNodes;
5804 days += startingPos;
5806 // convert everything to numbers so it's fast
5808 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5809 var today = new Date().clearTime().getTime();
5810 var sel = date.clearTime().getTime();
5811 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5812 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5813 var ddMatch = this.disabledDatesRE;
5814 var ddText = this.disabledDatesText;
5815 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5816 var ddaysText = this.disabledDaysText;
5817 var format = this.format;
5819 var setCellClass = function(cal, cell){
5821 var t = d.getTime();
5822 cell.firstChild.dateValue = t;
5824 cell.className += " x-date-today";
5825 cell.title = cal.todayText;
5828 cell.className += " x-date-selected";
5829 setTimeout(function(){
5830 try{cell.firstChild.focus();}catch(e){}
5835 cell.className = " x-date-disabled";
5836 cell.title = cal.minText;
5840 cell.className = " x-date-disabled";
5841 cell.title = cal.maxText;
5845 if(ddays.indexOf(d.getDay()) != -1){
5846 cell.title = ddaysText;
5847 cell.className = " x-date-disabled";
5850 if(ddMatch && format){
5851 var fvalue = d.dateFormat(format);
5852 if(ddMatch.test(fvalue)){
5853 cell.title = ddText.replace("%0", fvalue);
5854 cell.className = " x-date-disabled";
5860 for(; i < startingPos; i++) {
5861 textEls[i].innerHTML = (++prevStart);
5862 d.setDate(d.getDate()+1);
5863 cells[i].className = "x-date-prevday";
5864 setCellClass(this, cells[i]);
5866 for(; i < days; i++){
5867 intDay = i - startingPos + 1;
5868 textEls[i].innerHTML = (intDay);
5869 d.setDate(d.getDate()+1);
5870 cells[i].className = "x-date-active";
5871 setCellClass(this, cells[i]);
5874 for(; i < 42; i++) {
5875 textEls[i].innerHTML = (++extraDays);
5876 d.setDate(d.getDate()+1);
5877 cells[i].className = "x-date-nextday";
5878 setCellClass(this, cells[i]);
5881 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5882 this.fireEvent('monthchange', this, date);
5884 if(!this.internalRender){
5885 var main = this.el.dom.firstChild;
5886 var w = main.offsetWidth;
5887 this.el.setWidth(w + this.el.getBorderWidth("lr"));
5888 Roo.fly(main).setWidth(w);
5889 this.internalRender = true;
5890 // opera does not respect the auto grow header center column
5891 // then, after it gets a width opera refuses to recalculate
5892 // without a second pass
5893 if(Roo.isOpera && !this.secondPass){
5894 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5895 this.secondPass = true;
5896 this.update.defer(10, this, [date]);
5904 * Ext JS Library 1.1.1
5905 * Copyright(c) 2006-2007, Ext JS, LLC.
5907 * Originally Released Under LGPL - original licence link has changed is not relivant.
5910 * <script type="text/javascript">
5913 * @class Roo.TabPanel
5914 * @extends Roo.util.Observable
5915 * A lightweight tab container.
5919 // basic tabs 1, built from existing content
5920 var tabs = new Roo.TabPanel("tabs1");
5921 tabs.addTab("script", "View Script");
5922 tabs.addTab("markup", "View Markup");
5923 tabs.activate("script");
5925 // more advanced tabs, built from javascript
5926 var jtabs = new Roo.TabPanel("jtabs");
5927 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5929 // set up the UpdateManager
5930 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5931 var updater = tab2.getUpdateManager();
5932 updater.setDefaultUrl("ajax1.htm");
5933 tab2.on('activate', updater.refresh, updater, true);
5935 // Use setUrl for Ajax loading
5936 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5937 tab3.setUrl("ajax2.htm", null, true);
5940 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5943 jtabs.activate("jtabs-1");
5946 * Create a new TabPanel.
5947 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5948 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5950 Roo.TabPanel = function(container, config){
5952 * The container element for this TabPanel.
5955 this.el = Roo.get(container, true);
5957 if(typeof config == "boolean"){
5958 this.tabPosition = config ? "bottom" : "top";
5960 Roo.apply(this, config);
5963 if(this.tabPosition == "bottom"){
5964 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5965 this.el.addClass("x-tabs-bottom");
5967 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5968 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5969 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5971 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5973 if(this.tabPosition != "bottom"){
5974 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5977 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5978 this.el.addClass("x-tabs-top");
5982 this.bodyEl.setStyle("position", "relative");
5985 this.activateDelegate = this.activate.createDelegate(this);
5990 * Fires when the active tab changes
5991 * @param {Roo.TabPanel} this
5992 * @param {Roo.TabPanelItem} activePanel The new active tab
5996 * @event beforetabchange
5997 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5998 * @param {Roo.TabPanel} this
5999 * @param {Object} e Set cancel to true on this object to cancel the tab change
6000 * @param {Roo.TabPanelItem} tab The tab being changed to
6002 "beforetabchange" : true
6005 Roo.EventManager.onWindowResize(this.onResize, this);
6006 this.cpad = this.el.getPadding("lr");
6007 this.hiddenCount = 0;
6010 // toolbar on the tabbar support...
6012 var tcfg = this.toolbar;
6013 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
6014 this.toolbar = new Roo.Toolbar(tcfg);
6016 var tbl = tcfg.container.child('table', true);
6017 tbl.setAttribute('width', '100%');
6024 Roo.TabPanel.superclass.constructor.call(this);
6027 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
6029 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
6031 tabPosition : "top",
6033 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
6035 currentTabWidth : 0,
6037 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
6041 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
6045 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
6047 preferredTabWidth : 175,
6049 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
6053 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
6055 monitorResize : true,
6057 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
6062 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6063 * @param {String} id The id of the div to use <b>or create</b>
6064 * @param {String} text The text for the tab
6065 * @param {String} content (optional) Content to put in the TabPanelItem body
6066 * @param {Boolean} closable (optional) True to create a close icon on the tab
6067 * @return {Roo.TabPanelItem} The created TabPanelItem
6069 addTab : function(id, text, content, closable){
6070 var item = new Roo.TabPanelItem(this, id, text, closable);
6071 this.addTabItem(item);
6073 item.setContent(content);
6079 * Returns the {@link Roo.TabPanelItem} with the specified id/index
6080 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6081 * @return {Roo.TabPanelItem}
6083 getTab : function(id){
6084 return this.items[id];
6088 * Hides the {@link Roo.TabPanelItem} with the specified id/index
6089 * @param {String/Number} id The id or index of the TabPanelItem to hide.
6091 hideTab : function(id){
6092 var t = this.items[id];
6096 this.autoSizeTabs();
6101 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6102 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6104 unhideTab : function(id){
6105 var t = this.items[id];
6109 this.autoSizeTabs();
6114 * Adds an existing {@link Roo.TabPanelItem}.
6115 * @param {Roo.TabPanelItem} item The TabPanelItem to add
6117 addTabItem : function(item){
6118 this.items[item.id] = item;
6119 this.items.push(item);
6120 if(this.resizeTabs){
6121 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6122 this.autoSizeTabs();
6129 * Removes a {@link Roo.TabPanelItem}.
6130 * @param {String/Number} id The id or index of the TabPanelItem to remove.
6132 removeTab : function(id){
6133 var items = this.items;
6134 var tab = items[id];
6135 if(!tab) { return; }
6136 var index = items.indexOf(tab);
6137 if(this.active == tab && items.length > 1){
6138 var newTab = this.getNextAvailable(index);
6143 this.stripEl.dom.removeChild(tab.pnode.dom);
6144 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6145 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6147 items.splice(index, 1);
6148 delete this.items[tab.id];
6149 tab.fireEvent("close", tab);
6150 tab.purgeListeners();
6151 this.autoSizeTabs();
6154 getNextAvailable : function(start){
6155 var items = this.items;
6157 // look for a next tab that will slide over to
6158 // replace the one being removed
6159 while(index < items.length){
6160 var item = items[++index];
6161 if(item && !item.isHidden()){
6165 // if one isn't found select the previous tab (on the left)
6168 var item = items[--index];
6169 if(item && !item.isHidden()){
6177 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6178 * @param {String/Number} id The id or index of the TabPanelItem to disable.
6180 disableTab : function(id){
6181 var tab = this.items[id];
6182 if(tab && this.active != tab){
6188 * Enables a {@link Roo.TabPanelItem} that is disabled.
6189 * @param {String/Number} id The id or index of the TabPanelItem to enable.
6191 enableTab : function(id){
6192 var tab = this.items[id];
6197 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6198 * @param {String/Number} id The id or index of the TabPanelItem to activate.
6199 * @return {Roo.TabPanelItem} The TabPanelItem.
6201 activate : function(id){
6202 var tab = this.items[id];
6206 if(tab == this.active || tab.disabled){
6210 this.fireEvent("beforetabchange", this, e, tab);
6211 if(e.cancel !== true && !tab.disabled){
6215 this.active = this.items[id];
6217 this.fireEvent("tabchange", this, this.active);
6223 * Gets the active {@link Roo.TabPanelItem}.
6224 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6226 getActiveTab : function(){
6231 * Updates the tab body element to fit the height of the container element
6232 * for overflow scrolling
6233 * @param {Number} targetHeight (optional) Override the starting height from the elements height
6235 syncHeight : function(targetHeight){
6236 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6237 var bm = this.bodyEl.getMargins();
6238 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6239 this.bodyEl.setHeight(newHeight);
6243 onResize : function(){
6244 if(this.monitorResize){
6245 this.autoSizeTabs();
6250 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6252 beginUpdate : function(){
6253 this.updating = true;
6257 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6259 endUpdate : function(){
6260 this.updating = false;
6261 this.autoSizeTabs();
6265 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6267 autoSizeTabs : function(){
6268 var count = this.items.length;
6269 var vcount = count - this.hiddenCount;
6270 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6273 var w = Math.max(this.el.getWidth() - this.cpad, 10);
6274 var availWidth = Math.floor(w / vcount);
6275 var b = this.stripBody;
6276 if(b.getWidth() > w){
6277 var tabs = this.items;
6278 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6279 if(availWidth < this.minTabWidth){
6280 /*if(!this.sleft){ // incomplete scrolling code
6281 this.createScrollButtons();
6284 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6287 if(this.currentTabWidth < this.preferredTabWidth){
6288 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6294 * Returns the number of tabs in this TabPanel.
6297 getCount : function(){
6298 return this.items.length;
6302 * Resizes all the tabs to the passed width
6303 * @param {Number} The new width
6305 setTabWidth : function(width){
6306 this.currentTabWidth = width;
6307 for(var i = 0, len = this.items.length; i < len; i++) {
6308 if(!this.items[i].isHidden()) {
6309 this.items[i].setWidth(width);
6315 * Destroys this TabPanel
6316 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6318 destroy : function(removeEl){
6319 Roo.EventManager.removeResizeListener(this.onResize, this);
6320 for(var i = 0, len = this.items.length; i < len; i++){
6321 this.items[i].purgeListeners();
6323 if(removeEl === true){
6331 * @class Roo.TabPanelItem
6332 * @extends Roo.util.Observable
6333 * Represents an individual item (tab plus body) in a TabPanel.
6334 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6335 * @param {String} id The id of this TabPanelItem
6336 * @param {String} text The text for the tab of this TabPanelItem
6337 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6339 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6341 * The {@link Roo.TabPanel} this TabPanelItem belongs to
6342 * @type Roo.TabPanel
6344 this.tabPanel = tabPanel;
6346 * The id for this TabPanelItem
6351 this.disabled = false;
6355 this.loaded = false;
6356 this.closable = closable;
6359 * The body element for this TabPanelItem.
6362 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6363 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6364 this.bodyEl.setStyle("display", "block");
6365 this.bodyEl.setStyle("zoom", "1");
6368 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6370 this.el = Roo.get(els.el, true);
6371 this.inner = Roo.get(els.inner, true);
6372 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6373 this.pnode = Roo.get(els.el.parentNode, true);
6374 this.el.on("mousedown", this.onTabMouseDown, this);
6375 this.el.on("click", this.onTabClick, this);
6378 var c = Roo.get(els.close, true);
6379 c.dom.title = this.closeText;
6380 c.addClassOnOver("close-over");
6381 c.on("click", this.closeClick, this);
6387 * Fires when this tab becomes the active tab.
6388 * @param {Roo.TabPanel} tabPanel The parent TabPanel
6389 * @param {Roo.TabPanelItem} this
6393 * @event beforeclose
6394 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6395 * @param {Roo.TabPanelItem} this
6396 * @param {Object} e Set cancel to true on this object to cancel the close.
6398 "beforeclose": true,
6401 * Fires when this tab is closed.
6402 * @param {Roo.TabPanelItem} this
6407 * Fires when this tab is no longer the active tab.
6408 * @param {Roo.TabPanel} tabPanel The parent TabPanel
6409 * @param {Roo.TabPanelItem} this
6413 this.hidden = false;
6415 Roo.TabPanelItem.superclass.constructor.call(this);
6418 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6419 purgeListeners : function(){
6420 Roo.util.Observable.prototype.purgeListeners.call(this);
6421 this.el.removeAllListeners();
6424 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6427 this.pnode.addClass("on");
6430 this.tabPanel.stripWrap.repaint();
6432 this.fireEvent("activate", this.tabPanel, this);
6436 * Returns true if this tab is the active tab.
6439 isActive : function(){
6440 return this.tabPanel.getActiveTab() == this;
6444 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6447 this.pnode.removeClass("on");
6449 this.fireEvent("deactivate", this.tabPanel, this);
6452 hideAction : function(){
6454 this.bodyEl.setStyle("position", "absolute");
6455 this.bodyEl.setLeft("-20000px");
6456 this.bodyEl.setTop("-20000px");
6459 showAction : function(){
6460 this.bodyEl.setStyle("position", "relative");
6461 this.bodyEl.setTop("");
6462 this.bodyEl.setLeft("");
6467 * Set the tooltip for the tab.
6468 * @param {String} tooltip The tab's tooltip
6470 setTooltip : function(text){
6471 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6472 this.textEl.dom.qtip = text;
6473 this.textEl.dom.removeAttribute('title');
6475 this.textEl.dom.title = text;
6479 onTabClick : function(e){
6481 this.tabPanel.activate(this.id);
6484 onTabMouseDown : function(e){
6486 this.tabPanel.activate(this.id);
6489 getWidth : function(){
6490 return this.inner.getWidth();
6493 setWidth : function(width){
6494 var iwidth = width - this.pnode.getPadding("lr");
6495 this.inner.setWidth(iwidth);
6496 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6497 this.pnode.setWidth(width);
6501 * Show or hide the tab
6502 * @param {Boolean} hidden True to hide or false to show.
6504 setHidden : function(hidden){
6505 this.hidden = hidden;
6506 this.pnode.setStyle("display", hidden ? "none" : "");
6510 * Returns true if this tab is "hidden"
6513 isHidden : function(){
6518 * Returns the text for this tab
6521 getText : function(){
6525 autoSize : function(){
6526 //this.el.beginMeasure();
6527 this.textEl.setWidth(1);
6529 * #2804 [new] Tabs in Roojs
6530 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6532 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6533 //this.el.endMeasure();
6537 * Sets the text for the tab (Note: this also sets the tooltip text)
6538 * @param {String} text The tab's text and tooltip
6540 setText : function(text){
6542 this.textEl.update(text);
6543 this.setTooltip(text);
6544 if(!this.tabPanel.resizeTabs){
6549 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6551 activate : function(){
6552 this.tabPanel.activate(this.id);
6556 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6558 disable : function(){
6559 if(this.tabPanel.active != this){
6560 this.disabled = true;
6561 this.pnode.addClass("disabled");
6566 * Enables this TabPanelItem if it was previously disabled.
6568 enable : function(){
6569 this.disabled = false;
6570 this.pnode.removeClass("disabled");
6574 * Sets the content for this TabPanelItem.
6575 * @param {String} content The content
6576 * @param {Boolean} loadScripts true to look for and load scripts
6578 setContent : function(content, loadScripts){
6579 this.bodyEl.update(content, loadScripts);
6583 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6584 * @return {Roo.UpdateManager} The UpdateManager
6586 getUpdateManager : function(){
6587 return this.bodyEl.getUpdateManager();
6591 * Set a URL to be used to load the content for this TabPanelItem.
6592 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6593 * @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)
6594 * @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)
6595 * @return {Roo.UpdateManager} The UpdateManager
6597 setUrl : function(url, params, loadOnce){
6598 if(this.refreshDelegate){
6599 this.un('activate', this.refreshDelegate);
6601 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6602 this.on("activate", this.refreshDelegate);
6603 return this.bodyEl.getUpdateManager();
6607 _handleRefresh : function(url, params, loadOnce){
6608 if(!loadOnce || !this.loaded){
6609 var updater = this.bodyEl.getUpdateManager();
6610 updater.update(url, params, this._setLoaded.createDelegate(this));
6615 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
6616 * Will fail silently if the setUrl method has not been called.
6617 * This does not activate the panel, just updates its content.
6619 refresh : function(){
6620 if(this.refreshDelegate){
6621 this.loaded = false;
6622 this.refreshDelegate();
6627 _setLoaded : function(){
6632 closeClick : function(e){
6635 this.fireEvent("beforeclose", this, o);
6636 if(o.cancel !== true){
6637 this.tabPanel.removeTab(this.id);
6641 * The text displayed in the tooltip for the close icon.
6644 closeText : "Close this tab"
6648 Roo.TabPanel.prototype.createStrip = function(container){
6649 var strip = document.createElement("div");
6650 strip.className = "x-tabs-wrap";
6651 container.appendChild(strip);
6655 Roo.TabPanel.prototype.createStripList = function(strip){
6656 // div wrapper for retard IE
6657 // returns the "tr" element.
6658 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6659 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6660 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6661 return strip.firstChild.firstChild.firstChild.firstChild;
6664 Roo.TabPanel.prototype.createBody = function(container){
6665 var body = document.createElement("div");
6666 Roo.id(body, "tab-body");
6667 Roo.fly(body).addClass("x-tabs-body");
6668 container.appendChild(body);
6672 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6673 var body = Roo.getDom(id);
6675 body = document.createElement("div");
6678 Roo.fly(body).addClass("x-tabs-item-body");
6679 bodyEl.insertBefore(body, bodyEl.firstChild);
6683 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6684 var td = document.createElement("td");
6685 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6686 //stripEl.appendChild(td);
6688 td.className = "x-tabs-closable";
6690 this.closeTpl = new Roo.Template(
6691 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6692 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6693 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
6696 var el = this.closeTpl.overwrite(td, {"text": text});
6697 var close = el.getElementsByTagName("div")[0];
6698 var inner = el.getElementsByTagName("em")[0];
6699 return {"el": el, "close": close, "inner": inner};
6702 this.tabTpl = new Roo.Template(
6703 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6704 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6707 var el = this.tabTpl.overwrite(td, {"text": text});
6708 var inner = el.getElementsByTagName("em")[0];
6709 return {"el": el, "inner": inner};
6713 * Ext JS Library 1.1.1
6714 * Copyright(c) 2006-2007, Ext JS, LLC.
6716 * Originally Released Under LGPL - original licence link has changed is not relivant.
6719 * <script type="text/javascript">
6724 * @extends Roo.util.Observable
6725 * Simple Button class
6726 * @cfg {String} text The button text
6727 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6728 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6729 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6730 * @cfg {Object} scope The scope of the handler
6731 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6732 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6733 * @cfg {Boolean} hidden True to start hidden (defaults to false)
6734 * @cfg {Boolean} disabled True to start disabled (defaults to false)
6735 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6736 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6737 applies if enableToggle = true)
6738 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6739 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6740 an {@link Roo.util.ClickRepeater} config object (defaults to false).
6742 * Create a new button
6743 * @param {Object} config The config object
6745 Roo.Button = function(renderTo, config)
6749 renderTo = config.renderTo || false;
6752 Roo.apply(this, config);
6756 * Fires when this button is clicked
6757 * @param {Button} this
6758 * @param {EventObject} e The click event
6763 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6764 * @param {Button} this
6765 * @param {Boolean} pressed
6770 * Fires when the mouse hovers over the button
6771 * @param {Button} this
6772 * @param {Event} e The event object
6777 * Fires when the mouse exits the button
6778 * @param {Button} this
6779 * @param {Event} e The event object
6784 * Fires when the button is rendered
6785 * @param {Button} this
6790 this.menu = Roo.menu.MenuMgr.get(this.menu);
6792 // register listeners first!! - so render can be captured..
6793 Roo.util.Observable.call(this);
6795 this.render(renderTo);
6801 Roo.extend(Roo.Button, Roo.util.Observable, {
6807 * Read-only. True if this button is hidden
6812 * Read-only. True if this button is disabled
6817 * Read-only. True if this button is pressed (only if enableToggle = true)
6823 * @cfg {Number} tabIndex
6824 * The DOM tabIndex for this button (defaults to undefined)
6826 tabIndex : undefined,
6829 * @cfg {Boolean} enableToggle
6830 * True to enable pressed/not pressed toggling (defaults to false)
6832 enableToggle: false,
6835 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6839 * @cfg {String} menuAlign
6840 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6842 menuAlign : "tl-bl?",
6845 * @cfg {String} iconCls
6846 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6848 iconCls : undefined,
6850 * @cfg {String} type
6851 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
6856 menuClassTarget: 'tr',
6859 * @cfg {String} clickEvent
6860 * The type of event to map to the button's event handler (defaults to 'click')
6862 clickEvent : 'click',
6865 * @cfg {Boolean} handleMouseEvents
6866 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6868 handleMouseEvents : true,
6871 * @cfg {String} tooltipType
6872 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6874 tooltipType : 'qtip',
6878 * A CSS class to apply to the button's main element.
6882 * @cfg {Roo.Template} template (Optional)
6883 * An {@link Roo.Template} with which to create the Button's main element. This Template must
6884 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6885 * require code modifications if required elements (e.g. a button) aren't present.
6889 render : function(renderTo){
6891 if(this.hideParent){
6892 this.parentEl = Roo.get(renderTo);
6896 if(!Roo.Button.buttonTemplate){
6897 // hideous table template
6898 Roo.Button.buttonTemplate = new Roo.Template(
6899 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6900 '<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>',
6901 "</tr></tbody></table>");
6903 this.template = Roo.Button.buttonTemplate;
6905 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
6906 var btnEl = btn.child("button:first");
6907 btnEl.on('focus', this.onFocus, this);
6908 btnEl.on('blur', this.onBlur, this);
6910 btn.addClass(this.cls);
6913 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6916 btnEl.addClass(this.iconCls);
6918 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6921 if(this.tabIndex !== undefined){
6922 btnEl.dom.tabIndex = this.tabIndex;
6925 if(typeof this.tooltip == 'object'){
6926 Roo.QuickTips.tips(Roo.apply({
6930 btnEl.dom[this.tooltipType] = this.tooltip;
6934 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6938 this.el.dom.id = this.el.id = this.id;
6941 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6942 this.menu.on("show", this.onMenuShow, this);
6943 this.menu.on("hide", this.onMenuHide, this);
6945 btn.addClass("x-btn");
6946 if(Roo.isIE && !Roo.isIE7){
6947 this.autoWidth.defer(1, this);
6951 if(this.handleMouseEvents){
6952 btn.on("mouseover", this.onMouseOver, this);
6953 btn.on("mouseout", this.onMouseOut, this);
6954 btn.on("mousedown", this.onMouseDown, this);
6956 btn.on(this.clickEvent, this.onClick, this);
6957 //btn.on("mouseup", this.onMouseUp, this);
6964 Roo.ButtonToggleMgr.register(this);
6966 this.el.addClass("x-btn-pressed");
6969 var repeater = new Roo.util.ClickRepeater(btn,
6970 typeof this.repeat == "object" ? this.repeat : {}
6972 repeater.on("click", this.onClick, this);
6975 this.fireEvent('render', this);
6979 * Returns the button's underlying element
6980 * @return {Roo.Element} The element
6987 * Destroys this Button and removes any listeners.
6989 destroy : function(){
6990 Roo.ButtonToggleMgr.unregister(this);
6991 this.el.removeAllListeners();
6992 this.purgeListeners();
6997 autoWidth : function(){
6999 this.el.setWidth("auto");
7000 if(Roo.isIE7 && Roo.isStrict){
7001 var ib = this.el.child('button');
7002 if(ib && ib.getWidth() > 20){
7004 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7009 this.el.beginMeasure();
7011 if(this.el.getWidth() < this.minWidth){
7012 this.el.setWidth(this.minWidth);
7015 this.el.endMeasure();
7022 * Assigns this button's click handler
7023 * @param {Function} handler The function to call when the button is clicked
7024 * @param {Object} scope (optional) Scope for the function passed in
7026 setHandler : function(handler, scope){
7027 this.handler = handler;
7032 * Sets this button's text
7033 * @param {String} text The button text
7035 setText : function(text){
7038 this.el.child("td.x-btn-center button.x-btn-text").update(text);
7044 * Gets the text for this button
7045 * @return {String} The button text
7047 getText : function(){
7055 this.hidden = false;
7057 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7067 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7072 * Convenience function for boolean show/hide
7073 * @param {Boolean} visible True to show, false to hide
7075 setVisible: function(visible){
7084 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7085 * @param {Boolean} state (optional) Force a particular state
7087 toggle : function(state){
7088 state = state === undefined ? !this.pressed : state;
7089 if(state != this.pressed){
7091 this.el.addClass("x-btn-pressed");
7092 this.pressed = true;
7093 this.fireEvent("toggle", this, true);
7095 this.el.removeClass("x-btn-pressed");
7096 this.pressed = false;
7097 this.fireEvent("toggle", this, false);
7099 if(this.toggleHandler){
7100 this.toggleHandler.call(this.scope || this, this, state);
7109 this.el.child('button:first').focus();
7113 * Disable this button
7115 disable : function(){
7117 this.el.addClass("x-btn-disabled");
7119 this.disabled = true;
7123 * Enable this button
7125 enable : function(){
7127 this.el.removeClass("x-btn-disabled");
7129 this.disabled = false;
7133 * Convenience function for boolean enable/disable
7134 * @param {Boolean} enabled True to enable, false to disable
7136 setDisabled : function(v){
7137 this[v !== true ? "enable" : "disable"]();
7141 onClick : function(e)
7150 if(this.enableToggle){
7153 if(this.menu && !this.menu.isVisible()){
7154 this.menu.show(this.el, this.menuAlign);
7156 this.fireEvent("click", this, e);
7158 this.el.removeClass("x-btn-over");
7159 this.handler.call(this.scope || this, this, e);
7164 onMouseOver : function(e){
7166 this.el.addClass("x-btn-over");
7167 this.fireEvent('mouseover', this, e);
7171 onMouseOut : function(e){
7172 if(!e.within(this.el, true)){
7173 this.el.removeClass("x-btn-over");
7174 this.fireEvent('mouseout', this, e);
7178 onFocus : function(e){
7180 this.el.addClass("x-btn-focus");
7184 onBlur : function(e){
7185 this.el.removeClass("x-btn-focus");
7188 onMouseDown : function(e){
7189 if(!this.disabled && e.button == 0){
7190 this.el.addClass("x-btn-click");
7191 Roo.get(document).on('mouseup', this.onMouseUp, this);
7195 onMouseUp : function(e){
7197 this.el.removeClass("x-btn-click");
7198 Roo.get(document).un('mouseup', this.onMouseUp, this);
7202 onMenuShow : function(e){
7203 this.el.addClass("x-btn-menu-active");
7206 onMenuHide : function(e){
7207 this.el.removeClass("x-btn-menu-active");
7211 // Private utility class used by Button
7212 Roo.ButtonToggleMgr = function(){
7215 function toggleGroup(btn, state){
7217 var g = groups[btn.toggleGroup];
7218 for(var i = 0, l = g.length; i < l; i++){
7227 register : function(btn){
7228 if(!btn.toggleGroup){
7231 var g = groups[btn.toggleGroup];
7233 g = groups[btn.toggleGroup] = [];
7236 btn.on("toggle", toggleGroup);
7239 unregister : function(btn){
7240 if(!btn.toggleGroup){
7243 var g = groups[btn.toggleGroup];
7246 btn.un("toggle", toggleGroup);
7252 * Ext JS Library 1.1.1
7253 * Copyright(c) 2006-2007, Ext JS, LLC.
7255 * Originally Released Under LGPL - original licence link has changed is not relivant.
7258 * <script type="text/javascript">
7262 * @class Roo.SplitButton
7263 * @extends Roo.Button
7264 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7265 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
7266 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7267 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7268 * @cfg {String} arrowTooltip The title attribute of the arrow
7270 * Create a new menu button
7271 * @param {String/HTMLElement/Element} renderTo The element to append the button to
7272 * @param {Object} config The config object
7274 Roo.SplitButton = function(renderTo, config){
7275 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7278 * Fires when this button's arrow is clicked
7279 * @param {SplitButton} this
7280 * @param {EventObject} e The click event
7282 this.addEvents({"arrowclick":true});
7285 Roo.extend(Roo.SplitButton, Roo.Button, {
7286 render : function(renderTo){
7287 // this is one sweet looking template!
7288 var tpl = new Roo.Template(
7289 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7290 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7291 '<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>',
7292 "</tbody></table></td><td>",
7293 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7294 '<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>',
7295 "</tbody></table></td></tr></table>"
7297 var btn = tpl.append(renderTo, [this.text, this.type], true);
7298 var btnEl = btn.child("button");
7300 btn.addClass(this.cls);
7303 btnEl.setStyle('background-image', 'url(' +this.icon +')');
7306 btnEl.addClass(this.iconCls);
7308 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7312 if(this.handleMouseEvents){
7313 btn.on("mouseover", this.onMouseOver, this);
7314 btn.on("mouseout", this.onMouseOut, this);
7315 btn.on("mousedown", this.onMouseDown, this);
7316 btn.on("mouseup", this.onMouseUp, this);
7318 btn.on(this.clickEvent, this.onClick, this);
7320 if(typeof this.tooltip == 'object'){
7321 Roo.QuickTips.tips(Roo.apply({
7325 btnEl.dom[this.tooltipType] = this.tooltip;
7328 if(this.arrowTooltip){
7329 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7338 this.el.addClass("x-btn-pressed");
7340 if(Roo.isIE && !Roo.isIE7){
7341 this.autoWidth.defer(1, this);
7346 this.menu.on("show", this.onMenuShow, this);
7347 this.menu.on("hide", this.onMenuHide, this);
7349 this.fireEvent('render', this);
7353 autoWidth : function(){
7355 var tbl = this.el.child("table:first");
7356 var tbl2 = this.el.child("table:last");
7357 this.el.setWidth("auto");
7358 tbl.setWidth("auto");
7359 if(Roo.isIE7 && Roo.isStrict){
7360 var ib = this.el.child('button:first');
7361 if(ib && ib.getWidth() > 20){
7363 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7368 this.el.beginMeasure();
7370 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7371 tbl.setWidth(this.minWidth-tbl2.getWidth());
7374 this.el.endMeasure();
7377 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7381 * Sets this button's click handler
7382 * @param {Function} handler The function to call when the button is clicked
7383 * @param {Object} scope (optional) Scope for the function passed above
7385 setHandler : function(handler, scope){
7386 this.handler = handler;
7391 * Sets this button's arrow click handler
7392 * @param {Function} handler The function to call when the arrow is clicked
7393 * @param {Object} scope (optional) Scope for the function passed above
7395 setArrowHandler : function(handler, scope){
7396 this.arrowHandler = handler;
7405 this.el.child("button:first").focus();
7410 onClick : function(e){
7413 if(e.getTarget(".x-btn-menu-arrow-wrap")){
7414 if(this.menu && !this.menu.isVisible()){
7415 this.menu.show(this.el, this.menuAlign);
7417 this.fireEvent("arrowclick", this, e);
7418 if(this.arrowHandler){
7419 this.arrowHandler.call(this.scope || this, this, e);
7422 this.fireEvent("click", this, e);
7424 this.handler.call(this.scope || this, this, e);
7430 onMouseDown : function(e){
7432 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7436 onMouseUp : function(e){
7437 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7443 Roo.MenuButton = Roo.SplitButton;/*
7445 * Ext JS Library 1.1.1
7446 * Copyright(c) 2006-2007, Ext JS, LLC.
7448 * Originally Released Under LGPL - original licence link has changed is not relivant.
7451 * <script type="text/javascript">
7455 * @class Roo.Toolbar
7456 * Basic Toolbar class.
7458 * Creates a new Toolbar
7459 * @param {Object} container The config object
7461 Roo.Toolbar = function(container, buttons, config)
7463 /// old consturctor format still supported..
7464 if(container instanceof Array){ // omit the container for later rendering
7465 buttons = container;
7469 if (typeof(container) == 'object' && container.xtype) {
7471 container = config.container;
7472 buttons = config.buttons || []; // not really - use items!!
7475 if (config && config.items) {
7476 xitems = config.items;
7477 delete config.items;
7479 Roo.apply(this, config);
7480 this.buttons = buttons;
7483 this.render(container);
7485 this.xitems = xitems;
7486 Roo.each(xitems, function(b) {
7492 Roo.Toolbar.prototype = {
7494 * @cfg {Array} items
7495 * array of button configs or elements to add (will be converted to a MixedCollection)
7499 * @cfg {String/HTMLElement/Element} container
7500 * The id or element that will contain the toolbar
7503 render : function(ct){
7504 this.el = Roo.get(ct);
7506 this.el.addClass(this.cls);
7508 // using a table allows for vertical alignment
7509 // 100% width is needed by Safari...
7510 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7511 this.tr = this.el.child("tr", true);
7513 this.items = new Roo.util.MixedCollection(false, function(o){
7514 return o.id || ("item" + (++autoId));
7517 this.add.apply(this, this.buttons);
7518 delete this.buttons;
7523 * Adds element(s) to the toolbar -- this function takes a variable number of
7524 * arguments of mixed type and adds them to the toolbar.
7525 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7527 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7528 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7529 * <li>Field: Any form field (equivalent to {@link #addField})</li>
7530 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7531 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7532 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7533 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7534 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7535 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7537 * @param {Mixed} arg2
7538 * @param {Mixed} etc.
7541 var a = arguments, l = a.length;
7542 for(var i = 0; i < l; i++){
7547 _add : function(el) {
7550 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7553 if (el.applyTo){ // some kind of form field
7554 return this.addField(el);
7556 if (el.render){ // some kind of Toolbar.Item
7557 return this.addItem(el);
7559 if (typeof el == "string"){ // string
7560 if(el == "separator" || el == "-"){
7561 return this.addSeparator();
7564 return this.addSpacer();
7567 return this.addFill();
7569 return this.addText(el);
7572 if(el.tagName){ // element
7573 return this.addElement(el);
7575 if(typeof el == "object"){ // must be button config?
7576 return this.addButton(el);
7584 * Add an Xtype element
7585 * @param {Object} xtype Xtype Object
7586 * @return {Object} created Object
7588 addxtype : function(e){
7593 * Returns the Element for this toolbar.
7594 * @return {Roo.Element}
7602 * @return {Roo.Toolbar.Item} The separator item
7604 addSeparator : function(){
7605 return this.addItem(new Roo.Toolbar.Separator());
7609 * Adds a spacer element
7610 * @return {Roo.Toolbar.Spacer} The spacer item
7612 addSpacer : function(){
7613 return this.addItem(new Roo.Toolbar.Spacer());
7617 * Adds a fill element that forces subsequent additions to the right side of the toolbar
7618 * @return {Roo.Toolbar.Fill} The fill item
7620 addFill : function(){
7621 return this.addItem(new Roo.Toolbar.Fill());
7625 * Adds any standard HTML element to the toolbar
7626 * @param {String/HTMLElement/Element} el The element or id of the element to add
7627 * @return {Roo.Toolbar.Item} The element's item
7629 addElement : function(el){
7630 return this.addItem(new Roo.Toolbar.Item(el));
7633 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7634 * @type Roo.util.MixedCollection
7639 * Adds any Toolbar.Item or subclass
7640 * @param {Roo.Toolbar.Item} item
7641 * @return {Roo.Toolbar.Item} The item
7643 addItem : function(item){
7644 var td = this.nextBlock();
7646 this.items.add(item);
7651 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7652 * @param {Object/Array} config A button config or array of configs
7653 * @return {Roo.Toolbar.Button/Array}
7655 addButton : function(config){
7656 if(config instanceof Array){
7658 for(var i = 0, len = config.length; i < len; i++) {
7659 buttons.push(this.addButton(config[i]));
7664 if(!(config instanceof Roo.Toolbar.Button)){
7666 new Roo.Toolbar.SplitButton(config) :
7667 new Roo.Toolbar.Button(config);
7669 var td = this.nextBlock();
7676 * Adds text to the toolbar
7677 * @param {String} text The text to add
7678 * @return {Roo.Toolbar.Item} The element's item
7680 addText : function(text){
7681 return this.addItem(new Roo.Toolbar.TextItem(text));
7685 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7686 * @param {Number} index The index where the item is to be inserted
7687 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7688 * @return {Roo.Toolbar.Button/Item}
7690 insertButton : function(index, item){
7691 if(item instanceof Array){
7693 for(var i = 0, len = item.length; i < len; i++) {
7694 buttons.push(this.insertButton(index + i, item[i]));
7698 if (!(item instanceof Roo.Toolbar.Button)){
7699 item = new Roo.Toolbar.Button(item);
7701 var td = document.createElement("td");
7702 this.tr.insertBefore(td, this.tr.childNodes[index]);
7704 this.items.insert(index, item);
7709 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7710 * @param {Object} config
7711 * @return {Roo.Toolbar.Item} The element's item
7713 addDom : function(config, returnEl){
7714 var td = this.nextBlock();
7715 Roo.DomHelper.overwrite(td, config);
7716 var ti = new Roo.Toolbar.Item(td.firstChild);
7723 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7724 * @type Roo.util.MixedCollection
7729 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7730 * Note: the field should not have been rendered yet. For a field that has already been
7731 * rendered, use {@link #addElement}.
7732 * @param {Roo.form.Field} field
7733 * @return {Roo.ToolbarItem}
7737 addField : function(field) {
7740 this.fields = new Roo.util.MixedCollection(false, function(o){
7741 return o.id || ("item" + (++autoId));
7746 var td = this.nextBlock();
7748 var ti = new Roo.Toolbar.Item(td.firstChild);
7751 this.fields.add(field);
7762 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7763 this.el.child('div').hide();
7771 this.el.child('div').show();
7775 nextBlock : function(){
7776 var td = document.createElement("td");
7777 this.tr.appendChild(td);
7782 destroy : function(){
7783 if(this.items){ // rendered?
7784 Roo.destroy.apply(Roo, this.items.items);
7786 if(this.fields){ // rendered?
7787 Roo.destroy.apply(Roo, this.fields.items);
7789 Roo.Element.uncache(this.el, this.tr);
7794 * @class Roo.Toolbar.Item
7795 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7797 * Creates a new Item
7798 * @param {HTMLElement} el
7800 Roo.Toolbar.Item = function(el){
7802 if (typeof (el.xtype) != 'undefined') {
7807 this.el = Roo.getDom(el);
7808 this.id = Roo.id(this.el);
7809 this.hidden = false;
7814 * Fires when the button is rendered
7815 * @param {Button} this
7819 Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7821 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7822 //Roo.Toolbar.Item.prototype = {
7825 * Get this item's HTML Element
7826 * @return {HTMLElement}
7833 render : function(td){
7836 td.appendChild(this.el);
7838 this.fireEvent('render', this);
7842 * Removes and destroys this item.
7844 destroy : function(){
7845 this.td.parentNode.removeChild(this.td);
7852 this.hidden = false;
7853 this.td.style.display = "";
7861 this.td.style.display = "none";
7865 * Convenience function for boolean show/hide.
7866 * @param {Boolean} visible true to show/false to hide
7868 setVisible: function(visible){
7877 * Try to focus this item.
7880 Roo.fly(this.el).focus();
7884 * Disables this item.
7886 disable : function(){
7887 Roo.fly(this.td).addClass("x-item-disabled");
7888 this.disabled = true;
7889 this.el.disabled = true;
7893 * Enables this item.
7895 enable : function(){
7896 Roo.fly(this.td).removeClass("x-item-disabled");
7897 this.disabled = false;
7898 this.el.disabled = false;
7904 * @class Roo.Toolbar.Separator
7905 * @extends Roo.Toolbar.Item
7906 * A simple toolbar separator class
7908 * Creates a new Separator
7910 Roo.Toolbar.Separator = function(cfg){
7912 var s = document.createElement("span");
7913 s.className = "ytb-sep";
7918 Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7920 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7922 disable:Roo.emptyFn,
7927 * @class Roo.Toolbar.Spacer
7928 * @extends Roo.Toolbar.Item
7929 * A simple element that adds extra horizontal space to a toolbar.
7931 * Creates a new Spacer
7933 Roo.Toolbar.Spacer = function(cfg){
7934 var s = document.createElement("div");
7935 s.className = "ytb-spacer";
7939 Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7941 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7943 disable:Roo.emptyFn,
7948 * @class Roo.Toolbar.Fill
7949 * @extends Roo.Toolbar.Spacer
7950 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7952 * Creates a new Spacer
7954 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7956 render : function(td){
7957 td.style.width = '100%';
7958 Roo.Toolbar.Fill.superclass.render.call(this, td);
7963 * @class Roo.Toolbar.TextItem
7964 * @extends Roo.Toolbar.Item
7965 * A simple class that renders text directly into a toolbar.
7967 * Creates a new TextItem
7968 * @param {String} text
7970 Roo.Toolbar.TextItem = function(cfg){
7971 var text = cfg || "";
7972 if (typeof(cfg) == 'object') {
7973 text = cfg.text || "";
7977 var s = document.createElement("span");
7978 s.className = "ytb-text";
7984 Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg || s);
7986 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7990 disable:Roo.emptyFn,
7995 * @class Roo.Toolbar.Button
7996 * @extends Roo.Button
7997 * A button that renders into a toolbar.
7999 * Creates a new Button
8000 * @param {Object} config A standard {@link Roo.Button} config object
8002 Roo.Toolbar.Button = function(config){
8003 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
8005 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
8006 render : function(td){
8008 Roo.Toolbar.Button.superclass.render.call(this, td);
8012 * Removes and destroys this button
8014 destroy : function(){
8015 Roo.Toolbar.Button.superclass.destroy.call(this);
8016 this.td.parentNode.removeChild(this.td);
8023 this.hidden = false;
8024 this.td.style.display = "";
8032 this.td.style.display = "none";
8036 * Disables this item
8038 disable : function(){
8039 Roo.fly(this.td).addClass("x-item-disabled");
8040 this.disabled = true;
8046 enable : function(){
8047 Roo.fly(this.td).removeClass("x-item-disabled");
8048 this.disabled = false;
8052 Roo.ToolbarButton = Roo.Toolbar.Button;
8055 * @class Roo.Toolbar.SplitButton
8056 * @extends Roo.SplitButton
8057 * A menu button that renders into a toolbar.
8059 * Creates a new SplitButton
8060 * @param {Object} config A standard {@link Roo.SplitButton} config object
8062 Roo.Toolbar.SplitButton = function(config){
8063 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8065 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8066 render : function(td){
8068 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8072 * Removes and destroys this button
8074 destroy : function(){
8075 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8076 this.td.parentNode.removeChild(this.td);
8083 this.hidden = false;
8084 this.td.style.display = "";
8092 this.td.style.display = "none";
8097 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8099 * Ext JS Library 1.1.1
8100 * Copyright(c) 2006-2007, Ext JS, LLC.
8102 * Originally Released Under LGPL - original licence link has changed is not relivant.
8105 * <script type="text/javascript">
8109 * @class Roo.PagingToolbar
8110 * @extends Roo.Toolbar
8111 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8113 * Create a new PagingToolbar
8114 * @param {Object} config The config object
8116 Roo.PagingToolbar = function(el, ds, config)
8118 // old args format still supported... - xtype is prefered..
8119 if (typeof(el) == 'object' && el.xtype) {
8120 // created from xtype...
8123 el = config.container;
8127 items = config.items;
8131 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8134 this.renderButtons(this.el);
8137 // supprot items array.
8139 Roo.each(items, function(e) {
8140 this.add(Roo.factory(e));
8145 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8147 * @cfg {Roo.data.Store} dataSource
8148 * The underlying data store providing the paged data
8151 * @cfg {String/HTMLElement/Element} container
8152 * container The id or element that will contain the toolbar
8155 * @cfg {Boolean} displayInfo
8156 * True to display the displayMsg (defaults to false)
8159 * @cfg {Number} pageSize
8160 * The number of records to display per page (defaults to 20)
8164 * @cfg {String} displayMsg
8165 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8167 displayMsg : 'Displaying {0} - {1} of {2}',
8169 * @cfg {String} emptyMsg
8170 * The message to display when no records are found (defaults to "No data to display")
8172 emptyMsg : 'No data to display',
8174 * Customizable piece of the default paging text (defaults to "Page")
8177 beforePageText : "Page",
8179 * Customizable piece of the default paging text (defaults to "of %0")
8182 afterPageText : "of {0}",
8184 * Customizable piece of the default paging text (defaults to "First Page")
8187 firstText : "First Page",
8189 * Customizable piece of the default paging text (defaults to "Previous Page")
8192 prevText : "Previous Page",
8194 * Customizable piece of the default paging text (defaults to "Next Page")
8197 nextText : "Next Page",
8199 * Customizable piece of the default paging text (defaults to "Last Page")
8202 lastText : "Last Page",
8204 * Customizable piece of the default paging text (defaults to "Refresh")
8207 refreshText : "Refresh",
8210 renderButtons : function(el){
8211 Roo.PagingToolbar.superclass.render.call(this, el);
8212 this.first = this.addButton({
8213 tooltip: this.firstText,
8214 cls: "x-btn-icon x-grid-page-first",
8216 handler: this.onClick.createDelegate(this, ["first"])
8218 this.prev = this.addButton({
8219 tooltip: this.prevText,
8220 cls: "x-btn-icon x-grid-page-prev",
8222 handler: this.onClick.createDelegate(this, ["prev"])
8224 //this.addSeparator();
8225 this.add(this.beforePageText);
8226 this.field = Roo.get(this.addDom({
8231 cls: "x-grid-page-number"
8233 this.field.on("keydown", this.onPagingKeydown, this);
8234 this.field.on("focus", function(){this.dom.select();});
8235 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8236 this.field.setHeight(18);
8237 //this.addSeparator();
8238 this.next = this.addButton({
8239 tooltip: this.nextText,
8240 cls: "x-btn-icon x-grid-page-next",
8242 handler: this.onClick.createDelegate(this, ["next"])
8244 this.last = this.addButton({
8245 tooltip: this.lastText,
8246 cls: "x-btn-icon x-grid-page-last",
8248 handler: this.onClick.createDelegate(this, ["last"])
8250 //this.addSeparator();
8251 this.loading = this.addButton({
8252 tooltip: this.refreshText,
8253 cls: "x-btn-icon x-grid-loading",
8254 handler: this.onClick.createDelegate(this, ["refresh"])
8257 if(this.displayInfo){
8258 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8263 updateInfo : function(){
8265 var count = this.ds.getCount();
8266 var msg = count == 0 ?
8270 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
8272 this.displayEl.update(msg);
8277 onLoad : function(ds, r, o){
8278 this.cursor = o.params ? o.params.start : 0;
8279 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8281 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8282 this.field.dom.value = ap;
8283 this.first.setDisabled(ap == 1);
8284 this.prev.setDisabled(ap == 1);
8285 this.next.setDisabled(ap == ps);
8286 this.last.setDisabled(ap == ps);
8287 this.loading.enable();
8292 getPageData : function(){
8293 var total = this.ds.getTotalCount();
8296 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8297 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8302 onLoadError : function(){
8303 this.loading.enable();
8307 onPagingKeydown : function(e){
8309 var d = this.getPageData();
8311 var v = this.field.dom.value, pageNum;
8312 if(!v || isNaN(pageNum = parseInt(v, 10))){
8313 this.field.dom.value = d.activePage;
8316 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8317 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8320 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))
8322 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8323 this.field.dom.value = pageNum;
8324 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8327 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8329 var v = this.field.dom.value, pageNum;
8330 var increment = (e.shiftKey) ? 10 : 1;
8331 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8334 if(!v || isNaN(pageNum = parseInt(v, 10))) {
8335 this.field.dom.value = d.activePage;
8338 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8340 this.field.dom.value = parseInt(v, 10) + increment;
8341 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8342 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8349 beforeLoad : function(){
8351 this.loading.disable();
8356 onClick : function(which){
8360 ds.load({params:{start: 0, limit: this.pageSize}});
8363 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8366 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8369 var total = ds.getTotalCount();
8370 var extra = total % this.pageSize;
8371 var lastStart = extra ? (total - extra) : total-this.pageSize;
8372 ds.load({params:{start: lastStart, limit: this.pageSize}});
8375 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8381 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8382 * @param {Roo.data.Store} store The data store to unbind
8384 unbind : function(ds){
8385 ds.un("beforeload", this.beforeLoad, this);
8386 ds.un("load", this.onLoad, this);
8387 ds.un("loadexception", this.onLoadError, this);
8388 ds.un("remove", this.updateInfo, this);
8389 ds.un("add", this.updateInfo, this);
8390 this.ds = undefined;
8394 * Binds the paging toolbar to the specified {@link Roo.data.Store}
8395 * @param {Roo.data.Store} store The data store to bind
8397 bind : function(ds){
8398 ds.on("beforeload", this.beforeLoad, this);
8399 ds.on("load", this.onLoad, this);
8400 ds.on("loadexception", this.onLoadError, this);
8401 ds.on("remove", this.updateInfo, this);
8402 ds.on("add", this.updateInfo, this);
8407 * Ext JS Library 1.1.1
8408 * Copyright(c) 2006-2007, Ext JS, LLC.
8410 * Originally Released Under LGPL - original licence link has changed is not relivant.
8413 * <script type="text/javascript">
8417 * @class Roo.Resizable
8418 * @extends Roo.util.Observable
8419 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8420 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8421 * 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
8422 * the element will be wrapped for you automatically.</p>
8423 * <p>Here is the list of valid resize handles:</p>
8426 ------ -------------------
8435 'hd' horizontal drag
8438 * <p>Here's an example showing the creation of a typical Resizable:</p>
8440 var resizer = new Roo.Resizable("element-id", {
8448 resizer.on("resize", myHandler);
8450 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8451 * resizer.east.setDisplayed(false);</p>
8452 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8453 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8454 * resize operation's new size (defaults to [0, 0])
8455 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8456 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8457 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8458 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8459 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8460 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8461 * @cfg {Number} width The width of the element in pixels (defaults to null)
8462 * @cfg {Number} height The height of the element in pixels (defaults to null)
8463 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8464 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8465 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8466 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8467 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
8468 * in favor of the handles config option (defaults to false)
8469 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8470 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8471 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8472 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8473 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8474 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8475 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8476 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8477 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8478 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8479 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8481 * Create a new resizable component
8482 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8483 * @param {Object} config configuration options
8485 Roo.Resizable = function(el, config)
8487 this.el = Roo.get(el);
8489 if(config && config.wrap){
8490 config.resizeChild = this.el;
8491 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8492 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8493 this.el.setStyle("overflow", "hidden");
8494 this.el.setPositioning(config.resizeChild.getPositioning());
8495 config.resizeChild.clearPositioning();
8496 if(!config.width || !config.height){
8497 var csize = config.resizeChild.getSize();
8498 this.el.setSize(csize.width, csize.height);
8500 if(config.pinned && !config.adjustments){
8501 config.adjustments = "auto";
8505 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8506 this.proxy.unselectable();
8507 this.proxy.enableDisplayMode('block');
8509 Roo.apply(this, config);
8512 this.disableTrackOver = true;
8513 this.el.addClass("x-resizable-pinned");
8515 // if the element isn't positioned, make it relative
8516 var position = this.el.getStyle("position");
8517 if(position != "absolute" && position != "fixed"){
8518 this.el.setStyle("position", "relative");
8520 if(!this.handles){ // no handles passed, must be legacy style
8521 this.handles = 's,e,se';
8522 if(this.multiDirectional){
8523 this.handles += ',n,w';
8526 if(this.handles == "all"){
8527 this.handles = "n s e w ne nw se sw";
8529 var hs = this.handles.split(/\s*?[,;]\s*?| /);
8530 var ps = Roo.Resizable.positions;
8531 for(var i = 0, len = hs.length; i < len; i++){
8532 if(hs[i] && ps[hs[i]]){
8533 var pos = ps[hs[i]];
8534 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8538 this.corner = this.southeast;
8540 // updateBox = the box can move..
8541 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8542 this.updateBox = true;
8545 this.activeHandle = null;
8547 if(this.resizeChild){
8548 if(typeof this.resizeChild == "boolean"){
8549 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8551 this.resizeChild = Roo.get(this.resizeChild, true);
8555 if(this.adjustments == "auto"){
8556 var rc = this.resizeChild;
8557 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8558 if(rc && (hw || hn)){
8559 rc.position("relative");
8560 rc.setLeft(hw ? hw.el.getWidth() : 0);
8561 rc.setTop(hn ? hn.el.getHeight() : 0);
8563 this.adjustments = [
8564 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8565 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8570 this.dd = this.dynamic ?
8571 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8572 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8578 * @event beforeresize
8579 * Fired before resize is allowed. Set enabled to false to cancel resize.
8580 * @param {Roo.Resizable} this
8581 * @param {Roo.EventObject} e The mousedown event
8583 "beforeresize" : true,
8587 * @param {Roo.Resizable} this
8588 * @param {Number} x The new x position
8589 * @param {Number} y The new y position
8590 * @param {Number} w The new w width
8591 * @param {Number} h The new h hight
8592 * @param {Roo.EventObject} e The mouseup event
8597 * Fired after a resize.
8598 * @param {Roo.Resizable} this
8599 * @param {Number} width The new width
8600 * @param {Number} height The new height
8601 * @param {Roo.EventObject} e The mouseup event
8606 if(this.width !== null && this.height !== null){
8607 this.resizeTo(this.width, this.height);
8609 this.updateChildSize();
8612 this.el.dom.style.zoom = 1;
8614 Roo.Resizable.superclass.constructor.call(this);
8617 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8618 resizeChild : false,
8619 adjustments : [0, 0],
8629 multiDirectional : false,
8630 disableTrackOver : false,
8631 easing : 'easeOutStrong',
8633 heightIncrement : 0,
8637 preserveRatio : false,
8644 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8646 constrainTo: undefined,
8648 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8650 resizeRegion: undefined,
8654 * Perform a manual resize
8655 * @param {Number} width
8656 * @param {Number} height
8658 resizeTo : function(width, height){
8659 this.el.setSize(width, height);
8660 this.updateChildSize();
8661 this.fireEvent("resize", this, width, height, null);
8665 startSizing : function(e, handle){
8666 this.fireEvent("beforeresize", this, e);
8667 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8670 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
8671 this.overlay.unselectable();
8672 this.overlay.enableDisplayMode("block");
8673 this.overlay.on("mousemove", this.onMouseMove, this);
8674 this.overlay.on("mouseup", this.onMouseUp, this);
8676 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8678 this.resizing = true;
8679 this.startBox = this.el.getBox();
8680 this.startPoint = e.getXY();
8681 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8682 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8684 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8685 this.overlay.show();
8687 if(this.constrainTo) {
8688 var ct = Roo.get(this.constrainTo);
8689 this.resizeRegion = ct.getRegion().adjust(
8690 ct.getFrameWidth('t'),
8691 ct.getFrameWidth('l'),
8692 -ct.getFrameWidth('b'),
8693 -ct.getFrameWidth('r')
8697 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8699 this.proxy.setBox(this.startBox);
8701 this.proxy.setStyle('visibility', 'visible');
8707 onMouseDown : function(handle, e){
8710 this.activeHandle = handle;
8711 this.startSizing(e, handle);
8716 onMouseUp : function(e){
8717 var size = this.resizeElement();
8718 this.resizing = false;
8720 this.overlay.hide();
8722 this.fireEvent("resize", this, size.width, size.height, e);
8726 updateChildSize : function(){
8728 if(this.resizeChild){
8730 var child = this.resizeChild;
8731 var adj = this.adjustments;
8732 if(el.dom.offsetWidth){
8733 var b = el.getSize(true);
8734 child.setSize(b.width+adj[0], b.height+adj[1]);
8736 // Second call here for IE
8737 // The first call enables instant resizing and
8738 // the second call corrects scroll bars if they
8741 setTimeout(function(){
8742 if(el.dom.offsetWidth){
8743 var b = el.getSize(true);
8744 child.setSize(b.width+adj[0], b.height+adj[1]);
8752 snap : function(value, inc, min){
8753 if(!inc || !value) {
8756 var newValue = value;
8757 var m = value % inc;
8760 newValue = value + (inc-m);
8762 newValue = value - m;
8765 return Math.max(min, newValue);
8769 resizeElement : function(){
8770 var box = this.proxy.getBox();
8772 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8774 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8776 this.updateChildSize();
8784 constrain : function(v, diff, m, mx){
8787 }else if(v - diff > mx){
8794 onMouseMove : function(e){
8797 try{// try catch so if something goes wrong the user doesn't get hung
8799 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8803 //var curXY = this.startPoint;
8804 var curSize = this.curSize || this.startBox;
8805 var x = this.startBox.x, y = this.startBox.y;
8807 var w = curSize.width, h = curSize.height;
8809 var mw = this.minWidth, mh = this.minHeight;
8810 var mxw = this.maxWidth, mxh = this.maxHeight;
8811 var wi = this.widthIncrement;
8812 var hi = this.heightIncrement;
8814 var eventXY = e.getXY();
8815 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8816 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8818 var pos = this.activeHandle.position;
8823 w = Math.min(Math.max(mw, w), mxw);
8828 h = Math.min(Math.max(mh, h), mxh);
8833 w = Math.min(Math.max(mw, w), mxw);
8834 h = Math.min(Math.max(mh, h), mxh);
8837 diffY = this.constrain(h, diffY, mh, mxh);
8844 var adiffX = Math.abs(diffX);
8845 var sub = (adiffX % wi); // how much
8846 if (sub > (wi/2)) { // far enough to snap
8847 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8849 // remove difference..
8850 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8854 x = Math.max(this.minX, x);
8857 diffX = this.constrain(w, diffX, mw, mxw);
8863 w = Math.min(Math.max(mw, w), mxw);
8864 diffY = this.constrain(h, diffY, mh, mxh);
8869 diffX = this.constrain(w, diffX, mw, mxw);
8870 diffY = this.constrain(h, diffY, mh, mxh);
8877 diffX = this.constrain(w, diffX, mw, mxw);
8879 h = Math.min(Math.max(mh, h), mxh);
8885 var sw = this.snap(w, wi, mw);
8886 var sh = this.snap(h, hi, mh);
8887 if(sw != w || sh != h){
8910 if(this.preserveRatio){
8915 h = Math.min(Math.max(mh, h), mxh);
8920 w = Math.min(Math.max(mw, w), mxw);
8925 w = Math.min(Math.max(mw, w), mxw);
8931 w = Math.min(Math.max(mw, w), mxw);
8937 h = Math.min(Math.max(mh, h), mxh);
8945 h = Math.min(Math.max(mh, h), mxh);
8955 h = Math.min(Math.max(mh, h), mxh);
8963 if (pos == 'hdrag') {
8966 this.proxy.setBounds(x, y, w, h);
8968 this.resizeElement();
8972 this.fireEvent("resizing", this, x, y, w, h, e);
8976 handleOver : function(){
8978 this.el.addClass("x-resizable-over");
8983 handleOut : function(){
8985 this.el.removeClass("x-resizable-over");
8990 * Returns the element this component is bound to.
8991 * @return {Roo.Element}
8998 * Returns the resizeChild element (or null).
8999 * @return {Roo.Element}
9001 getResizeChild : function(){
9002 return this.resizeChild;
9004 groupHandler : function()
9009 * Destroys this resizable. If the element was wrapped and
9010 * removeEl is not true then the element remains.
9011 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9013 destroy : function(removeEl){
9014 this.proxy.remove();
9016 this.overlay.removeAllListeners();
9017 this.overlay.remove();
9019 var ps = Roo.Resizable.positions;
9021 if(typeof ps[k] != "function" && this[ps[k]]){
9022 var h = this[ps[k]];
9023 h.el.removeAllListeners();
9035 // hash to map config positions to true positions
9036 Roo.Resizable.positions = {
9037 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
9042 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
9044 // only initialize the template if resizable is used
9045 var tpl = Roo.DomHelper.createTemplate(
9046 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
9049 Roo.Resizable.Handle.prototype.tpl = tpl;
9051 this.position = pos;
9053 // show north drag fro topdra
9054 var handlepos = pos == 'hdrag' ? 'north' : pos;
9056 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
9057 if (pos == 'hdrag') {
9058 this.el.setStyle('cursor', 'pointer');
9060 this.el.unselectable();
9062 this.el.setOpacity(0);
9064 this.el.on("mousedown", this.onMouseDown, this);
9065 if(!disableTrackOver){
9066 this.el.on("mouseover", this.onMouseOver, this);
9067 this.el.on("mouseout", this.onMouseOut, this);
9072 Roo.Resizable.Handle.prototype = {
9073 afterResize : function(rz){
9078 onMouseDown : function(e){
9079 this.rz.onMouseDown(this, e);
9082 onMouseOver : function(e){
9083 this.rz.handleOver(this, e);
9086 onMouseOut : function(e){
9087 this.rz.handleOut(this, e);
9091 * Ext JS Library 1.1.1
9092 * Copyright(c) 2006-2007, Ext JS, LLC.
9094 * Originally Released Under LGPL - original licence link has changed is not relivant.
9097 * <script type="text/javascript">
9102 * @extends Roo.Component
9103 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9105 * Create a new Editor
9106 * @param {Roo.form.Field} field The Field object (or descendant)
9107 * @param {Object} config The config object
9109 Roo.Editor = function(field, config){
9110 Roo.Editor.superclass.constructor.call(this, config);
9114 * @event beforestartedit
9115 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
9116 * false from the handler of this event.
9117 * @param {Editor} this
9118 * @param {Roo.Element} boundEl The underlying element bound to this editor
9119 * @param {Mixed} value The field value being set
9121 "beforestartedit" : true,
9124 * Fires when this editor is displayed
9125 * @param {Roo.Element} boundEl The underlying element bound to this editor
9126 * @param {Mixed} value The starting field value
9130 * @event beforecomplete
9131 * Fires after a change has been made to the field, but before the change is reflected in the underlying
9132 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
9133 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9134 * event will not fire since no edit actually occurred.
9135 * @param {Editor} this
9136 * @param {Mixed} value The current field value
9137 * @param {Mixed} startValue The original field value
9139 "beforecomplete" : true,
9142 * Fires after editing is complete and any changed value has been written to the underlying field.
9143 * @param {Editor} this
9144 * @param {Mixed} value The current field value
9145 * @param {Mixed} startValue The original field value
9150 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9151 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9152 * @param {Roo.form.Field} this
9153 * @param {Roo.EventObject} e The event object
9159 Roo.extend(Roo.Editor, Roo.Component, {
9161 * @cfg {Boolean/String} autosize
9162 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9163 * or "height" to adopt the height only (defaults to false)
9166 * @cfg {Boolean} revertInvalid
9167 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9168 * validation fails (defaults to true)
9171 * @cfg {Boolean} ignoreNoChange
9172 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9173 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
9174 * will never be ignored.
9177 * @cfg {Boolean} hideEl
9178 * False to keep the bound element visible while the editor is displayed (defaults to true)
9181 * @cfg {Mixed} value
9182 * The data value of the underlying field (defaults to "")
9186 * @cfg {String} alignment
9187 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9191 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9192 * for bottom-right shadow (defaults to "frame")
9196 * @cfg {Boolean} constrain True to constrain the editor to the viewport
9200 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9202 completeOnEnter : false,
9204 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9206 cancelOnEsc : false,
9208 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9213 onRender : function(ct, position){
9214 this.el = new Roo.Layer({
9215 shadow: this.shadow,
9221 constrain: this.constrain
9223 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9224 if(this.field.msgTarget != 'title'){
9225 this.field.msgTarget = 'qtip';
9227 this.field.render(this.el);
9229 this.field.el.dom.setAttribute('autocomplete', 'off');
9231 this.field.on("specialkey", this.onSpecialKey, this);
9232 if(this.swallowKeys){
9233 this.field.el.swallowEvent(['keydown','keypress']);
9236 this.field.on("blur", this.onBlur, this);
9237 if(this.field.grow){
9238 this.field.on("autosize", this.el.sync, this.el, {delay:1});
9242 onSpecialKey : function(field, e)
9244 //Roo.log('editor onSpecialKey');
9245 if(this.completeOnEnter && e.getKey() == e.ENTER){
9247 this.completeEdit();
9250 // do not fire special key otherwise it might hide close the editor...
9251 if(e.getKey() == e.ENTER){
9254 if(this.cancelOnEsc && e.getKey() == e.ESC){
9258 this.fireEvent('specialkey', field, e);
9263 * Starts the editing process and shows the editor.
9264 * @param {String/HTMLElement/Element} el The element to edit
9265 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9266 * to the innerHTML of el.
9268 startEdit : function(el, value){
9270 this.completeEdit();
9272 this.boundEl = Roo.get(el);
9273 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9275 this.render(this.parentEl || document.body);
9277 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9280 this.startValue = v;
9281 this.field.setValue(v);
9283 var sz = this.boundEl.getSize();
9284 switch(this.autoSize){
9286 this.setSize(sz.width, "");
9289 this.setSize("", sz.height);
9292 this.setSize(sz.width, sz.height);
9295 this.el.alignTo(this.boundEl, this.alignment);
9296 this.editing = true;
9298 Roo.QuickTips.disable();
9304 * Sets the height and width of this editor.
9305 * @param {Number} width The new width
9306 * @param {Number} height The new height
9308 setSize : function(w, h){
9309 this.field.setSize(w, h);
9316 * Realigns the editor to the bound field based on the current alignment config value.
9318 realign : function(){
9319 this.el.alignTo(this.boundEl, this.alignment);
9323 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9324 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9326 completeEdit : function(remainVisible){
9330 var v = this.getValue();
9331 if(this.revertInvalid !== false && !this.field.isValid()){
9332 v = this.startValue;
9333 this.cancelEdit(true);
9335 if(String(v) === String(this.startValue) && this.ignoreNoChange){
9336 this.editing = false;
9340 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9341 this.editing = false;
9342 if(this.updateEl && this.boundEl){
9343 this.boundEl.update(v);
9345 if(remainVisible !== true){
9348 this.fireEvent("complete", this, v, this.startValue);
9353 onShow : function(){
9355 if(this.hideEl !== false){
9356 this.boundEl.hide();
9359 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9360 this.fixIEFocus = true;
9361 this.deferredFocus.defer(50, this);
9365 this.fireEvent("startedit", this.boundEl, this.startValue);
9368 deferredFocus : function(){
9375 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
9376 * reverted to the original starting value.
9377 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9378 * cancel (defaults to false)
9380 cancelEdit : function(remainVisible){
9382 this.setValue(this.startValue);
9383 if(remainVisible !== true){
9390 onBlur : function(){
9391 if(this.allowBlur !== true && this.editing){
9392 this.completeEdit();
9397 onHide : function(){
9399 this.completeEdit();
9403 if(this.field.collapse){
9404 this.field.collapse();
9407 if(this.hideEl !== false){
9408 this.boundEl.show();
9411 Roo.QuickTips.enable();
9416 * Sets the data value of the editor
9417 * @param {Mixed} value Any valid value supported by the underlying field
9419 setValue : function(v){
9420 this.field.setValue(v);
9424 * Gets the data value of the editor
9425 * @return {Mixed} The data value
9427 getValue : function(){
9428 return this.field.getValue();
9432 * Ext JS Library 1.1.1
9433 * Copyright(c) 2006-2007, Ext JS, LLC.
9435 * Originally Released Under LGPL - original licence link has changed is not relivant.
9438 * <script type="text/javascript">
9442 * @class Roo.BasicDialog
9443 * @extends Roo.util.Observable
9444 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
9446 var dlg = new Roo.BasicDialog("my-dlg", {
9455 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9456 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
9457 dlg.addButton('Cancel', dlg.hide, dlg);
9460 <b>A Dialog should always be a direct child of the body element.</b>
9461 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9462 * @cfg {String} title Default text to display in the title bar (defaults to null)
9463 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9464 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9465 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9466 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9467 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9468 * (defaults to null with no animation)
9469 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9470 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9471 * property for valid values (defaults to 'all')
9472 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9473 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9474 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9475 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9476 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9477 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9478 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9479 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9480 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9481 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9482 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9483 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9484 * draggable = true (defaults to false)
9485 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9486 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9487 * shadow (defaults to false)
9488 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9489 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9490 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9491 * @cfg {Array} buttons Array of buttons
9492 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9494 * Create a new BasicDialog.
9495 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9496 * @param {Object} config Configuration options
9498 Roo.BasicDialog = function(el, config){
9499 this.el = Roo.get(el);
9500 var dh = Roo.DomHelper;
9501 if(!this.el && config && config.autoCreate){
9502 if(typeof config.autoCreate == "object"){
9503 if(!config.autoCreate.id){
9504 config.autoCreate.id = el;
9506 this.el = dh.append(document.body,
9507 config.autoCreate, true);
9509 this.el = dh.append(document.body,
9510 {tag: "div", id: el, style:'visibility:hidden;'}, true);
9514 el.setDisplayed(true);
9515 el.hide = this.hideAction;
9517 el.addClass("x-dlg");
9519 Roo.apply(this, config);
9521 this.proxy = el.createProxy("x-dlg-proxy");
9522 this.proxy.hide = this.hideAction;
9523 this.proxy.setOpacity(.5);
9527 el.setWidth(config.width);
9530 el.setHeight(config.height);
9532 this.size = el.getSize();
9533 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9534 this.xy = [config.x,config.y];
9536 this.xy = el.getCenterXY(true);
9538 /** The header element @type Roo.Element */
9539 this.header = el.child("> .x-dlg-hd");
9540 /** The body element @type Roo.Element */
9541 this.body = el.child("> .x-dlg-bd");
9542 /** The footer element @type Roo.Element */
9543 this.footer = el.child("> .x-dlg-ft");
9546 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
9549 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9552 this.header.unselectable();
9554 this.header.update(this.title);
9556 // this element allows the dialog to be focused for keyboard event
9557 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9558 this.focusEl.swallowEvent("click", true);
9560 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9562 // wrap the body and footer for special rendering
9563 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9565 this.bwrap.dom.appendChild(this.footer.dom);
9568 this.bg = this.el.createChild({
9569 tag: "div", cls:"x-dlg-bg",
9570 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
9572 this.centerBg = this.bg.child("div.x-dlg-bg-center");
9575 if(this.autoScroll !== false && !this.autoTabs){
9576 this.body.setStyle("overflow", "auto");
9579 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9581 if(this.closable !== false){
9582 this.el.addClass("x-dlg-closable");
9583 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9584 this.close.on("click", this.closeClick, this);
9585 this.close.addClassOnOver("x-dlg-close-over");
9587 if(this.collapsible !== false){
9588 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9589 this.collapseBtn.on("click", this.collapseClick, this);
9590 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9591 this.header.on("dblclick", this.collapseClick, this);
9593 if(this.resizable !== false){
9594 this.el.addClass("x-dlg-resizable");
9595 this.resizer = new Roo.Resizable(el, {
9596 minWidth: this.minWidth || 80,
9597 minHeight:this.minHeight || 80,
9598 handles: this.resizeHandles || "all",
9601 this.resizer.on("beforeresize", this.beforeResize, this);
9602 this.resizer.on("resize", this.onResize, this);
9604 if(this.draggable !== false){
9605 el.addClass("x-dlg-draggable");
9606 if (!this.proxyDrag) {
9607 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9610 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9612 dd.setHandleElId(this.header.id);
9613 dd.endDrag = this.endMove.createDelegate(this);
9614 dd.startDrag = this.startMove.createDelegate(this);
9615 dd.onDrag = this.onDrag.createDelegate(this);
9620 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9621 this.mask.enableDisplayMode("block");
9623 this.el.addClass("x-dlg-modal");
9626 this.shadow = new Roo.Shadow({
9627 mode : typeof this.shadow == "string" ? this.shadow : "sides",
9628 offset : this.shadowOffset
9631 this.shadowOffset = 0;
9633 if(Roo.useShims && this.shim !== false){
9634 this.shim = this.el.createShim();
9635 this.shim.hide = this.hideAction;
9644 var bts= this.buttons;
9646 Roo.each(bts, function(b) {
9655 * Fires when a key is pressed
9656 * @param {Roo.BasicDialog} this
9657 * @param {Roo.EventObject} e
9662 * Fires when this dialog is moved by the user.
9663 * @param {Roo.BasicDialog} this
9664 * @param {Number} x The new page X
9665 * @param {Number} y The new page Y
9670 * Fires when this dialog is resized by the user.
9671 * @param {Roo.BasicDialog} this
9672 * @param {Number} width The new width
9673 * @param {Number} height The new height
9678 * Fires before this dialog is hidden.
9679 * @param {Roo.BasicDialog} this
9681 "beforehide" : true,
9684 * Fires when this dialog is hidden.
9685 * @param {Roo.BasicDialog} this
9690 * Fires before this dialog is shown.
9691 * @param {Roo.BasicDialog} this
9693 "beforeshow" : true,
9696 * Fires when this dialog is shown.
9697 * @param {Roo.BasicDialog} this
9701 el.on("keydown", this.onKeyDown, this);
9702 el.on("mousedown", this.toFront, this);
9703 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9705 Roo.DialogManager.register(this);
9706 Roo.BasicDialog.superclass.constructor.call(this);
9709 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9710 shadowOffset: Roo.isIE ? 6 : 5,
9714 defaultButton: null,
9715 buttonAlign: "right",
9720 * Sets the dialog title text
9721 * @param {String} text The title text to display
9722 * @return {Roo.BasicDialog} this
9724 setTitle : function(text){
9725 this.header.update(text);
9730 closeClick : function(){
9735 collapseClick : function(){
9736 this[this.collapsed ? "expand" : "collapse"]();
9740 * Collapses the dialog to its minimized state (only the title bar is visible).
9741 * Equivalent to the user clicking the collapse dialog button.
9743 collapse : function(){
9744 if(!this.collapsed){
9745 this.collapsed = true;
9746 this.el.addClass("x-dlg-collapsed");
9747 this.restoreHeight = this.el.getHeight();
9748 this.resizeTo(this.el.getWidth(), this.header.getHeight());
9753 * Expands a collapsed dialog back to its normal state. Equivalent to the user
9754 * clicking the expand dialog button.
9756 expand : function(){
9758 this.collapsed = false;
9759 this.el.removeClass("x-dlg-collapsed");
9760 this.resizeTo(this.el.getWidth(), this.restoreHeight);
9765 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9766 * @return {Roo.TabPanel} The tabs component
9768 initTabs : function(){
9769 var tabs = this.getTabs();
9770 while(tabs.getTab(0)){
9773 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9775 tabs.addTab(Roo.id(dom), dom.title);
9783 beforeResize : function(){
9784 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9788 onResize : function(){
9790 this.syncBodyHeight();
9791 this.adjustAssets();
9793 this.fireEvent("resize", this, this.size.width, this.size.height);
9797 onKeyDown : function(e){
9798 if(this.isVisible()){
9799 this.fireEvent("keydown", this, e);
9804 * Resizes the dialog.
9805 * @param {Number} width
9806 * @param {Number} height
9807 * @return {Roo.BasicDialog} this
9809 resizeTo : function(width, height){
9810 this.el.setSize(width, height);
9811 this.size = {width: width, height: height};
9812 this.syncBodyHeight();
9813 if(this.fixedcenter){
9816 if(this.isVisible()){
9818 this.adjustAssets();
9820 this.fireEvent("resize", this, width, height);
9826 * Resizes the dialog to fit the specified content size.
9827 * @param {Number} width
9828 * @param {Number} height
9829 * @return {Roo.BasicDialog} this
9831 setContentSize : function(w, h){
9832 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9833 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9834 //if(!this.el.isBorderBox()){
9835 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9836 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9839 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9840 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9842 this.resizeTo(w, h);
9847 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
9848 * executed in response to a particular key being pressed while the dialog is active.
9849 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9850 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9851 * @param {Function} fn The function to call
9852 * @param {Object} scope (optional) The scope of the function
9853 * @return {Roo.BasicDialog} this
9855 addKeyListener : function(key, fn, scope){
9856 var keyCode, shift, ctrl, alt;
9857 if(typeof key == "object" && !(key instanceof Array)){
9858 keyCode = key["key"];
9859 shift = key["shift"];
9865 var handler = function(dlg, e){
9866 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
9868 if(keyCode instanceof Array){
9869 for(var i = 0, len = keyCode.length; i < len; i++){
9870 if(keyCode[i] == k){
9871 fn.call(scope || window, dlg, k, e);
9877 fn.call(scope || window, dlg, k, e);
9882 this.on("keydown", handler);
9887 * Returns the TabPanel component (creates it if it doesn't exist).
9888 * Note: If you wish to simply check for the existence of tabs without creating them,
9889 * check for a null 'tabs' property.
9890 * @return {Roo.TabPanel} The tabs component
9892 getTabs : function(){
9894 this.el.addClass("x-dlg-auto-tabs");
9895 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9896 this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9902 * Adds a button to the footer section of the dialog.
9903 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9904 * object or a valid Roo.DomHelper element config
9905 * @param {Function} handler The function called when the button is clicked
9906 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9907 * @return {Roo.Button} The new button
9909 addButton : function(config, handler, scope){
9910 var dh = Roo.DomHelper;
9912 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9914 if(!this.btnContainer){
9915 var tb = this.footer.createChild({
9917 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9918 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9920 this.btnContainer = tb.firstChild.firstChild.firstChild;
9925 minWidth: this.minButtonWidth,
9928 if(typeof config == "string"){
9929 bconfig.text = config;
9932 bconfig.dhconfig = config;
9934 Roo.apply(bconfig, config);
9938 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9939 bconfig.position = Math.max(0, bconfig.position);
9940 fc = this.btnContainer.childNodes[bconfig.position];
9943 var btn = new Roo.Button(
9945 this.btnContainer.insertBefore(document.createElement("td"),fc)
9946 : this.btnContainer.appendChild(document.createElement("td")),
9947 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
9950 this.syncBodyHeight();
9953 * Array of all the buttons that have been added to this dialog via addButton
9958 this.buttons.push(btn);
9963 * Sets the default button to be focused when the dialog is displayed.
9964 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9965 * @return {Roo.BasicDialog} this
9967 setDefaultButton : function(btn){
9968 this.defaultButton = btn;
9973 getHeaderFooterHeight : function(safe){
9976 height += this.header.getHeight();
9979 var fm = this.footer.getMargins();
9980 height += (this.footer.getHeight()+fm.top+fm.bottom);
9982 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9983 height += this.centerBg.getPadding("tb");
9988 syncBodyHeight : function()
9990 var bd = this.body, // the text
9991 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9993 var height = this.size.height - this.getHeaderFooterHeight(false);
9994 bd.setHeight(height-bd.getMargins("tb"));
9995 var hh = this.header.getHeight();
9996 var h = this.size.height-hh;
9999 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
10000 bw.setHeight(h-cb.getPadding("tb"));
10002 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
10003 bd.setWidth(bw.getWidth(true));
10005 this.tabs.syncHeight();
10007 this.tabs.el.repaint();
10013 * Restores the previous state of the dialog if Roo.state is configured.
10014 * @return {Roo.BasicDialog} this
10016 restoreState : function(){
10017 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
10018 if(box && box.width){
10019 this.xy = [box.x, box.y];
10020 this.resizeTo(box.width, box.height);
10026 beforeShow : function(){
10028 if(this.fixedcenter){
10029 this.xy = this.el.getCenterXY(true);
10032 Roo.get(document.body).addClass("x-body-masked");
10033 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10036 this.constrainXY();
10040 animShow : function(){
10041 var b = Roo.get(this.animateTarget).getBox();
10042 this.proxy.setSize(b.width, b.height);
10043 this.proxy.setLocation(b.x, b.y);
10045 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
10046 true, .35, this.showEl.createDelegate(this));
10050 * Shows the dialog.
10051 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
10052 * @return {Roo.BasicDialog} this
10054 show : function(animateTarget){
10055 if (this.fireEvent("beforeshow", this) === false){
10058 if(this.syncHeightBeforeShow){
10059 this.syncBodyHeight();
10060 }else if(this.firstShow){
10061 this.firstShow = false;
10062 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10064 this.animateTarget = animateTarget || this.animateTarget;
10065 if(!this.el.isVisible()){
10067 if(this.animateTarget && Roo.get(this.animateTarget)){
10077 showEl : function(){
10079 this.el.setXY(this.xy);
10081 this.adjustAssets(true);
10084 // IE peekaboo bug - fix found by Dave Fenwick
10088 this.fireEvent("show", this);
10092 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
10093 * dialog itself will receive focus.
10095 focus : function(){
10096 if(this.defaultButton){
10097 this.defaultButton.focus();
10099 this.focusEl.focus();
10104 constrainXY : function(){
10105 if(this.constraintoviewport !== false){
10106 if(!this.viewSize){
10107 if(this.container){
10108 var s = this.container.getSize();
10109 this.viewSize = [s.width, s.height];
10111 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10114 var s = Roo.get(this.container||document).getScroll();
10116 var x = this.xy[0], y = this.xy[1];
10117 var w = this.size.width, h = this.size.height;
10118 var vw = this.viewSize[0], vh = this.viewSize[1];
10119 // only move it if it needs it
10121 // first validate right/bottom
10122 if(x + w > vw+s.left){
10126 if(y + h > vh+s.top){
10130 // then make sure top/left isn't negative
10142 if(this.isVisible()){
10143 this.el.setLocation(x, y);
10144 this.adjustAssets();
10151 onDrag : function(){
10152 if(!this.proxyDrag){
10153 this.xy = this.el.getXY();
10154 this.adjustAssets();
10159 adjustAssets : function(doShow){
10160 var x = this.xy[0], y = this.xy[1];
10161 var w = this.size.width, h = this.size.height;
10162 if(doShow === true){
10164 this.shadow.show(this.el);
10170 if(this.shadow && this.shadow.isVisible()){
10171 this.shadow.show(this.el);
10173 if(this.shim && this.shim.isVisible()){
10174 this.shim.setBounds(x, y, w, h);
10179 adjustViewport : function(w, h){
10181 w = Roo.lib.Dom.getViewWidth();
10182 h = Roo.lib.Dom.getViewHeight();
10185 this.viewSize = [w, h];
10186 if(this.modal && this.mask.isVisible()){
10187 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10188 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10190 if(this.isVisible()){
10191 this.constrainXY();
10196 * Destroys this dialog and all its supporting elements (including any tabs, shim,
10197 * shadow, proxy, mask, etc.) Also removes all event listeners.
10198 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10200 destroy : function(removeEl){
10201 if(this.isVisible()){
10202 this.animateTarget = null;
10205 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10207 this.tabs.destroy(removeEl);
10220 for(var i = 0, len = this.buttons.length; i < len; i++){
10221 this.buttons[i].destroy();
10224 this.el.removeAllListeners();
10225 if(removeEl === true){
10226 this.el.update("");
10229 Roo.DialogManager.unregister(this);
10233 startMove : function(){
10234 if(this.proxyDrag){
10237 if(this.constraintoviewport !== false){
10238 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10243 endMove : function(){
10244 if(!this.proxyDrag){
10245 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10247 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10250 this.refreshSize();
10251 this.adjustAssets();
10253 this.fireEvent("move", this, this.xy[0], this.xy[1]);
10257 * Brings this dialog to the front of any other visible dialogs
10258 * @return {Roo.BasicDialog} this
10260 toFront : function(){
10261 Roo.DialogManager.bringToFront(this);
10266 * Sends this dialog to the back (under) of any other visible dialogs
10267 * @return {Roo.BasicDialog} this
10269 toBack : function(){
10270 Roo.DialogManager.sendToBack(this);
10275 * Centers this dialog in the viewport
10276 * @return {Roo.BasicDialog} this
10278 center : function(){
10279 var xy = this.el.getCenterXY(true);
10280 this.moveTo(xy[0], xy[1]);
10285 * Moves the dialog's top-left corner to the specified point
10286 * @param {Number} x
10287 * @param {Number} y
10288 * @return {Roo.BasicDialog} this
10290 moveTo : function(x, y){
10292 if(this.isVisible()){
10293 this.el.setXY(this.xy);
10294 this.adjustAssets();
10300 * Aligns the dialog to the specified element
10301 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10302 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10303 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10304 * @return {Roo.BasicDialog} this
10306 alignTo : function(element, position, offsets){
10307 this.xy = this.el.getAlignToXY(element, position, offsets);
10308 if(this.isVisible()){
10309 this.el.setXY(this.xy);
10310 this.adjustAssets();
10316 * Anchors an element to another element and realigns it when the window is resized.
10317 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10318 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10319 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10320 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10321 * is a number, it is used as the buffer delay (defaults to 50ms).
10322 * @return {Roo.BasicDialog} this
10324 anchorTo : function(el, alignment, offsets, monitorScroll){
10325 var action = function(){
10326 this.alignTo(el, alignment, offsets);
10328 Roo.EventManager.onWindowResize(action, this);
10329 var tm = typeof monitorScroll;
10330 if(tm != 'undefined'){
10331 Roo.EventManager.on(window, 'scroll', action, this,
10332 {buffer: tm == 'number' ? monitorScroll : 50});
10339 * Returns true if the dialog is visible
10340 * @return {Boolean}
10342 isVisible : function(){
10343 return this.el.isVisible();
10347 animHide : function(callback){
10348 var b = Roo.get(this.animateTarget).getBox();
10350 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10352 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10353 this.hideEl.createDelegate(this, [callback]));
10357 * Hides the dialog.
10358 * @param {Function} callback (optional) Function to call when the dialog is hidden
10359 * @return {Roo.BasicDialog} this
10361 hide : function(callback){
10362 if (this.fireEvent("beforehide", this) === false){
10366 this.shadow.hide();
10371 // sometimes animateTarget seems to get set.. causing problems...
10372 // this just double checks..
10373 if(this.animateTarget && Roo.get(this.animateTarget)) {
10374 this.animHide(callback);
10377 this.hideEl(callback);
10383 hideEl : function(callback){
10387 Roo.get(document.body).removeClass("x-body-masked");
10389 this.fireEvent("hide", this);
10390 if(typeof callback == "function"){
10396 hideAction : function(){
10397 this.setLeft("-10000px");
10398 this.setTop("-10000px");
10399 this.setStyle("visibility", "hidden");
10403 refreshSize : function(){
10404 this.size = this.el.getSize();
10405 this.xy = this.el.getXY();
10406 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10410 // z-index is managed by the DialogManager and may be overwritten at any time
10411 setZIndex : function(index){
10413 this.mask.setStyle("z-index", index);
10416 this.shim.setStyle("z-index", ++index);
10419 this.shadow.setZIndex(++index);
10421 this.el.setStyle("z-index", ++index);
10423 this.proxy.setStyle("z-index", ++index);
10426 this.resizer.proxy.setStyle("z-index", ++index);
10429 this.lastZIndex = index;
10433 * Returns the element for this dialog
10434 * @return {Roo.Element} The underlying dialog Element
10436 getEl : function(){
10442 * @class Roo.DialogManager
10443 * Provides global access to BasicDialogs that have been created and
10444 * support for z-indexing (layering) multiple open dialogs.
10446 Roo.DialogManager = function(){
10448 var accessList = [];
10452 var sortDialogs = function(d1, d2){
10453 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10457 var orderDialogs = function(){
10458 accessList.sort(sortDialogs);
10459 var seed = Roo.DialogManager.zseed;
10460 for(var i = 0, len = accessList.length; i < len; i++){
10461 var dlg = accessList[i];
10463 dlg.setZIndex(seed + (i*10));
10470 * The starting z-index for BasicDialogs (defaults to 9000)
10471 * @type Number The z-index value
10476 register : function(dlg){
10477 list[dlg.id] = dlg;
10478 accessList.push(dlg);
10482 unregister : function(dlg){
10483 delete list[dlg.id];
10486 if(!accessList.indexOf){
10487 for( i = 0, len = accessList.length; i < len; i++){
10488 if(accessList[i] == dlg){
10489 accessList.splice(i, 1);
10494 i = accessList.indexOf(dlg);
10496 accessList.splice(i, 1);
10502 * Gets a registered dialog by id
10503 * @param {String/Object} id The id of the dialog or a dialog
10504 * @return {Roo.BasicDialog} this
10506 get : function(id){
10507 return typeof id == "object" ? id : list[id];
10511 * Brings the specified dialog to the front
10512 * @param {String/Object} dlg The id of the dialog or a dialog
10513 * @return {Roo.BasicDialog} this
10515 bringToFront : function(dlg){
10516 dlg = this.get(dlg);
10519 dlg._lastAccess = new Date().getTime();
10526 * Sends the specified dialog to the back
10527 * @param {String/Object} dlg The id of the dialog or a dialog
10528 * @return {Roo.BasicDialog} this
10530 sendToBack : function(dlg){
10531 dlg = this.get(dlg);
10532 dlg._lastAccess = -(new Date().getTime());
10538 * Hides all dialogs
10540 hideAll : function(){
10541 for(var id in list){
10542 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10551 * @class Roo.LayoutDialog
10552 * @extends Roo.BasicDialog
10553 * Dialog which provides adjustments for working with a layout in a Dialog.
10554 * Add your necessary layout config options to the dialog's config.<br>
10555 * Example usage (including a nested layout):
10558 dialog = new Roo.LayoutDialog("download-dlg", {
10567 // layout config merges with the dialog config
10569 tabPosition: "top",
10570 alwaysShowTabs: true
10573 dialog.addKeyListener(27, dialog.hide, dialog);
10574 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10575 dialog.addButton("Build It!", this.getDownload, this);
10577 // we can even add nested layouts
10578 var innerLayout = new Roo.BorderLayout("dl-inner", {
10588 innerLayout.beginUpdate();
10589 innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10590 innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10591 innerLayout.endUpdate(true);
10593 var layout = dialog.getLayout();
10594 layout.beginUpdate();
10595 layout.add("center", new Roo.ContentPanel("standard-panel",
10596 {title: "Download the Source", fitToFrame:true}));
10597 layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10598 {title: "Build your own roo.js"}));
10599 layout.getRegion("center").showPanel(sp);
10600 layout.endUpdate();
10604 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10605 * @param {Object} config configuration options
10607 Roo.LayoutDialog = function(el, cfg){
10610 if (typeof(cfg) == 'undefined') {
10611 config = Roo.apply({}, el);
10612 // not sure why we use documentElement here.. - it should always be body.
10613 // IE7 borks horribly if we use documentElement.
10614 // webkit also does not like documentElement - it creates a body element...
10615 el = Roo.get( document.body || document.documentElement ).createChild();
10616 //config.autoCreate = true;
10620 config.autoTabs = false;
10621 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10622 this.body.setStyle({overflow:"hidden", position:"relative"});
10623 this.layout = new Roo.BorderLayout(this.body.dom, config);
10624 this.layout.monitorWindowResize = false;
10625 this.el.addClass("x-dlg-auto-layout");
10626 // fix case when center region overwrites center function
10627 this.center = Roo.BasicDialog.prototype.center;
10628 this.on("show", this.layout.layout, this.layout, true);
10629 if (config.items) {
10630 var xitems = config.items;
10631 delete config.items;
10632 Roo.each(xitems, this.addxtype, this);
10637 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10639 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10642 endUpdate : function(){
10643 this.layout.endUpdate();
10647 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10650 beginUpdate : function(){
10651 this.layout.beginUpdate();
10655 * Get the BorderLayout for this dialog
10656 * @return {Roo.BorderLayout}
10658 getLayout : function(){
10659 return this.layout;
10662 showEl : function(){
10663 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10665 this.layout.layout();
10670 // Use the syncHeightBeforeShow config option to control this automatically
10671 syncBodyHeight : function(){
10672 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10673 if(this.layout){this.layout.layout();}
10677 * Add an xtype element (actually adds to the layout.)
10678 * @return {Object} xdata xtype object data.
10681 addxtype : function(c) {
10682 return this.layout.addxtype(c);
10686 * Ext JS Library 1.1.1
10687 * Copyright(c) 2006-2007, Ext JS, LLC.
10689 * Originally Released Under LGPL - original licence link has changed is not relivant.
10692 * <script type="text/javascript">
10696 * @class Roo.MessageBox
10697 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
10701 Roo.Msg.alert('Status', 'Changes saved successfully.');
10703 // Prompt for user data:
10704 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10706 // process text value...
10710 // Show a dialog using config options:
10712 title:'Save Changes?',
10713 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10714 buttons: Roo.Msg.YESNOCANCEL,
10721 Roo.MessageBox = function(){
10722 var dlg, opt, mask, waitTimer;
10723 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10724 var buttons, activeTextEl, bwidth;
10727 var handleButton = function(button){
10729 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10733 var handleHide = function(){
10734 if(opt && opt.cls){
10735 dlg.el.removeClass(opt.cls);
10738 Roo.TaskMgr.stop(waitTimer);
10744 var updateButtons = function(b){
10747 buttons["ok"].hide();
10748 buttons["cancel"].hide();
10749 buttons["yes"].hide();
10750 buttons["no"].hide();
10751 dlg.footer.dom.style.display = 'none';
10754 dlg.footer.dom.style.display = '';
10755 for(var k in buttons){
10756 if(typeof buttons[k] != "function"){
10759 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10760 width += buttons[k].el.getWidth()+15;
10770 var handleEsc = function(d, k, e){
10771 if(opt && opt.closable !== false){
10781 * Returns a reference to the underlying {@link Roo.BasicDialog} element
10782 * @return {Roo.BasicDialog} The BasicDialog element
10784 getDialog : function(){
10786 dlg = new Roo.BasicDialog("x-msg-box", {
10791 constraintoviewport:false,
10793 collapsible : false,
10796 width:400, height:100,
10797 buttonAlign:"center",
10798 closeClick : function(){
10799 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10800 handleButton("no");
10802 handleButton("cancel");
10806 dlg.on("hide", handleHide);
10808 dlg.addKeyListener(27, handleEsc);
10810 var bt = this.buttonText;
10811 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10812 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10813 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10814 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10815 bodyEl = dlg.body.createChild({
10817 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>'
10819 msgEl = bodyEl.dom.firstChild;
10820 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10821 textboxEl.enableDisplayMode();
10822 textboxEl.addKeyListener([10,13], function(){
10823 if(dlg.isVisible() && opt && opt.buttons){
10824 if(opt.buttons.ok){
10825 handleButton("ok");
10826 }else if(opt.buttons.yes){
10827 handleButton("yes");
10831 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10832 textareaEl.enableDisplayMode();
10833 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10834 progressEl.enableDisplayMode();
10835 var pf = progressEl.dom.firstChild;
10837 pp = Roo.get(pf.firstChild);
10838 pp.setHeight(pf.offsetHeight);
10846 * Updates the message box body text
10847 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10848 * the XHTML-compliant non-breaking space character '&#160;')
10849 * @return {Roo.MessageBox} This message box
10851 updateText : function(text){
10852 if(!dlg.isVisible() && !opt.width){
10853 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10855 msgEl.innerHTML = text || ' ';
10857 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10858 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10860 Math.min(opt.width || cw , this.maxWidth),
10861 Math.max(opt.minWidth || this.minWidth, bwidth)
10864 activeTextEl.setWidth(w);
10866 if(dlg.isVisible()){
10867 dlg.fixedcenter = false;
10869 // to big, make it scroll. = But as usual stupid IE does not support
10872 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10873 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10874 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10876 bodyEl.dom.style.height = '';
10877 bodyEl.dom.style.overflowY = '';
10880 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10882 bodyEl.dom.style.overflowX = '';
10885 dlg.setContentSize(w, bodyEl.getHeight());
10886 if(dlg.isVisible()){
10887 dlg.fixedcenter = true;
10893 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
10894 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10895 * @param {Number} value Any number between 0 and 1 (e.g., .5)
10896 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10897 * @return {Roo.MessageBox} This message box
10899 updateProgress : function(value, text){
10901 this.updateText(text);
10903 if (pp) { // weird bug on my firefox - for some reason this is not defined
10904 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10910 * Returns true if the message box is currently displayed
10911 * @return {Boolean} True if the message box is visible, else false
10913 isVisible : function(){
10914 return dlg && dlg.isVisible();
10918 * Hides the message box if it is displayed
10921 if(this.isVisible()){
10927 * Displays a new message box, or reinitializes an existing message box, based on the config options
10928 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10929 * The following config object properties are supported:
10931 Property Type Description
10932 ---------- --------------- ------------------------------------------------------------------------------------
10933 animEl String/Element An id or Element from which the message box should animate as it opens and
10934 closes (defaults to undefined)
10935 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10936 cancel:'Bar'}), or false to not show any buttons (defaults to false)
10937 closable Boolean False to hide the top-right close button (defaults to true). Note that
10938 progress and wait dialogs will ignore this property and always hide the
10939 close button as they can only be closed programmatically.
10940 cls String A custom CSS class to apply to the message box element
10941 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
10942 displayed (defaults to 75)
10943 fn Function A callback function to execute after closing the dialog. The arguments to the
10944 function will be btn (the name of the button that was clicked, if applicable,
10945 e.g. "ok"), and text (the value of the active text field, if applicable).
10946 Progress and wait dialogs will ignore this option since they do not respond to
10947 user actions and can only be closed programmatically, so any required function
10948 should be called by the same code after it closes the dialog.
10949 icon String A CSS class that provides a background image to be used as an icon for
10950 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10951 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
10952 minWidth Number The minimum width in pixels of the message box (defaults to 100)
10953 modal Boolean False to allow user interaction with the page while the message box is
10954 displayed (defaults to true)
10955 msg String A string that will replace the existing message box body text (defaults
10956 to the XHTML-compliant non-breaking space character ' ')
10957 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
10958 progress Boolean True to display a progress bar (defaults to false)
10959 progressText String The text to display inside the progress bar if progress = true (defaults to '')
10960 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
10961 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
10962 title String The title text
10963 value String The string value to set into the active textbox element if displayed
10964 wait Boolean True to display a progress bar (defaults to false)
10965 width Number The width of the dialog in pixels
10972 msg: 'Please enter your address:',
10974 buttons: Roo.MessageBox.OKCANCEL,
10977 animEl: 'addAddressBtn'
10980 * @param {Object} config Configuration options
10981 * @return {Roo.MessageBox} This message box
10983 show : function(options)
10986 // this causes nightmares if you show one dialog after another
10987 // especially on callbacks..
10989 if(this.isVisible()){
10992 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10993 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
10994 Roo.log("New Dialog Message:" + options.msg )
10995 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10996 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10999 var d = this.getDialog();
11001 d.setTitle(opt.title || " ");
11002 d.close.setDisplayed(opt.closable !== false);
11003 activeTextEl = textboxEl;
11004 opt.prompt = opt.prompt || (opt.multiline ? true : false);
11009 textareaEl.setHeight(typeof opt.multiline == "number" ?
11010 opt.multiline : this.defaultTextHeight);
11011 activeTextEl = textareaEl;
11020 progressEl.setDisplayed(opt.progress === true);
11021 this.updateProgress(0);
11022 activeTextEl.dom.value = opt.value || "";
11024 dlg.setDefaultButton(activeTextEl);
11026 var bs = opt.buttons;
11029 db = buttons["ok"];
11030 }else if(bs && bs.yes){
11031 db = buttons["yes"];
11033 dlg.setDefaultButton(db);
11035 bwidth = updateButtons(opt.buttons);
11036 this.updateText(opt.msg);
11038 d.el.addClass(opt.cls);
11040 d.proxyDrag = opt.proxyDrag === true;
11041 d.modal = opt.modal !== false;
11042 d.mask = opt.modal !== false ? mask : false;
11043 if(!d.isVisible()){
11044 // force it to the end of the z-index stack so it gets a cursor in FF
11045 document.body.appendChild(dlg.el.dom);
11046 d.animateTarget = null;
11047 d.show(options.animEl);
11053 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
11054 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
11055 * and closing the message box when the process is complete.
11056 * @param {String} title The title bar text
11057 * @param {String} msg The message box body text
11058 * @return {Roo.MessageBox} This message box
11060 progress : function(title, msg){
11067 minWidth: this.minProgressWidth,
11074 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11075 * If a callback function is passed it will be called after the user clicks the button, and the
11076 * id of the button that was clicked will be passed as the only parameter to the callback
11077 * (could also be the top-right close button).
11078 * @param {String} title The title bar text
11079 * @param {String} msg The message box body text
11080 * @param {Function} fn (optional) The callback function invoked after the message box is closed
11081 * @param {Object} scope (optional) The scope of the callback function
11082 * @return {Roo.MessageBox} This message box
11084 alert : function(title, msg, fn, scope){
11097 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
11098 * interaction while waiting for a long-running process to complete that does not have defined intervals.
11099 * You are responsible for closing the message box when the process is complete.
11100 * @param {String} msg The message box body text
11101 * @param {String} title (optional) The title bar text
11102 * @return {Roo.MessageBox} This message box
11104 wait : function(msg, title){
11115 waitTimer = Roo.TaskMgr.start({
11117 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11125 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11126 * If a callback function is passed it will be called after the user clicks either button, and the id of the
11127 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11128 * @param {String} title The title bar text
11129 * @param {String} msg The message box body text
11130 * @param {Function} fn (optional) The callback function invoked after the message box is closed
11131 * @param {Object} scope (optional) The scope of the callback function
11132 * @return {Roo.MessageBox} This message box
11134 confirm : function(title, msg, fn, scope){
11138 buttons: this.YESNO,
11147 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11148 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
11149 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11150 * (could also be the top-right close button) and the text that was entered will be passed as the two
11151 * parameters to the callback.
11152 * @param {String} title The title bar text
11153 * @param {String} msg The message box body text
11154 * @param {Function} fn (optional) The callback function invoked after the message box is closed
11155 * @param {Object} scope (optional) The scope of the callback function
11156 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11157 * property, or the height in pixels to create the textbox (defaults to false / single-line)
11158 * @return {Roo.MessageBox} This message box
11160 prompt : function(title, msg, fn, scope, multiline){
11164 buttons: this.OKCANCEL,
11169 multiline: multiline,
11176 * Button config that displays a single OK button
11181 * Button config that displays Yes and No buttons
11184 YESNO : {yes:true, no:true},
11186 * Button config that displays OK and Cancel buttons
11189 OKCANCEL : {ok:true, cancel:true},
11191 * Button config that displays Yes, No and Cancel buttons
11194 YESNOCANCEL : {yes:true, no:true, cancel:true},
11197 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11200 defaultTextHeight : 75,
11202 * The maximum width in pixels of the message box (defaults to 600)
11207 * The minimum width in pixels of the message box (defaults to 100)
11212 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
11213 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11216 minProgressWidth : 250,
11218 * An object containing the default button text strings that can be overriden for localized language support.
11219 * Supported properties are: ok, cancel, yes and no.
11220 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11233 * Shorthand for {@link Roo.MessageBox}
11235 Roo.Msg = Roo.MessageBox;/*
11237 * Ext JS Library 1.1.1
11238 * Copyright(c) 2006-2007, Ext JS, LLC.
11240 * Originally Released Under LGPL - original licence link has changed is not relivant.
11243 * <script type="text/javascript">
11246 * @class Roo.QuickTips
11247 * Provides attractive and customizable tooltips for any element.
11250 Roo.QuickTips = function(){
11251 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11252 var ce, bd, xy, dd;
11253 var visible = false, disabled = true, inited = false;
11254 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11256 var onOver = function(e){
11260 var t = e.getTarget();
11261 if(!t || t.nodeType !== 1 || t == document || t == document.body){
11264 if(ce && t == ce.el){
11265 clearTimeout(hideProc);
11268 if(t && tagEls[t.id]){
11269 tagEls[t.id].el = t;
11270 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11273 var ttp, et = Roo.fly(t);
11274 var ns = cfg.namespace;
11275 if(tm.interceptTitles && t.title){
11278 t.removeAttribute("title");
11279 e.preventDefault();
11281 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11284 showProc = show.defer(tm.showDelay, tm, [{
11286 text: ttp.replace(/\\n/g,'<br/>'),
11287 width: et.getAttributeNS(ns, cfg.width),
11288 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11289 title: et.getAttributeNS(ns, cfg.title),
11290 cls: et.getAttributeNS(ns, cfg.cls)
11295 var onOut = function(e){
11296 clearTimeout(showProc);
11297 var t = e.getTarget();
11298 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11299 hideProc = setTimeout(hide, tm.hideDelay);
11303 var onMove = function(e){
11309 if(tm.trackMouse && ce){
11314 var onDown = function(e){
11315 clearTimeout(showProc);
11316 clearTimeout(hideProc);
11318 if(tm.hideOnClick){
11321 tm.enable.defer(100, tm);
11326 var getPad = function(){
11327 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11330 var show = function(o){
11334 clearTimeout(dismissProc);
11336 if(removeCls){ // in case manually hidden
11337 el.removeClass(removeCls);
11341 el.addClass(ce.cls);
11342 removeCls = ce.cls;
11345 tipTitle.update(ce.title);
11348 tipTitle.update('');
11351 el.dom.style.width = tm.maxWidth+'px';
11352 //tipBody.dom.style.width = '';
11353 tipBodyText.update(o.text);
11354 var p = getPad(), w = ce.width;
11356 var td = tipBodyText.dom;
11357 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11358 if(aw > tm.maxWidth){
11360 }else if(aw < tm.minWidth){
11366 //tipBody.setWidth(w);
11367 el.setWidth(parseInt(w, 10) + p);
11368 if(ce.autoHide === false){
11369 close.setDisplayed(true);
11374 close.setDisplayed(false);
11380 el.avoidY = xy[1]-18;
11385 el.setStyle("visibility", "visible");
11386 el.fadeIn({callback: afterShow});
11392 var afterShow = function(){
11396 if(tm.autoDismiss && ce.autoHide !== false){
11397 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11402 var hide = function(noanim){
11403 clearTimeout(dismissProc);
11404 clearTimeout(hideProc);
11406 if(el.isVisible()){
11408 if(noanim !== true && tm.animate){
11409 el.fadeOut({callback: afterHide});
11416 var afterHide = function(){
11419 el.removeClass(removeCls);
11426 * @cfg {Number} minWidth
11427 * The minimum width of the quick tip (defaults to 40)
11431 * @cfg {Number} maxWidth
11432 * The maximum width of the quick tip (defaults to 300)
11436 * @cfg {Boolean} interceptTitles
11437 * True to automatically use the element's DOM title value if available (defaults to false)
11439 interceptTitles : false,
11441 * @cfg {Boolean} trackMouse
11442 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11444 trackMouse : false,
11446 * @cfg {Boolean} hideOnClick
11447 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11449 hideOnClick : true,
11451 * @cfg {Number} showDelay
11452 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11456 * @cfg {Number} hideDelay
11457 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11461 * @cfg {Boolean} autoHide
11462 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11463 * Used in conjunction with hideDelay.
11468 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11469 * (defaults to true). Used in conjunction with autoDismissDelay.
11471 autoDismiss : true,
11474 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11476 autoDismissDelay : 5000,
11478 * @cfg {Boolean} animate
11479 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11484 * @cfg {String} title
11485 * Title text to display (defaults to ''). This can be any valid HTML markup.
11489 * @cfg {String} text
11490 * Body text to display (defaults to ''). This can be any valid HTML markup.
11494 * @cfg {String} cls
11495 * A CSS class to apply to the base quick tip element (defaults to '').
11499 * @cfg {Number} width
11500 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
11501 * minWidth or maxWidth.
11506 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
11507 * or display QuickTips in a page.
11510 tm = Roo.QuickTips;
11511 cfg = tm.tagConfig;
11513 if(!Roo.isReady){ // allow calling of init() before onReady
11514 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11517 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11518 el.fxDefaults = {stopFx: true};
11519 // maximum custom styling
11520 //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>');
11521 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>');
11522 tipTitle = el.child('h3');
11523 tipTitle.enableDisplayMode("block");
11524 tipBody = el.child('div.x-tip-bd');
11525 tipBodyText = el.child('div.x-tip-bd-inner');
11526 //bdLeft = el.child('div.x-tip-bd-left');
11527 //bdRight = el.child('div.x-tip-bd-right');
11528 close = el.child('div.x-tip-close');
11529 close.enableDisplayMode("block");
11530 close.on("click", hide);
11531 var d = Roo.get(document);
11532 d.on("mousedown", onDown);
11533 d.on("mouseover", onOver);
11534 d.on("mouseout", onOut);
11535 d.on("mousemove", onMove);
11536 esc = d.addKeyListener(27, hide);
11539 dd = el.initDD("default", null, {
11540 onDrag : function(){
11544 dd.setHandleElId(tipTitle.id);
11553 * Configures a new quick tip instance and assigns it to a target element. The following config options
11556 Property Type Description
11557 ---------- --------------------- ------------------------------------------------------------------------
11558 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
11560 * @param {Object} config The config object
11562 register : function(config){
11563 var cs = config instanceof Array ? config : arguments;
11564 for(var i = 0, len = cs.length; i < len; i++) {
11566 var target = c.target;
11568 if(target instanceof Array){
11569 for(var j = 0, jlen = target.length; j < jlen; j++){
11570 tagEls[target[j]] = c;
11573 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11580 * Removes this quick tip from its element and destroys it.
11581 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11583 unregister : function(el){
11584 delete tagEls[Roo.id(el)];
11588 * Enable this quick tip.
11590 enable : function(){
11591 if(inited && disabled){
11593 if(locks.length < 1){
11600 * Disable this quick tip.
11602 disable : function(){
11604 clearTimeout(showProc);
11605 clearTimeout(hideProc);
11606 clearTimeout(dismissProc);
11614 * Returns true if the quick tip is enabled, else false.
11616 isEnabled : function(){
11622 namespace : "roo", // was ext?? this may break..
11623 alt_namespace : "ext",
11624 attribute : "qtip",
11634 // backwards compat
11635 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11637 * Ext JS Library 1.1.1
11638 * Copyright(c) 2006-2007, Ext JS, LLC.
11640 * Originally Released Under LGPL - original licence link has changed is not relivant.
11643 * <script type="text/javascript">
11648 * @class Roo.tree.TreePanel
11649 * @extends Roo.data.Tree
11651 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11652 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11653 * @cfg {Boolean} enableDD true to enable drag and drop
11654 * @cfg {Boolean} enableDrag true to enable just drag
11655 * @cfg {Boolean} enableDrop true to enable just drop
11656 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11657 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11658 * @cfg {String} ddGroup The DD group this TreePanel belongs to
11659 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11660 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11661 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11662 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11663 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11664 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11665 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11666 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11667 * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11668 * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11669 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11670 * @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>
11671 * @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>
11674 * @param {String/HTMLElement/Element} el The container element
11675 * @param {Object} config
11677 Roo.tree.TreePanel = function(el, config){
11679 var loader = false;
11681 root = config.root;
11682 delete config.root;
11684 if (config.loader) {
11685 loader = config.loader;
11686 delete config.loader;
11689 Roo.apply(this, config);
11690 Roo.tree.TreePanel.superclass.constructor.call(this);
11691 this.el = Roo.get(el);
11692 this.el.addClass('x-tree');
11693 //console.log(root);
11695 this.setRootNode( Roo.factory(root, Roo.tree));
11698 this.loader = Roo.factory(loader, Roo.tree);
11701 * Read-only. The id of the container element becomes this TreePanel's id.
11703 this.id = this.el.id;
11706 * @event beforeload
11707 * Fires before a node is loaded, return false to cancel
11708 * @param {Node} node The node being loaded
11710 "beforeload" : true,
11713 * Fires when a node is loaded
11714 * @param {Node} node The node that was loaded
11718 * @event textchange
11719 * Fires when the text for a node is changed
11720 * @param {Node} node The node
11721 * @param {String} text The new text
11722 * @param {String} oldText The old text
11724 "textchange" : true,
11726 * @event beforeexpand
11727 * Fires before a node is expanded, return false to cancel.
11728 * @param {Node} node The node
11729 * @param {Boolean} deep
11730 * @param {Boolean} anim
11732 "beforeexpand" : true,
11734 * @event beforecollapse
11735 * Fires before a node is collapsed, return false to cancel.
11736 * @param {Node} node The node
11737 * @param {Boolean} deep
11738 * @param {Boolean} anim
11740 "beforecollapse" : true,
11743 * Fires when a node is expanded
11744 * @param {Node} node The node
11748 * @event disabledchange
11749 * Fires when the disabled status of a node changes
11750 * @param {Node} node The node
11751 * @param {Boolean} disabled
11753 "disabledchange" : true,
11756 * Fires when a node is collapsed
11757 * @param {Node} node The node
11761 * @event beforeclick
11762 * Fires before click processing on a node. Return false to cancel the default action.
11763 * @param {Node} node The node
11764 * @param {Roo.EventObject} e The event object
11766 "beforeclick":true,
11768 * @event checkchange
11769 * Fires when a node with a checkbox's checked property changes
11770 * @param {Node} this This node
11771 * @param {Boolean} checked
11773 "checkchange":true,
11776 * Fires when a node is clicked
11777 * @param {Node} node The node
11778 * @param {Roo.EventObject} e The event object
11783 * Fires when a node is double clicked
11784 * @param {Node} node The node
11785 * @param {Roo.EventObject} e The event object
11789 * @event contextmenu
11790 * Fires when a node is right clicked
11791 * @param {Node} node The node
11792 * @param {Roo.EventObject} e The event object
11794 "contextmenu":true,
11796 * @event beforechildrenrendered
11797 * Fires right before the child nodes for a node are rendered
11798 * @param {Node} node The node
11800 "beforechildrenrendered":true,
11803 * Fires when a node starts being dragged
11804 * @param {Roo.tree.TreePanel} this
11805 * @param {Roo.tree.TreeNode} node
11806 * @param {event} e The raw browser event
11808 "startdrag" : true,
11811 * Fires when a drag operation is complete
11812 * @param {Roo.tree.TreePanel} this
11813 * @param {Roo.tree.TreeNode} node
11814 * @param {event} e The raw browser event
11819 * Fires when a dragged node is dropped on a valid DD target
11820 * @param {Roo.tree.TreePanel} this
11821 * @param {Roo.tree.TreeNode} node
11822 * @param {DD} dd The dd it was dropped on
11823 * @param {event} e The raw browser event
11827 * @event beforenodedrop
11828 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11829 * passed to handlers has the following properties:<br />
11830 * <ul style="padding:5px;padding-left:16px;">
11831 * <li>tree - The TreePanel</li>
11832 * <li>target - The node being targeted for the drop</li>
11833 * <li>data - The drag data from the drag source</li>
11834 * <li>point - The point of the drop - append, above or below</li>
11835 * <li>source - The drag source</li>
11836 * <li>rawEvent - Raw mouse event</li>
11837 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11838 * to be inserted by setting them on this object.</li>
11839 * <li>cancel - Set this to true to cancel the drop.</li>
11841 * @param {Object} dropEvent
11843 "beforenodedrop" : true,
11846 * Fires after a DD object is dropped on a node in this tree. The dropEvent
11847 * passed to handlers has the following properties:<br />
11848 * <ul style="padding:5px;padding-left:16px;">
11849 * <li>tree - The TreePanel</li>
11850 * <li>target - The node being targeted for the drop</li>
11851 * <li>data - The drag data from the drag source</li>
11852 * <li>point - The point of the drop - append, above or below</li>
11853 * <li>source - The drag source</li>
11854 * <li>rawEvent - Raw mouse event</li>
11855 * <li>dropNode - Dropped node(s).</li>
11857 * @param {Object} dropEvent
11861 * @event nodedragover
11862 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11863 * passed to handlers has the following properties:<br />
11864 * <ul style="padding:5px;padding-left:16px;">
11865 * <li>tree - The TreePanel</li>
11866 * <li>target - The node being targeted for the drop</li>
11867 * <li>data - The drag data from the drag source</li>
11868 * <li>point - The point of the drop - append, above or below</li>
11869 * <li>source - The drag source</li>
11870 * <li>rawEvent - Raw mouse event</li>
11871 * <li>dropNode - Drop node(s) provided by the source.</li>
11872 * <li>cancel - Set this to true to signal drop not allowed.</li>
11874 * @param {Object} dragOverEvent
11876 "nodedragover" : true,
11878 * @event appendnode
11879 * Fires when append node to the tree
11880 * @param {Roo.tree.TreePanel} this
11881 * @param {Roo.tree.TreeNode} node
11882 * @param {Number} index The index of the newly appended node
11884 "appendnode" : true
11887 if(this.singleExpand){
11888 this.on("beforeexpand", this.restrictExpand, this);
11891 this.editor.tree = this;
11892 this.editor = Roo.factory(this.editor, Roo.tree);
11895 if (this.selModel) {
11896 this.selModel = Roo.factory(this.selModel, Roo.tree);
11900 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11901 rootVisible : true,
11902 animate: Roo.enableFx,
11905 hlDrop : Roo.enableFx,
11909 rendererTip: false,
11911 restrictExpand : function(node){
11912 var p = node.parentNode;
11914 if(p.expandedChild && p.expandedChild.parentNode == p){
11915 p.expandedChild.collapse();
11917 p.expandedChild = node;
11921 // private override
11922 setRootNode : function(node){
11923 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11924 if(!this.rootVisible){
11925 node.ui = new Roo.tree.RootTreeNodeUI(node);
11931 * Returns the container element for this TreePanel
11933 getEl : function(){
11938 * Returns the default TreeLoader for this TreePanel
11940 getLoader : function(){
11941 return this.loader;
11947 expandAll : function(){
11948 this.root.expand(true);
11952 * Collapse all nodes
11954 collapseAll : function(){
11955 this.root.collapse(true);
11959 * Returns the selection model used by this TreePanel
11961 getSelectionModel : function(){
11962 if(!this.selModel){
11963 this.selModel = new Roo.tree.DefaultSelectionModel();
11965 return this.selModel;
11969 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11970 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11971 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11974 getChecked : function(a, startNode){
11975 startNode = startNode || this.root;
11977 var f = function(){
11978 if(this.attributes.checked){
11979 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11982 startNode.cascade(f);
11987 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11988 * @param {String} path
11989 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11990 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11991 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11993 expandPath : function(path, attr, callback){
11994 attr = attr || "id";
11995 var keys = path.split(this.pathSeparator);
11996 var curNode = this.root;
11997 if(curNode.attributes[attr] != keys[1]){ // invalid root
11999 callback(false, null);
12004 var f = function(){
12005 if(++index == keys.length){
12007 callback(true, curNode);
12011 var c = curNode.findChild(attr, keys[index]);
12014 callback(false, curNode);
12019 c.expand(false, false, f);
12021 curNode.expand(false, false, f);
12025 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
12026 * @param {String} path
12027 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
12028 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
12029 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
12031 selectPath : function(path, attr, callback){
12032 attr = attr || "id";
12033 var keys = path.split(this.pathSeparator);
12034 var v = keys.pop();
12035 if(keys.length > 0){
12036 var f = function(success, node){
12037 if(success && node){
12038 var n = node.findChild(attr, v);
12044 }else if(callback){
12045 callback(false, n);
12049 callback(false, n);
12053 this.expandPath(keys.join(this.pathSeparator), attr, f);
12055 this.root.select();
12057 callback(true, this.root);
12062 getTreeEl : function(){
12067 * Trigger rendering of this TreePanel
12069 render : function(){
12070 if (this.innerCt) {
12071 return this; // stop it rendering more than once!!
12074 this.innerCt = this.el.createChild({tag:"ul",
12075 cls:"x-tree-root-ct " +
12076 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12078 if(this.containerScroll){
12079 Roo.dd.ScrollManager.register(this.el);
12081 if((this.enableDD || this.enableDrop) && !this.dropZone){
12083 * The dropZone used by this tree if drop is enabled
12084 * @type Roo.tree.TreeDropZone
12086 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12087 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12090 if((this.enableDD || this.enableDrag) && !this.dragZone){
12092 * The dragZone used by this tree if drag is enabled
12093 * @type Roo.tree.TreeDragZone
12095 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12096 ddGroup: this.ddGroup || "TreeDD",
12097 scroll: this.ddScroll
12100 this.getSelectionModel().init(this);
12102 Roo.log("ROOT not set in tree");
12105 this.root.render();
12106 if(!this.rootVisible){
12107 this.root.renderChildren();
12113 * Ext JS Library 1.1.1
12114 * Copyright(c) 2006-2007, Ext JS, LLC.
12116 * Originally Released Under LGPL - original licence link has changed is not relivant.
12119 * <script type="text/javascript">
12124 * @class Roo.tree.DefaultSelectionModel
12125 * @extends Roo.util.Observable
12126 * The default single selection for a TreePanel.
12127 * @param {Object} cfg Configuration
12129 Roo.tree.DefaultSelectionModel = function(cfg){
12130 this.selNode = null;
12136 * @event selectionchange
12137 * Fires when the selected node changes
12138 * @param {DefaultSelectionModel} this
12139 * @param {TreeNode} node the new selection
12141 "selectionchange" : true,
12144 * @event beforeselect
12145 * Fires before the selected node changes, return false to cancel the change
12146 * @param {DefaultSelectionModel} this
12147 * @param {TreeNode} node the new selection
12148 * @param {TreeNode} node the old selection
12150 "beforeselect" : true
12153 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12156 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12157 init : function(tree){
12159 tree.getTreeEl().on("keydown", this.onKeyDown, this);
12160 tree.on("click", this.onNodeClick, this);
12163 onNodeClick : function(node, e){
12164 if (e.ctrlKey && this.selNode == node) {
12165 this.unselect(node);
12173 * @param {TreeNode} node The node to select
12174 * @return {TreeNode} The selected node
12176 select : function(node){
12177 var last = this.selNode;
12178 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12180 last.ui.onSelectedChange(false);
12182 this.selNode = node;
12183 node.ui.onSelectedChange(true);
12184 this.fireEvent("selectionchange", this, node, last);
12191 * @param {TreeNode} node The node to unselect
12193 unselect : function(node){
12194 if(this.selNode == node){
12195 this.clearSelections();
12200 * Clear all selections
12202 clearSelections : function(){
12203 var n = this.selNode;
12205 n.ui.onSelectedChange(false);
12206 this.selNode = null;
12207 this.fireEvent("selectionchange", this, null);
12213 * Get the selected node
12214 * @return {TreeNode} The selected node
12216 getSelectedNode : function(){
12217 return this.selNode;
12221 * Returns true if the node is selected
12222 * @param {TreeNode} node The node to check
12223 * @return {Boolean}
12225 isSelected : function(node){
12226 return this.selNode == node;
12230 * Selects the node above the selected node in the tree, intelligently walking the nodes
12231 * @return TreeNode The new selection
12233 selectPrevious : function(){
12234 var s = this.selNode || this.lastSelNode;
12238 var ps = s.previousSibling;
12240 if(!ps.isExpanded() || ps.childNodes.length < 1){
12241 return this.select(ps);
12243 var lc = ps.lastChild;
12244 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12247 return this.select(lc);
12249 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12250 return this.select(s.parentNode);
12256 * Selects the node above the selected node in the tree, intelligently walking the nodes
12257 * @return TreeNode The new selection
12259 selectNext : function(){
12260 var s = this.selNode || this.lastSelNode;
12264 if(s.firstChild && s.isExpanded()){
12265 return this.select(s.firstChild);
12266 }else if(s.nextSibling){
12267 return this.select(s.nextSibling);
12268 }else if(s.parentNode){
12270 s.parentNode.bubble(function(){
12271 if(this.nextSibling){
12272 newS = this.getOwnerTree().selModel.select(this.nextSibling);
12281 onKeyDown : function(e){
12282 var s = this.selNode || this.lastSelNode;
12283 // undesirable, but required
12288 var k = e.getKey();
12296 this.selectPrevious();
12299 e.preventDefault();
12300 if(s.hasChildNodes()){
12301 if(!s.isExpanded()){
12303 }else if(s.firstChild){
12304 this.select(s.firstChild, e);
12309 e.preventDefault();
12310 if(s.hasChildNodes() && s.isExpanded()){
12312 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12313 this.select(s.parentNode, e);
12321 * @class Roo.tree.MultiSelectionModel
12322 * @extends Roo.util.Observable
12323 * Multi selection for a TreePanel.
12324 * @param {Object} cfg Configuration
12326 Roo.tree.MultiSelectionModel = function(){
12327 this.selNodes = [];
12331 * @event selectionchange
12332 * Fires when the selected nodes change
12333 * @param {MultiSelectionModel} this
12334 * @param {Array} nodes Array of the selected nodes
12336 "selectionchange" : true
12338 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12342 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12343 init : function(tree){
12345 tree.getTreeEl().on("keydown", this.onKeyDown, this);
12346 tree.on("click", this.onNodeClick, this);
12349 onNodeClick : function(node, e){
12350 this.select(node, e, e.ctrlKey);
12355 * @param {TreeNode} node The node to select
12356 * @param {EventObject} e (optional) An event associated with the selection
12357 * @param {Boolean} keepExisting True to retain existing selections
12358 * @return {TreeNode} The selected node
12360 select : function(node, e, keepExisting){
12361 if(keepExisting !== true){
12362 this.clearSelections(true);
12364 if(this.isSelected(node)){
12365 this.lastSelNode = node;
12368 this.selNodes.push(node);
12369 this.selMap[node.id] = node;
12370 this.lastSelNode = node;
12371 node.ui.onSelectedChange(true);
12372 this.fireEvent("selectionchange", this, this.selNodes);
12378 * @param {TreeNode} node The node to unselect
12380 unselect : function(node){
12381 if(this.selMap[node.id]){
12382 node.ui.onSelectedChange(false);
12383 var sn = this.selNodes;
12386 index = sn.indexOf(node);
12388 for(var i = 0, len = sn.length; i < len; i++){
12396 this.selNodes.splice(index, 1);
12398 delete this.selMap[node.id];
12399 this.fireEvent("selectionchange", this, this.selNodes);
12404 * Clear all selections
12406 clearSelections : function(suppressEvent){
12407 var sn = this.selNodes;
12409 for(var i = 0, len = sn.length; i < len; i++){
12410 sn[i].ui.onSelectedChange(false);
12412 this.selNodes = [];
12414 if(suppressEvent !== true){
12415 this.fireEvent("selectionchange", this, this.selNodes);
12421 * Returns true if the node is selected
12422 * @param {TreeNode} node The node to check
12423 * @return {Boolean}
12425 isSelected : function(node){
12426 return this.selMap[node.id] ? true : false;
12430 * Returns an array of the selected nodes
12433 getSelectedNodes : function(){
12434 return this.selNodes;
12437 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12439 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12441 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12444 * Ext JS Library 1.1.1
12445 * Copyright(c) 2006-2007, Ext JS, LLC.
12447 * Originally Released Under LGPL - original licence link has changed is not relivant.
12450 * <script type="text/javascript">
12454 * @class Roo.tree.TreeNode
12455 * @extends Roo.data.Node
12456 * @cfg {String} text The text for this node
12457 * @cfg {Boolean} expanded true to start the node expanded
12458 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12459 * @cfg {Boolean} allowDrop false if this node cannot be drop on
12460 * @cfg {Boolean} disabled true to start the node disabled
12461 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12462 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
12463 * @cfg {String} cls A css class to be added to the node
12464 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12465 * @cfg {String} href URL of the link used for the node (defaults to #)
12466 * @cfg {String} hrefTarget target frame for the link
12467 * @cfg {String} qtip An Ext QuickTip for the node
12468 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12469 * @cfg {Boolean} singleClickExpand True for single click expand on this node
12470 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12471 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12472 * (defaults to undefined with no checkbox rendered)
12474 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12476 Roo.tree.TreeNode = function(attributes){
12477 attributes = attributes || {};
12478 if(typeof attributes == "string"){
12479 attributes = {text: attributes};
12481 this.childrenRendered = false;
12482 this.rendered = false;
12483 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12484 this.expanded = attributes.expanded === true;
12485 this.isTarget = attributes.isTarget !== false;
12486 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12487 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12490 * Read-only. The text for this node. To change it use setText().
12493 this.text = attributes.text;
12495 * True if this node is disabled.
12498 this.disabled = attributes.disabled === true;
12502 * @event textchange
12503 * Fires when the text for this node is changed
12504 * @param {Node} this This node
12505 * @param {String} text The new text
12506 * @param {String} oldText The old text
12508 "textchange" : true,
12510 * @event beforeexpand
12511 * Fires before this node is expanded, return false to cancel.
12512 * @param {Node} this This node
12513 * @param {Boolean} deep
12514 * @param {Boolean} anim
12516 "beforeexpand" : true,
12518 * @event beforecollapse
12519 * Fires before this node is collapsed, return false to cancel.
12520 * @param {Node} this This node
12521 * @param {Boolean} deep
12522 * @param {Boolean} anim
12524 "beforecollapse" : true,
12527 * Fires when this node is expanded
12528 * @param {Node} this This node
12532 * @event disabledchange
12533 * Fires when the disabled status of this node changes
12534 * @param {Node} this This node
12535 * @param {Boolean} disabled
12537 "disabledchange" : true,
12540 * Fires when this node is collapsed
12541 * @param {Node} this This node
12545 * @event beforeclick
12546 * Fires before click processing. Return false to cancel the default action.
12547 * @param {Node} this This node
12548 * @param {Roo.EventObject} e The event object
12550 "beforeclick":true,
12552 * @event checkchange
12553 * Fires when a node with a checkbox's checked property changes
12554 * @param {Node} this This node
12555 * @param {Boolean} checked
12557 "checkchange":true,
12560 * Fires when this node is clicked
12561 * @param {Node} this This node
12562 * @param {Roo.EventObject} e The event object
12567 * Fires when this node is double clicked
12568 * @param {Node} this This node
12569 * @param {Roo.EventObject} e The event object
12573 * @event contextmenu
12574 * Fires when this node is right clicked
12575 * @param {Node} this This node
12576 * @param {Roo.EventObject} e The event object
12578 "contextmenu":true,
12580 * @event beforechildrenrendered
12581 * Fires right before the child nodes for this node are rendered
12582 * @param {Node} this This node
12584 "beforechildrenrendered":true
12587 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12590 * Read-only. The UI for this node
12593 this.ui = new uiClass(this);
12595 // finally support items[]
12596 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12601 Roo.each(this.attributes.items, function(c) {
12602 this.appendChild(Roo.factory(c,Roo.Tree));
12604 delete this.attributes.items;
12609 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12610 preventHScroll: true,
12612 * Returns true if this node is expanded
12613 * @return {Boolean}
12615 isExpanded : function(){
12616 return this.expanded;
12620 * Returns the UI object for this node
12621 * @return {TreeNodeUI}
12623 getUI : function(){
12627 // private override
12628 setFirstChild : function(node){
12629 var of = this.firstChild;
12630 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12631 if(this.childrenRendered && of && node != of){
12632 of.renderIndent(true, true);
12635 this.renderIndent(true, true);
12639 // private override
12640 setLastChild : function(node){
12641 var ol = this.lastChild;
12642 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12643 if(this.childrenRendered && ol && node != ol){
12644 ol.renderIndent(true, true);
12647 this.renderIndent(true, true);
12651 // these methods are overridden to provide lazy rendering support
12652 // private override
12653 appendChild : function()
12655 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12656 if(node && this.childrenRendered){
12659 this.ui.updateExpandIcon();
12663 // private override
12664 removeChild : function(node){
12665 this.ownerTree.getSelectionModel().unselect(node);
12666 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12667 // if it's been rendered remove dom node
12668 if(this.childrenRendered){
12671 if(this.childNodes.length < 1){
12672 this.collapse(false, false);
12674 this.ui.updateExpandIcon();
12676 if(!this.firstChild) {
12677 this.childrenRendered = false;
12682 // private override
12683 insertBefore : function(node, refNode){
12684 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12685 if(newNode && refNode && this.childrenRendered){
12688 this.ui.updateExpandIcon();
12693 * Sets the text for this node
12694 * @param {String} text
12696 setText : function(text){
12697 var oldText = this.text;
12699 this.attributes.text = text;
12700 if(this.rendered){ // event without subscribing
12701 this.ui.onTextChange(this, text, oldText);
12703 this.fireEvent("textchange", this, text, oldText);
12707 * Triggers selection of this node
12709 select : function(){
12710 this.getOwnerTree().getSelectionModel().select(this);
12714 * Triggers deselection of this node
12716 unselect : function(){
12717 this.getOwnerTree().getSelectionModel().unselect(this);
12721 * Returns true if this node is selected
12722 * @return {Boolean}
12724 isSelected : function(){
12725 return this.getOwnerTree().getSelectionModel().isSelected(this);
12729 * Expand this node.
12730 * @param {Boolean} deep (optional) True to expand all children as well
12731 * @param {Boolean} anim (optional) false to cancel the default animation
12732 * @param {Function} callback (optional) A callback to be called when
12733 * expanding this node completes (does not wait for deep expand to complete).
12734 * Called with 1 parameter, this node.
12736 expand : function(deep, anim, callback){
12737 if(!this.expanded){
12738 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12741 if(!this.childrenRendered){
12742 this.renderChildren();
12744 this.expanded = true;
12746 if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12747 this.ui.animExpand(function(){
12748 this.fireEvent("expand", this);
12749 if(typeof callback == "function"){
12753 this.expandChildNodes(true);
12755 }.createDelegate(this));
12759 this.fireEvent("expand", this);
12760 if(typeof callback == "function"){
12765 if(typeof callback == "function"){
12770 this.expandChildNodes(true);
12774 isHiddenRoot : function(){
12775 return this.isRoot && !this.getOwnerTree().rootVisible;
12779 * Collapse this node.
12780 * @param {Boolean} deep (optional) True to collapse all children as well
12781 * @param {Boolean} anim (optional) false to cancel the default animation
12783 collapse : function(deep, anim){
12784 if(this.expanded && !this.isHiddenRoot()){
12785 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12788 this.expanded = false;
12789 if((this.getOwnerTree().animate && anim !== false) || anim){
12790 this.ui.animCollapse(function(){
12791 this.fireEvent("collapse", this);
12793 this.collapseChildNodes(true);
12795 }.createDelegate(this));
12798 this.ui.collapse();
12799 this.fireEvent("collapse", this);
12803 var cs = this.childNodes;
12804 for(var i = 0, len = cs.length; i < len; i++) {
12805 cs[i].collapse(true, false);
12811 delayedExpand : function(delay){
12812 if(!this.expandProcId){
12813 this.expandProcId = this.expand.defer(delay, this);
12818 cancelExpand : function(){
12819 if(this.expandProcId){
12820 clearTimeout(this.expandProcId);
12822 this.expandProcId = false;
12826 * Toggles expanded/collapsed state of the node
12828 toggle : function(){
12837 * Ensures all parent nodes are expanded
12839 ensureVisible : function(callback){
12840 var tree = this.getOwnerTree();
12841 tree.expandPath(this.parentNode.getPath(), false, function(){
12842 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12843 Roo.callback(callback);
12844 }.createDelegate(this));
12848 * Expand all child nodes
12849 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12851 expandChildNodes : function(deep){
12852 var cs = this.childNodes;
12853 for(var i = 0, len = cs.length; i < len; i++) {
12854 cs[i].expand(deep);
12859 * Collapse all child nodes
12860 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12862 collapseChildNodes : function(deep){
12863 var cs = this.childNodes;
12864 for(var i = 0, len = cs.length; i < len; i++) {
12865 cs[i].collapse(deep);
12870 * Disables this node
12872 disable : function(){
12873 this.disabled = true;
12875 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12876 this.ui.onDisableChange(this, true);
12878 this.fireEvent("disabledchange", this, true);
12882 * Enables this node
12884 enable : function(){
12885 this.disabled = false;
12886 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12887 this.ui.onDisableChange(this, false);
12889 this.fireEvent("disabledchange", this, false);
12893 renderChildren : function(suppressEvent){
12894 if(suppressEvent !== false){
12895 this.fireEvent("beforechildrenrendered", this);
12897 var cs = this.childNodes;
12898 for(var i = 0, len = cs.length; i < len; i++){
12899 cs[i].render(true);
12901 this.childrenRendered = true;
12905 sort : function(fn, scope){
12906 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12907 if(this.childrenRendered){
12908 var cs = this.childNodes;
12909 for(var i = 0, len = cs.length; i < len; i++){
12910 cs[i].render(true);
12916 render : function(bulkRender){
12917 this.ui.render(bulkRender);
12918 if(!this.rendered){
12919 this.rendered = true;
12921 this.expanded = false;
12922 this.expand(false, false);
12928 renderIndent : function(deep, refresh){
12930 this.ui.childIndent = null;
12932 this.ui.renderIndent();
12933 if(deep === true && this.childrenRendered){
12934 var cs = this.childNodes;
12935 for(var i = 0, len = cs.length; i < len; i++){
12936 cs[i].renderIndent(true, refresh);
12942 * Ext JS Library 1.1.1
12943 * Copyright(c) 2006-2007, Ext JS, LLC.
12945 * Originally Released Under LGPL - original licence link has changed is not relivant.
12948 * <script type="text/javascript">
12952 * @class Roo.tree.AsyncTreeNode
12953 * @extends Roo.tree.TreeNode
12954 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12956 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12958 Roo.tree.AsyncTreeNode = function(config){
12959 this.loaded = false;
12960 this.loading = false;
12961 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12963 * @event beforeload
12964 * Fires before this node is loaded, return false to cancel
12965 * @param {Node} this This node
12967 this.addEvents({'beforeload':true, 'load': true});
12970 * Fires when this node is loaded
12971 * @param {Node} this This node
12974 * The loader used by this node (defaults to using the tree's defined loader)
12979 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12980 expand : function(deep, anim, callback){
12981 if(this.loading){ // if an async load is already running, waiting til it's done
12983 var f = function(){
12984 if(!this.loading){ // done loading
12985 clearInterval(timer);
12986 this.expand(deep, anim, callback);
12988 }.createDelegate(this);
12989 timer = setInterval(f, 200);
12993 if(this.fireEvent("beforeload", this) === false){
12996 this.loading = true;
12997 this.ui.beforeLoad(this);
12998 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
13000 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
13004 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
13008 * Returns true if this node is currently loading
13009 * @return {Boolean}
13011 isLoading : function(){
13012 return this.loading;
13015 loadComplete : function(deep, anim, callback){
13016 this.loading = false;
13017 this.loaded = true;
13018 this.ui.afterLoad(this);
13019 this.fireEvent("load", this);
13020 this.expand(deep, anim, callback);
13024 * Returns true if this node has been loaded
13025 * @return {Boolean}
13027 isLoaded : function(){
13028 return this.loaded;
13031 hasChildNodes : function(){
13032 if(!this.isLeaf() && !this.loaded){
13035 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
13040 * Trigger a reload for this node
13041 * @param {Function} callback
13043 reload : function(callback){
13044 this.collapse(false, false);
13045 while(this.firstChild){
13046 this.removeChild(this.firstChild);
13048 this.childrenRendered = false;
13049 this.loaded = false;
13050 if(this.isHiddenRoot()){
13051 this.expanded = false;
13053 this.expand(false, false, callback);
13057 * Ext JS Library 1.1.1
13058 * Copyright(c) 2006-2007, Ext JS, LLC.
13060 * Originally Released Under LGPL - original licence link has changed is not relivant.
13063 * <script type="text/javascript">
13067 * @class Roo.tree.TreeNodeUI
13069 * @param {Object} node The node to render
13070 * The TreeNode UI implementation is separate from the
13071 * tree implementation. Unless you are customizing the tree UI,
13072 * you should never have to use this directly.
13074 Roo.tree.TreeNodeUI = function(node){
13076 this.rendered = false;
13077 this.animating = false;
13078 this.emptyIcon = Roo.BLANK_IMAGE_URL;
13081 Roo.tree.TreeNodeUI.prototype = {
13082 removeChild : function(node){
13084 this.ctNode.removeChild(node.ui.getEl());
13088 beforeLoad : function(){
13089 this.addClass("x-tree-node-loading");
13092 afterLoad : function(){
13093 this.removeClass("x-tree-node-loading");
13096 onTextChange : function(node, text, oldText){
13098 this.textNode.innerHTML = text;
13102 onDisableChange : function(node, state){
13103 this.disabled = state;
13105 this.addClass("x-tree-node-disabled");
13107 this.removeClass("x-tree-node-disabled");
13111 onSelectedChange : function(state){
13114 this.addClass("x-tree-selected");
13117 this.removeClass("x-tree-selected");
13121 onMove : function(tree, node, oldParent, newParent, index, refNode){
13122 this.childIndent = null;
13124 var targetNode = newParent.ui.getContainer();
13125 if(!targetNode){//target not rendered
13126 this.holder = document.createElement("div");
13127 this.holder.appendChild(this.wrap);
13130 var insertBefore = refNode ? refNode.ui.getEl() : null;
13132 targetNode.insertBefore(this.wrap, insertBefore);
13134 targetNode.appendChild(this.wrap);
13136 this.node.renderIndent(true);
13140 addClass : function(cls){
13142 Roo.fly(this.elNode).addClass(cls);
13146 removeClass : function(cls){
13148 Roo.fly(this.elNode).removeClass(cls);
13152 remove : function(){
13154 this.holder = document.createElement("div");
13155 this.holder.appendChild(this.wrap);
13159 fireEvent : function(){
13160 return this.node.fireEvent.apply(this.node, arguments);
13163 initEvents : function(){
13164 this.node.on("move", this.onMove, this);
13165 var E = Roo.EventManager;
13166 var a = this.anchor;
13168 var el = Roo.fly(a, '_treeui');
13170 if(Roo.isOpera){ // opera render bug ignores the CSS
13171 el.setStyle("text-decoration", "none");
13174 el.on("click", this.onClick, this);
13175 el.on("dblclick", this.onDblClick, this);
13178 Roo.EventManager.on(this.checkbox,
13179 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13182 el.on("contextmenu", this.onContextMenu, this);
13184 var icon = Roo.fly(this.iconNode);
13185 icon.on("click", this.onClick, this);
13186 icon.on("dblclick", this.onDblClick, this);
13187 icon.on("contextmenu", this.onContextMenu, this);
13188 E.on(this.ecNode, "click", this.ecClick, this, true);
13190 if(this.node.disabled){
13191 this.addClass("x-tree-node-disabled");
13193 if(this.node.hidden){
13194 this.addClass("x-tree-node-disabled");
13196 var ot = this.node.getOwnerTree();
13197 var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
13198 if(dd && (!this.node.isRoot || ot.rootVisible)){
13199 Roo.dd.Registry.register(this.elNode, {
13201 handles: this.getDDHandles(),
13207 getDDHandles : function(){
13208 return [this.iconNode, this.textNode];
13213 this.wrap.style.display = "none";
13219 this.wrap.style.display = "";
13223 onContextMenu : function(e){
13224 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13225 e.preventDefault();
13227 this.fireEvent("contextmenu", this.node, e);
13231 onClick : function(e){
13236 if(this.fireEvent("beforeclick", this.node, e) !== false){
13237 if(!this.disabled && this.node.attributes.href){
13238 this.fireEvent("click", this.node, e);
13241 e.preventDefault();
13246 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13247 this.node.toggle();
13250 this.fireEvent("click", this.node, e);
13256 onDblClick : function(e){
13257 e.preventDefault();
13262 this.toggleCheck();
13264 if(!this.animating && this.node.hasChildNodes()){
13265 this.node.toggle();
13267 this.fireEvent("dblclick", this.node, e);
13270 onCheckChange : function(){
13271 var checked = this.checkbox.checked;
13272 this.node.attributes.checked = checked;
13273 this.fireEvent('checkchange', this.node, checked);
13276 ecClick : function(e){
13277 if(!this.animating && this.node.hasChildNodes()){
13278 this.node.toggle();
13282 startDrop : function(){
13283 this.dropping = true;
13286 // delayed drop so the click event doesn't get fired on a drop
13287 endDrop : function(){
13288 setTimeout(function(){
13289 this.dropping = false;
13290 }.createDelegate(this), 50);
13293 expand : function(){
13294 this.updateExpandIcon();
13295 this.ctNode.style.display = "";
13298 focus : function(){
13299 if(!this.node.preventHScroll){
13300 try{this.anchor.focus();
13302 }else if(!Roo.isIE){
13304 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13305 var l = noscroll.scrollLeft;
13306 this.anchor.focus();
13307 noscroll.scrollLeft = l;
13312 toggleCheck : function(value){
13313 var cb = this.checkbox;
13315 cb.checked = (value === undefined ? !cb.checked : value);
13321 this.anchor.blur();
13325 animExpand : function(callback){
13326 var ct = Roo.get(this.ctNode);
13328 if(!this.node.hasChildNodes()){
13329 this.updateExpandIcon();
13330 this.ctNode.style.display = "";
13331 Roo.callback(callback);
13334 this.animating = true;
13335 this.updateExpandIcon();
13338 callback : function(){
13339 this.animating = false;
13340 Roo.callback(callback);
13343 duration: this.node.ownerTree.duration || .25
13347 highlight : function(){
13348 var tree = this.node.getOwnerTree();
13349 Roo.fly(this.wrap).highlight(
13350 tree.hlColor || "C3DAF9",
13351 {endColor: tree.hlBaseColor}
13355 collapse : function(){
13356 this.updateExpandIcon();
13357 this.ctNode.style.display = "none";
13360 animCollapse : function(callback){
13361 var ct = Roo.get(this.ctNode);
13362 ct.enableDisplayMode('block');
13365 this.animating = true;
13366 this.updateExpandIcon();
13369 callback : function(){
13370 this.animating = false;
13371 Roo.callback(callback);
13374 duration: this.node.ownerTree.duration || .25
13378 getContainer : function(){
13379 return this.ctNode;
13382 getEl : function(){
13386 appendDDGhost : function(ghostNode){
13387 ghostNode.appendChild(this.elNode.cloneNode(true));
13390 getDDRepairXY : function(){
13391 return Roo.lib.Dom.getXY(this.iconNode);
13394 onRender : function(){
13398 render : function(bulkRender){
13399 var n = this.node, a = n.attributes;
13400 var targetNode = n.parentNode ?
13401 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13403 if(!this.rendered){
13404 this.rendered = true;
13406 this.renderElements(n, a, targetNode, bulkRender);
13409 if(this.textNode.setAttributeNS){
13410 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13412 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13415 this.textNode.setAttribute("ext:qtip", a.qtip);
13417 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13420 }else if(a.qtipCfg){
13421 a.qtipCfg.target = Roo.id(this.textNode);
13422 Roo.QuickTips.register(a.qtipCfg);
13425 if(!this.node.expanded){
13426 this.updateExpandIcon();
13429 if(bulkRender === true) {
13430 targetNode.appendChild(this.wrap);
13435 renderElements : function(n, a, targetNode, bulkRender)
13437 // add some indent caching, this helps performance when rendering a large tree
13438 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13439 var t = n.getOwnerTree();
13440 var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13441 if (typeof(n.attributes.html) != 'undefined') {
13442 txt = n.attributes.html;
13444 var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13445 var cb = typeof a.checked == 'boolean';
13446 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13447 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13448 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13449 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13450 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13451 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13452 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13453 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
13454 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13455 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13458 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13459 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13460 n.nextSibling.ui.getEl(), buf.join(""));
13462 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13465 this.elNode = this.wrap.childNodes[0];
13466 this.ctNode = this.wrap.childNodes[1];
13467 var cs = this.elNode.childNodes;
13468 this.indentNode = cs[0];
13469 this.ecNode = cs[1];
13470 this.iconNode = cs[2];
13473 this.checkbox = cs[3];
13476 this.anchor = cs[index];
13477 this.textNode = cs[index].firstChild;
13480 getAnchor : function(){
13481 return this.anchor;
13484 getTextEl : function(){
13485 return this.textNode;
13488 getIconEl : function(){
13489 return this.iconNode;
13492 isChecked : function(){
13493 return this.checkbox ? this.checkbox.checked : false;
13496 updateExpandIcon : function(){
13498 var n = this.node, c1, c2;
13499 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13500 var hasChild = n.hasChildNodes();
13504 c1 = "x-tree-node-collapsed";
13505 c2 = "x-tree-node-expanded";
13508 c1 = "x-tree-node-expanded";
13509 c2 = "x-tree-node-collapsed";
13512 this.removeClass("x-tree-node-leaf");
13513 this.wasLeaf = false;
13515 if(this.c1 != c1 || this.c2 != c2){
13516 Roo.fly(this.elNode).replaceClass(c1, c2);
13517 this.c1 = c1; this.c2 = c2;
13520 // this changes non-leafs into leafs if they have no children.
13521 // it's not very rational behaviour..
13523 if(!this.wasLeaf && this.node.leaf){
13524 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13527 this.wasLeaf = true;
13530 var ecc = "x-tree-ec-icon "+cls;
13531 if(this.ecc != ecc){
13532 this.ecNode.className = ecc;
13538 getChildIndent : function(){
13539 if(!this.childIndent){
13543 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13545 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13547 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13552 this.childIndent = buf.join("");
13554 return this.childIndent;
13557 renderIndent : function(){
13560 var p = this.node.parentNode;
13562 indent = p.ui.getChildIndent();
13564 if(this.indentMarkup != indent){ // don't rerender if not required
13565 this.indentNode.innerHTML = indent;
13566 this.indentMarkup = indent;
13568 this.updateExpandIcon();
13573 Roo.tree.RootTreeNodeUI = function(){
13574 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13576 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13577 render : function(){
13578 if(!this.rendered){
13579 var targetNode = this.node.ownerTree.innerCt.dom;
13580 this.node.expanded = true;
13581 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13582 this.wrap = this.ctNode = targetNode.firstChild;
13585 collapse : function(){
13587 expand : function(){
13591 * Ext JS Library 1.1.1
13592 * Copyright(c) 2006-2007, Ext JS, LLC.
13594 * Originally Released Under LGPL - original licence link has changed is not relivant.
13597 * <script type="text/javascript">
13600 * @class Roo.tree.TreeLoader
13601 * @extends Roo.util.Observable
13602 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13603 * nodes from a specified URL. The response must be a javascript Array definition
13604 * who's elements are node definition objects. eg:
13609 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13610 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13617 * The old style respose with just an array is still supported, but not recommended.
13620 * A server request is sent, and child nodes are loaded only when a node is expanded.
13621 * The loading node's id is passed to the server under the parameter name "node" to
13622 * enable the server to produce the correct child nodes.
13624 * To pass extra parameters, an event handler may be attached to the "beforeload"
13625 * event, and the parameters specified in the TreeLoader's baseParams property:
13627 myTreeLoader.on("beforeload", function(treeLoader, node) {
13628 this.baseParams.category = node.attributes.category;
13633 * This would pass an HTTP parameter called "category" to the server containing
13634 * the value of the Node's "category" attribute.
13636 * Creates a new Treeloader.
13637 * @param {Object} config A config object containing config properties.
13639 Roo.tree.TreeLoader = function(config){
13640 this.baseParams = {};
13641 this.requestMethod = "POST";
13642 Roo.apply(this, config);
13647 * @event beforeload
13648 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13649 * @param {Object} This TreeLoader object.
13650 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13651 * @param {Object} callback The callback function specified in the {@link #load} call.
13656 * Fires when the node has been successfuly loaded.
13657 * @param {Object} This TreeLoader object.
13658 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13659 * @param {Object} response The response object containing the data from the server.
13663 * @event loadexception
13664 * Fires if the network request failed.
13665 * @param {Object} This TreeLoader object.
13666 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13667 * @param {Object} response The response object containing the data from the server.
13669 loadexception : true,
13672 * Fires before a node is created, enabling you to return custom Node types
13673 * @param {Object} This TreeLoader object.
13674 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13679 Roo.tree.TreeLoader.superclass.constructor.call(this);
13682 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13684 * @cfg {String} dataUrl The URL from which to request a Json string which
13685 * specifies an array of node definition object representing the child nodes
13689 * @cfg {String} requestMethod either GET or POST
13690 * defaults to POST (due to BC)
13694 * @cfg {Object} baseParams (optional) An object containing properties which
13695 * specify HTTP parameters to be passed to each request for child nodes.
13698 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13699 * created by this loader. If the attributes sent by the server have an attribute in this object,
13700 * they take priority.
13703 * @cfg {Object} uiProviders (optional) An object containing properties which
13705 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13706 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13707 * <i>uiProvider</i> attribute of a returned child node is a string rather
13708 * than a reference to a TreeNodeUI implementation, this that string value
13709 * is used as a property name in the uiProviders object. You can define the provider named
13710 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13715 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13716 * child nodes before loading.
13718 clearOnLoad : true,
13721 * @cfg {String} root (optional) Default to false. Use this to read data from an object
13722 * property on loading, rather than expecting an array. (eg. more compatible to a standard
13723 * Grid query { data : [ .....] }
13728 * @cfg {String} queryParam (optional)
13729 * Name of the query as it will be passed on the querystring (defaults to 'node')
13730 * eg. the request will be ?node=[id]
13737 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13738 * This is called automatically when a node is expanded, but may be used to reload
13739 * a node (or append new children if the {@link #clearOnLoad} option is false.)
13740 * @param {Roo.tree.TreeNode} node
13741 * @param {Function} callback
13743 load : function(node, callback){
13744 if(this.clearOnLoad){
13745 while(node.firstChild){
13746 node.removeChild(node.firstChild);
13749 if(node.attributes.children){ // preloaded json children
13750 var cs = node.attributes.children;
13751 for(var i = 0, len = cs.length; i < len; i++){
13752 node.appendChild(this.createNode(cs[i]));
13754 if(typeof callback == "function"){
13757 }else if(this.dataUrl){
13758 this.requestData(node, callback);
13762 getParams: function(node){
13763 var buf = [], bp = this.baseParams;
13764 for(var key in bp){
13765 if(typeof bp[key] != "function"){
13766 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13769 var n = this.queryParam === false ? 'node' : this.queryParam;
13770 buf.push(n + "=", encodeURIComponent(node.id));
13771 return buf.join("");
13774 requestData : function(node, callback){
13775 if(this.fireEvent("beforeload", this, node, callback) !== false){
13776 this.transId = Roo.Ajax.request({
13777 method:this.requestMethod,
13778 url: this.dataUrl||this.url,
13779 success: this.handleResponse,
13780 failure: this.handleFailure,
13782 argument: {callback: callback, node: node},
13783 params: this.getParams(node)
13786 // if the load is cancelled, make sure we notify
13787 // the node that we are done
13788 if(typeof callback == "function"){
13794 isLoading : function(){
13795 return this.transId ? true : false;
13798 abort : function(){
13799 if(this.isLoading()){
13800 Roo.Ajax.abort(this.transId);
13805 createNode : function(attr)
13807 // apply baseAttrs, nice idea Corey!
13808 if(this.baseAttrs){
13809 Roo.applyIf(attr, this.baseAttrs);
13811 if(this.applyLoader !== false){
13812 attr.loader = this;
13814 // uiProvider = depreciated..
13816 if(typeof(attr.uiProvider) == 'string'){
13817 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
13818 /** eval:var:attr */ eval(attr.uiProvider);
13820 if(typeof(this.uiProviders['default']) != 'undefined') {
13821 attr.uiProvider = this.uiProviders['default'];
13824 this.fireEvent('create', this, attr);
13826 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13828 new Roo.tree.TreeNode(attr) :
13829 new Roo.tree.AsyncTreeNode(attr));
13832 processResponse : function(response, node, callback)
13834 var json = response.responseText;
13837 var o = Roo.decode(json);
13839 if (this.root === false && typeof(o.success) != undefined) {
13840 this.root = 'data'; // the default behaviour for list like data..
13843 if (this.root !== false && !o.success) {
13844 // it's a failure condition.
13845 var a = response.argument;
13846 this.fireEvent("loadexception", this, a.node, response);
13847 Roo.log("Load failed - should have a handler really");
13853 if (this.root !== false) {
13857 for(var i = 0, len = o.length; i < len; i++){
13858 var n = this.createNode(o[i]);
13860 node.appendChild(n);
13863 if(typeof callback == "function"){
13864 callback(this, node);
13867 this.handleFailure(response);
13871 handleResponse : function(response){
13872 this.transId = false;
13873 var a = response.argument;
13874 this.processResponse(response, a.node, a.callback);
13875 this.fireEvent("load", this, a.node, response);
13878 handleFailure : function(response)
13880 // should handle failure better..
13881 this.transId = false;
13882 var a = response.argument;
13883 this.fireEvent("loadexception", this, a.node, response);
13884 if(typeof a.callback == "function"){
13885 a.callback(this, a.node);
13890 * Ext JS Library 1.1.1
13891 * Copyright(c) 2006-2007, Ext JS, LLC.
13893 * Originally Released Under LGPL - original licence link has changed is not relivant.
13896 * <script type="text/javascript">
13900 * @class Roo.tree.TreeFilter
13901 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13902 * @param {TreePanel} tree
13903 * @param {Object} config (optional)
13905 Roo.tree.TreeFilter = function(tree, config){
13907 this.filtered = {};
13908 Roo.apply(this, config);
13911 Roo.tree.TreeFilter.prototype = {
13918 * Filter the data by a specific attribute.
13919 * @param {String/RegExp} value Either string that the attribute value
13920 * should start with or a RegExp to test against the attribute
13921 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13922 * @param {TreeNode} startNode (optional) The node to start the filter at.
13924 filter : function(value, attr, startNode){
13925 attr = attr || "text";
13927 if(typeof value == "string"){
13928 var vlen = value.length;
13929 // auto clear empty filter
13930 if(vlen == 0 && this.clearBlank){
13934 value = value.toLowerCase();
13936 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13938 }else if(value.exec){ // regex?
13940 return value.test(n.attributes[attr]);
13943 throw 'Illegal filter type, must be string or regex';
13945 this.filterBy(f, null, startNode);
13949 * Filter by a function. The passed function will be called with each
13950 * node in the tree (or from the startNode). If the function returns true, the node is kept
13951 * otherwise it is filtered. If a node is filtered, its children are also filtered.
13952 * @param {Function} fn The filter function
13953 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13955 filterBy : function(fn, scope, startNode){
13956 startNode = startNode || this.tree.root;
13957 if(this.autoClear){
13960 var af = this.filtered, rv = this.reverse;
13961 var f = function(n){
13962 if(n == startNode){
13968 var m = fn.call(scope || n, n);
13976 startNode.cascade(f);
13979 if(typeof id != "function"){
13981 if(n && n.parentNode){
13982 n.parentNode.removeChild(n);
13990 * Clears the current filter. Note: with the "remove" option
13991 * set a filter cannot be cleared.
13993 clear : function(){
13995 var af = this.filtered;
13997 if(typeof id != "function"){
14004 this.filtered = {};
14009 * Ext JS Library 1.1.1
14010 * Copyright(c) 2006-2007, Ext JS, LLC.
14012 * Originally Released Under LGPL - original licence link has changed is not relivant.
14015 * <script type="text/javascript">
14020 * @class Roo.tree.TreeSorter
14021 * Provides sorting of nodes in a TreePanel
14023 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
14024 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
14025 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
14026 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
14027 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
14028 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
14030 * @param {TreePanel} tree
14031 * @param {Object} config
14033 Roo.tree.TreeSorter = function(tree, config){
14034 Roo.apply(this, config);
14035 tree.on("beforechildrenrendered", this.doSort, this);
14036 tree.on("append", this.updateSort, this);
14037 tree.on("insert", this.updateSort, this);
14039 var dsc = this.dir && this.dir.toLowerCase() == "desc";
14040 var p = this.property || "text";
14041 var sortType = this.sortType;
14042 var fs = this.folderSort;
14043 var cs = this.caseSensitive === true;
14044 var leafAttr = this.leafAttr || 'leaf';
14046 this.sortFn = function(n1, n2){
14048 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
14051 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
14055 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
14056 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
14058 return dsc ? +1 : -1;
14060 return dsc ? -1 : +1;
14067 Roo.tree.TreeSorter.prototype = {
14068 doSort : function(node){
14069 node.sort(this.sortFn);
14072 compareNodes : function(n1, n2){
14073 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14076 updateSort : function(tree, node){
14077 if(node.childrenRendered){
14078 this.doSort.defer(1, this, [node]);
14083 * Ext JS Library 1.1.1
14084 * Copyright(c) 2006-2007, Ext JS, LLC.
14086 * Originally Released Under LGPL - original licence link has changed is not relivant.
14089 * <script type="text/javascript">
14092 if(Roo.dd.DropZone){
14094 Roo.tree.TreeDropZone = function(tree, config){
14095 this.allowParentInsert = false;
14096 this.allowContainerDrop = false;
14097 this.appendOnly = false;
14098 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14100 this.lastInsertClass = "x-tree-no-status";
14101 this.dragOverData = {};
14104 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14105 ddGroup : "TreeDD",
14108 expandDelay : 1000,
14110 expandNode : function(node){
14111 if(node.hasChildNodes() && !node.isExpanded()){
14112 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14116 queueExpand : function(node){
14117 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14120 cancelExpand : function(){
14121 if(this.expandProcId){
14122 clearTimeout(this.expandProcId);
14123 this.expandProcId = false;
14127 isValidDropPoint : function(n, pt, dd, e, data){
14128 if(!n || !data){ return false; }
14129 var targetNode = n.node;
14130 var dropNode = data.node;
14131 // default drop rules
14132 if(!(targetNode && targetNode.isTarget && pt)){
14135 if(pt == "append" && targetNode.allowChildren === false){
14138 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14141 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14144 // reuse the object
14145 var overEvent = this.dragOverData;
14146 overEvent.tree = this.tree;
14147 overEvent.target = targetNode;
14148 overEvent.data = data;
14149 overEvent.point = pt;
14150 overEvent.source = dd;
14151 overEvent.rawEvent = e;
14152 overEvent.dropNode = dropNode;
14153 overEvent.cancel = false;
14154 var result = this.tree.fireEvent("nodedragover", overEvent);
14155 return overEvent.cancel === false && result !== false;
14158 getDropPoint : function(e, n, dd)
14162 return tn.allowChildren !== false ? "append" : false; // always append for root
14164 var dragEl = n.ddel;
14165 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14166 var y = Roo.lib.Event.getPageY(e);
14167 //var noAppend = tn.allowChildren === false || tn.isLeaf();
14169 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14170 var noAppend = tn.allowChildren === false;
14171 if(this.appendOnly || tn.parentNode.allowChildren === false){
14172 return noAppend ? false : "append";
14174 var noBelow = false;
14175 if(!this.allowParentInsert){
14176 noBelow = tn.hasChildNodes() && tn.isExpanded();
14178 var q = (b - t) / (noAppend ? 2 : 3);
14179 if(y >= t && y < (t + q)){
14181 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14188 onNodeEnter : function(n, dd, e, data)
14190 this.cancelExpand();
14193 onNodeOver : function(n, dd, e, data)
14196 var pt = this.getDropPoint(e, n, dd);
14199 // auto node expand check
14200 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14201 this.queueExpand(node);
14202 }else if(pt != "append"){
14203 this.cancelExpand();
14206 // set the insert point style on the target node
14207 var returnCls = this.dropNotAllowed;
14208 if(this.isValidDropPoint(n, pt, dd, e, data)){
14213 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14214 cls = "x-tree-drag-insert-above";
14215 }else if(pt == "below"){
14216 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14217 cls = "x-tree-drag-insert-below";
14219 returnCls = "x-tree-drop-ok-append";
14220 cls = "x-tree-drag-append";
14222 if(this.lastInsertClass != cls){
14223 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14224 this.lastInsertClass = cls;
14231 onNodeOut : function(n, dd, e, data){
14233 this.cancelExpand();
14234 this.removeDropIndicators(n);
14237 onNodeDrop : function(n, dd, e, data){
14238 var point = this.getDropPoint(e, n, dd);
14239 var targetNode = n.node;
14240 targetNode.ui.startDrop();
14241 if(!this.isValidDropPoint(n, point, dd, e, data)){
14242 targetNode.ui.endDrop();
14245 // first try to find the drop node
14246 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14249 target: targetNode,
14254 dropNode: dropNode,
14257 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14258 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14259 targetNode.ui.endDrop();
14262 // allow target changing
14263 targetNode = dropEvent.target;
14264 if(point == "append" && !targetNode.isExpanded()){
14265 targetNode.expand(false, null, function(){
14266 this.completeDrop(dropEvent);
14267 }.createDelegate(this));
14269 this.completeDrop(dropEvent);
14274 completeDrop : function(de){
14275 var ns = de.dropNode, p = de.point, t = de.target;
14276 if(!(ns instanceof Array)){
14280 for(var i = 0, len = ns.length; i < len; i++){
14283 t.parentNode.insertBefore(n, t);
14284 }else if(p == "below"){
14285 t.parentNode.insertBefore(n, t.nextSibling);
14291 if(this.tree.hlDrop){
14295 this.tree.fireEvent("nodedrop", de);
14298 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14299 if(this.tree.hlDrop){
14300 dropNode.ui.focus();
14301 dropNode.ui.highlight();
14303 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14306 getTree : function(){
14310 removeDropIndicators : function(n){
14313 Roo.fly(el).removeClass([
14314 "x-tree-drag-insert-above",
14315 "x-tree-drag-insert-below",
14316 "x-tree-drag-append"]);
14317 this.lastInsertClass = "_noclass";
14321 beforeDragDrop : function(target, e, id){
14322 this.cancelExpand();
14326 afterRepair : function(data){
14327 if(data && Roo.enableFx){
14328 data.node.ui.highlight();
14338 * Ext JS Library 1.1.1
14339 * Copyright(c) 2006-2007, Ext JS, LLC.
14341 * Originally Released Under LGPL - original licence link has changed is not relivant.
14344 * <script type="text/javascript">
14348 if(Roo.dd.DragZone){
14349 Roo.tree.TreeDragZone = function(tree, config){
14350 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14354 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14355 ddGroup : "TreeDD",
14357 onBeforeDrag : function(data, e){
14359 return n && n.draggable && !n.disabled;
14363 onInitDrag : function(e){
14364 var data = this.dragData;
14365 this.tree.getSelectionModel().select(data.node);
14366 this.proxy.update("");
14367 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14368 this.tree.fireEvent("startdrag", this.tree, data.node, e);
14371 getRepairXY : function(e, data){
14372 return data.node.ui.getDDRepairXY();
14375 onEndDrag : function(data, e){
14376 this.tree.fireEvent("enddrag", this.tree, data.node, e);
14381 onValidDrop : function(dd, e, id){
14382 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14386 beforeInvalidDrop : function(e, id){
14387 // this scrolls the original position back into view
14388 var sm = this.tree.getSelectionModel();
14389 sm.clearSelections();
14390 sm.select(this.dragData.node);
14395 * Ext JS Library 1.1.1
14396 * Copyright(c) 2006-2007, Ext JS, LLC.
14398 * Originally Released Under LGPL - original licence link has changed is not relivant.
14401 * <script type="text/javascript">
14404 * @class Roo.tree.TreeEditor
14405 * @extends Roo.Editor
14406 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
14407 * as the editor field.
14409 * @param {Object} config (used to be the tree panel.)
14410 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14412 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14413 * @cfg {Roo.form.TextField|Object} field The field configuration
14417 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14420 if (oldconfig) { // old style..
14421 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14424 tree = config.tree;
14425 config.field = config.field || {};
14426 config.field.xtype = 'TextField';
14427 field = Roo.factory(config.field, Roo.form);
14429 config = config || {};
14434 * @event beforenodeedit
14435 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
14436 * false from the handler of this event.
14437 * @param {Editor} this
14438 * @param {Roo.tree.Node} node
14440 "beforenodeedit" : true
14444 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14448 tree.on('beforeclick', this.beforeNodeClick, this);
14449 tree.getTreeEl().on('mousedown', this.hide, this);
14450 this.on('complete', this.updateNode, this);
14451 this.on('beforestartedit', this.fitToTree, this);
14452 this.on('startedit', this.bindScroll, this, {delay:10});
14453 this.on('specialkey', this.onSpecialKey, this);
14456 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14458 * @cfg {String} alignment
14459 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14465 * @cfg {Boolean} hideEl
14466 * True to hide the bound element while the editor is displayed (defaults to false)
14470 * @cfg {String} cls
14471 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14473 cls: "x-small-editor x-tree-editor",
14475 * @cfg {Boolean} shim
14476 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14482 * @cfg {Number} maxWidth
14483 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
14484 * the containing tree element's size, it will be automatically limited for you to the container width, taking
14485 * scroll and client offsets into account prior to each edit.
14492 fitToTree : function(ed, el){
14493 var td = this.tree.getTreeEl().dom, nd = el.dom;
14494 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
14495 td.scrollLeft = nd.offsetLeft;
14499 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14500 this.setSize(w, '');
14502 return this.fireEvent('beforenodeedit', this, this.editNode);
14507 triggerEdit : function(node){
14508 this.completeEdit();
14509 this.editNode = node;
14510 this.startEdit(node.ui.textNode, node.text);
14514 bindScroll : function(){
14515 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14519 beforeNodeClick : function(node, e){
14520 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14521 this.lastClick = new Date();
14522 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14524 this.triggerEdit(node);
14531 updateNode : function(ed, value){
14532 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14533 this.editNode.setText(value);
14537 onHide : function(){
14538 Roo.tree.TreeEditor.superclass.onHide.call(this);
14540 this.editNode.ui.focus();
14545 onSpecialKey : function(field, e){
14546 var k = e.getKey();
14550 }else if(k == e.ENTER && !e.hasModifier()){
14552 this.completeEdit();
14555 });//<Script type="text/javascript">
14558 * Ext JS Library 1.1.1
14559 * Copyright(c) 2006-2007, Ext JS, LLC.
14561 * Originally Released Under LGPL - original licence link has changed is not relivant.
14564 * <script type="text/javascript">
14568 * Not documented??? - probably should be...
14571 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14572 //focus: Roo.emptyFn, // prevent odd scrolling behavior
14574 renderElements : function(n, a, targetNode, bulkRender){
14575 //consel.log("renderElements?");
14576 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14578 var t = n.getOwnerTree();
14579 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14581 var cols = t.columns;
14582 var bw = t.borderWidth;
14584 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14585 var cb = typeof a.checked == "boolean";
14586 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14587 var colcls = 'x-t-' + tid + '-c0';
14589 '<li class="x-tree-node">',
14592 '<div class="x-tree-node-el ', a.cls,'">',
14594 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14597 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14598 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
14599 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14600 (a.icon ? ' x-tree-node-inline-icon' : ''),
14601 (a.iconCls ? ' '+a.iconCls : ''),
14602 '" unselectable="on" />',
14603 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
14604 (a.checked ? 'checked="checked" />' : ' />')) : ''),
14606 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14607 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14608 '<span unselectable="on" qtip="' + tx + '">',
14612 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14613 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14615 for(var i = 1, len = cols.length; i < len; i++){
14617 colcls = 'x-t-' + tid + '-c' +i;
14618 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14619 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14620 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14626 '<div class="x-clear"></div></div>',
14627 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14630 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14631 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14632 n.nextSibling.ui.getEl(), buf.join(""));
14634 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14636 var el = this.wrap.firstChild;
14638 this.elNode = el.firstChild;
14639 this.ranchor = el.childNodes[1];
14640 this.ctNode = this.wrap.childNodes[1];
14641 var cs = el.firstChild.childNodes;
14642 this.indentNode = cs[0];
14643 this.ecNode = cs[1];
14644 this.iconNode = cs[2];
14647 this.checkbox = cs[3];
14650 this.anchor = cs[index];
14652 this.textNode = cs[index].firstChild;
14654 //el.on("click", this.onClick, this);
14655 //el.on("dblclick", this.onDblClick, this);
14658 // console.log(this);
14660 initEvents : function(){
14661 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14664 var a = this.ranchor;
14666 var el = Roo.get(a);
14668 if(Roo.isOpera){ // opera render bug ignores the CSS
14669 el.setStyle("text-decoration", "none");
14672 el.on("click", this.onClick, this);
14673 el.on("dblclick", this.onDblClick, this);
14674 el.on("contextmenu", this.onContextMenu, this);
14678 /*onSelectedChange : function(state){
14681 this.addClass("x-tree-selected");
14684 this.removeClass("x-tree-selected");
14687 addClass : function(cls){
14689 Roo.fly(this.elRow).addClass(cls);
14695 removeClass : function(cls){
14697 Roo.fly(this.elRow).removeClass(cls);
14703 });//<Script type="text/javascript">
14707 * Ext JS Library 1.1.1
14708 * Copyright(c) 2006-2007, Ext JS, LLC.
14710 * Originally Released Under LGPL - original licence link has changed is not relivant.
14713 * <script type="text/javascript">
14718 * @class Roo.tree.ColumnTree
14719 * @extends Roo.data.TreePanel
14720 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
14721 * @cfg {int} borderWidth compined right/left border allowance
14723 * @param {String/HTMLElement/Element} el The container element
14724 * @param {Object} config
14726 Roo.tree.ColumnTree = function(el, config)
14728 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14732 * Fire this event on a container when it resizes
14733 * @param {int} w Width
14734 * @param {int} h Height
14738 this.on('resize', this.onResize, this);
14741 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14745 borderWidth: Roo.isBorderBox ? 0 : 2,
14748 render : function(){
14749 // add the header.....
14751 Roo.tree.ColumnTree.superclass.render.apply(this);
14753 this.el.addClass('x-column-tree');
14755 this.headers = this.el.createChild(
14756 {cls:'x-tree-headers'},this.innerCt.dom);
14758 var cols = this.columns, c;
14759 var totalWidth = 0;
14761 var len = cols.length;
14762 for(var i = 0; i < len; i++){
14764 totalWidth += c.width;
14765 this.headEls.push(this.headers.createChild({
14766 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14768 cls:'x-tree-hd-text',
14771 style:'width:'+(c.width-this.borderWidth)+'px;'
14774 this.headers.createChild({cls:'x-clear'});
14775 // prevent floats from wrapping when clipped
14776 this.headers.setWidth(totalWidth);
14777 //this.innerCt.setWidth(totalWidth);
14778 this.innerCt.setStyle({ overflow: 'auto' });
14779 this.onResize(this.width, this.height);
14783 onResize : function(w,h)
14788 this.innerCt.setWidth(this.width);
14789 this.innerCt.setHeight(this.height-20);
14792 var cols = this.columns, c;
14793 var totalWidth = 0;
14795 var len = cols.length;
14796 for(var i = 0; i < len; i++){
14798 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14799 // it's the expander..
14800 expEl = this.headEls[i];
14803 totalWidth += c.width;
14807 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
14809 this.headers.setWidth(w-20);
14818 * Ext JS Library 1.1.1
14819 * Copyright(c) 2006-2007, Ext JS, LLC.
14821 * Originally Released Under LGPL - original licence link has changed is not relivant.
14824 * <script type="text/javascript">
14828 * @class Roo.menu.Menu
14829 * @extends Roo.util.Observable
14830 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
14831 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14833 * Creates a new Menu
14834 * @param {Object} config Configuration options
14836 Roo.menu.Menu = function(config){
14838 Roo.menu.Menu.superclass.constructor.call(this, config);
14840 this.id = this.id || Roo.id();
14843 * @event beforeshow
14844 * Fires before this menu is displayed
14845 * @param {Roo.menu.Menu} this
14849 * @event beforehide
14850 * Fires before this menu is hidden
14851 * @param {Roo.menu.Menu} this
14856 * Fires after this menu is displayed
14857 * @param {Roo.menu.Menu} this
14862 * Fires after this menu is hidden
14863 * @param {Roo.menu.Menu} this
14868 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14869 * @param {Roo.menu.Menu} this
14870 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14871 * @param {Roo.EventObject} e
14876 * Fires when the mouse is hovering over this menu
14877 * @param {Roo.menu.Menu} this
14878 * @param {Roo.EventObject} e
14879 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14884 * Fires when the mouse exits this menu
14885 * @param {Roo.menu.Menu} this
14886 * @param {Roo.EventObject} e
14887 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14892 * Fires when a menu item contained in this menu is clicked
14893 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14894 * @param {Roo.EventObject} e
14898 if (this.registerMenu) {
14899 Roo.menu.MenuMgr.register(this);
14902 var mis = this.items;
14903 this.items = new Roo.util.MixedCollection();
14905 this.add.apply(this, mis);
14909 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14911 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14915 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14916 * for bottom-right shadow (defaults to "sides")
14920 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14921 * this menu (defaults to "tl-tr?")
14923 subMenuAlign : "tl-tr?",
14925 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14926 * relative to its element of origin (defaults to "tl-bl?")
14928 defaultAlign : "tl-bl?",
14930 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14932 allowOtherMenus : false,
14934 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14936 registerMenu : true,
14941 render : function(){
14945 var el = this.el = new Roo.Layer({
14947 shadow:this.shadow,
14949 parentEl: this.parentEl || document.body,
14953 this.keyNav = new Roo.menu.MenuNav(this);
14956 el.addClass("x-menu-plain");
14959 el.addClass(this.cls);
14961 // generic focus element
14962 this.focusEl = el.createChild({
14963 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14965 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14966 //disabling touch- as it's causing issues ..
14967 //ul.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
14968 ul.on('click' , this.onClick, this);
14971 ul.on("mouseover", this.onMouseOver, this);
14972 ul.on("mouseout", this.onMouseOut, this);
14973 this.items.each(function(item){
14978 var li = document.createElement("li");
14979 li.className = "x-menu-list-item";
14980 ul.dom.appendChild(li);
14981 item.render(li, this);
14988 autoWidth : function(){
14989 var el = this.el, ul = this.ul;
14993 var w = this.width;
14996 }else if(Roo.isIE){
14997 el.setWidth(this.minWidth);
14998 var t = el.dom.offsetWidth; // force recalc
14999 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
15004 delayAutoWidth : function(){
15007 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
15009 this.awTask.delay(20);
15014 findTargetItem : function(e){
15015 var t = e.getTarget(".x-menu-list-item", this.ul, true);
15016 if(t && t.menuItemId){
15017 return this.items.get(t.menuItemId);
15022 onClick : function(e){
15023 Roo.log("menu.onClick");
15024 var t = this.findTargetItem(e);
15029 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
15030 if(t == this.activeItem && t.shouldDeactivate(e)){
15031 this.activeItem.deactivate();
15032 delete this.activeItem;
15036 this.setActiveItem(t, true);
15044 this.fireEvent("click", this, t, e);
15048 setActiveItem : function(item, autoExpand){
15049 if(item != this.activeItem){
15050 if(this.activeItem){
15051 this.activeItem.deactivate();
15053 this.activeItem = item;
15054 item.activate(autoExpand);
15055 }else if(autoExpand){
15061 tryActivate : function(start, step){
15062 var items = this.items;
15063 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
15064 var item = items.get(i);
15065 if(!item.disabled && item.canActivate){
15066 this.setActiveItem(item, false);
15074 onMouseOver : function(e){
15076 if(t = this.findTargetItem(e)){
15077 if(t.canActivate && !t.disabled){
15078 this.setActiveItem(t, true);
15081 this.fireEvent("mouseover", this, e, t);
15085 onMouseOut : function(e){
15087 if(t = this.findTargetItem(e)){
15088 if(t == this.activeItem && t.shouldDeactivate(e)){
15089 this.activeItem.deactivate();
15090 delete this.activeItem;
15093 this.fireEvent("mouseout", this, e, t);
15097 * Read-only. Returns true if the menu is currently displayed, else false.
15100 isVisible : function(){
15101 return this.el && !this.hidden;
15105 * Displays this menu relative to another element
15106 * @param {String/HTMLElement/Roo.Element} element The element to align to
15107 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15108 * the element (defaults to this.defaultAlign)
15109 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15111 show : function(el, pos, parentMenu){
15112 this.parentMenu = parentMenu;
15116 this.fireEvent("beforeshow", this);
15117 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15121 * Displays this menu at a specific xy position
15122 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15123 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15125 showAt : function(xy, parentMenu, /* private: */_e){
15126 this.parentMenu = parentMenu;
15131 this.fireEvent("beforeshow", this);
15132 xy = this.el.adjustForConstraints(xy);
15136 this.hidden = false;
15138 this.fireEvent("show", this);
15141 focus : function(){
15143 this.doFocus.defer(50, this);
15147 doFocus : function(){
15149 this.focusEl.focus();
15154 * Hides this menu and optionally all parent menus
15155 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15157 hide : function(deep){
15158 if(this.el && this.isVisible()){
15159 this.fireEvent("beforehide", this);
15160 if(this.activeItem){
15161 this.activeItem.deactivate();
15162 this.activeItem = null;
15165 this.hidden = true;
15166 this.fireEvent("hide", this);
15168 if(deep === true && this.parentMenu){
15169 this.parentMenu.hide(true);
15174 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15175 * Any of the following are valid:
15177 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15178 * <li>An HTMLElement object which will be converted to a menu item</li>
15179 * <li>A menu item config object that will be created as a new menu item</li>
15180 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15181 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15186 var menu = new Roo.menu.Menu();
15188 // Create a menu item to add by reference
15189 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15191 // Add a bunch of items at once using different methods.
15192 // Only the last item added will be returned.
15193 var item = menu.add(
15194 menuItem, // add existing item by ref
15195 'Dynamic Item', // new TextItem
15196 '-', // new separator
15197 { text: 'Config Item' } // new item by config
15200 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15201 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15204 var a = arguments, l = a.length, item;
15205 for(var i = 0; i < l; i++){
15207 if ((typeof(el) == "object") && el.xtype && el.xns) {
15208 el = Roo.factory(el, Roo.menu);
15211 if(el.render){ // some kind of Item
15212 item = this.addItem(el);
15213 }else if(typeof el == "string"){ // string
15214 if(el == "separator" || el == "-"){
15215 item = this.addSeparator();
15217 item = this.addText(el);
15219 }else if(el.tagName || el.el){ // element
15220 item = this.addElement(el);
15221 }else if(typeof el == "object"){ // must be menu item config?
15222 item = this.addMenuItem(el);
15229 * Returns this menu's underlying {@link Roo.Element} object
15230 * @return {Roo.Element} The element
15232 getEl : function(){
15240 * Adds a separator bar to the menu
15241 * @return {Roo.menu.Item} The menu item that was added
15243 addSeparator : function(){
15244 return this.addItem(new Roo.menu.Separator());
15248 * Adds an {@link Roo.Element} object to the menu
15249 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15250 * @return {Roo.menu.Item} The menu item that was added
15252 addElement : function(el){
15253 return this.addItem(new Roo.menu.BaseItem(el));
15257 * Adds an existing object based on {@link Roo.menu.Item} to the menu
15258 * @param {Roo.menu.Item} item The menu item to add
15259 * @return {Roo.menu.Item} The menu item that was added
15261 addItem : function(item){
15262 this.items.add(item);
15264 var li = document.createElement("li");
15265 li.className = "x-menu-list-item";
15266 this.ul.dom.appendChild(li);
15267 item.render(li, this);
15268 this.delayAutoWidth();
15274 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15275 * @param {Object} config A MenuItem config object
15276 * @return {Roo.menu.Item} The menu item that was added
15278 addMenuItem : function(config){
15279 if(!(config instanceof Roo.menu.Item)){
15280 if(typeof config.checked == "boolean"){ // must be check menu item config?
15281 config = new Roo.menu.CheckItem(config);
15283 config = new Roo.menu.Item(config);
15286 return this.addItem(config);
15290 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15291 * @param {String} text The text to display in the menu item
15292 * @return {Roo.menu.Item} The menu item that was added
15294 addText : function(text){
15295 return this.addItem(new Roo.menu.TextItem({ text : text }));
15299 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15300 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15301 * @param {Roo.menu.Item} item The menu item to add
15302 * @return {Roo.menu.Item} The menu item that was added
15304 insert : function(index, item){
15305 this.items.insert(index, item);
15307 var li = document.createElement("li");
15308 li.className = "x-menu-list-item";
15309 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15310 item.render(li, this);
15311 this.delayAutoWidth();
15317 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15318 * @param {Roo.menu.Item} item The menu item to remove
15320 remove : function(item){
15321 this.items.removeKey(item.id);
15326 * Removes and destroys all items in the menu
15328 removeAll : function(){
15330 while(f = this.items.first()){
15336 // MenuNav is a private utility class used internally by the Menu
15337 Roo.menu.MenuNav = function(menu){
15338 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15339 this.scope = this.menu = menu;
15342 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15343 doRelay : function(e, h){
15344 var k = e.getKey();
15345 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15346 this.menu.tryActivate(0, 1);
15349 return h.call(this.scope || this, e, this.menu);
15352 up : function(e, m){
15353 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15354 m.tryActivate(m.items.length-1, -1);
15358 down : function(e, m){
15359 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15360 m.tryActivate(0, 1);
15364 right : function(e, m){
15366 m.activeItem.expandMenu(true);
15370 left : function(e, m){
15372 if(m.parentMenu && m.parentMenu.activeItem){
15373 m.parentMenu.activeItem.activate();
15377 enter : function(e, m){
15379 e.stopPropagation();
15380 m.activeItem.onClick(e);
15381 m.fireEvent("click", this, m.activeItem);
15387 * Ext JS Library 1.1.1
15388 * Copyright(c) 2006-2007, Ext JS, LLC.
15390 * Originally Released Under LGPL - original licence link has changed is not relivant.
15393 * <script type="text/javascript">
15397 * @class Roo.menu.MenuMgr
15398 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15401 Roo.menu.MenuMgr = function(){
15402 var menus, active, groups = {}, attached = false, lastShow = new Date();
15404 // private - called when first menu is created
15407 active = new Roo.util.MixedCollection();
15408 Roo.get(document).addKeyListener(27, function(){
15409 if(active.length > 0){
15416 function hideAll(){
15417 if(active && active.length > 0){
15418 var c = active.clone();
15419 c.each(function(m){
15426 function onHide(m){
15428 if(active.length < 1){
15429 Roo.get(document).un("mousedown", onMouseDown);
15435 function onShow(m){
15436 var last = active.last();
15437 lastShow = new Date();
15440 Roo.get(document).on("mousedown", onMouseDown);
15444 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15445 m.parentMenu.activeChild = m;
15446 }else if(last && last.isVisible()){
15447 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15452 function onBeforeHide(m){
15454 m.activeChild.hide();
15456 if(m.autoHideTimer){
15457 clearTimeout(m.autoHideTimer);
15458 delete m.autoHideTimer;
15463 function onBeforeShow(m){
15464 var pm = m.parentMenu;
15465 if(!pm && !m.allowOtherMenus){
15467 }else if(pm && pm.activeChild && active != m){
15468 pm.activeChild.hide();
15473 function onMouseDown(e){
15474 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15480 function onBeforeCheck(mi, state){
15482 var g = groups[mi.group];
15483 for(var i = 0, l = g.length; i < l; i++){
15485 g[i].setChecked(false);
15494 * Hides all menus that are currently visible
15496 hideAll : function(){
15501 register : function(menu){
15505 menus[menu.id] = menu;
15506 menu.on("beforehide", onBeforeHide);
15507 menu.on("hide", onHide);
15508 menu.on("beforeshow", onBeforeShow);
15509 menu.on("show", onShow);
15510 var g = menu.group;
15511 if(g && menu.events["checkchange"]){
15515 groups[g].push(menu);
15516 menu.on("checkchange", onCheck);
15521 * Returns a {@link Roo.menu.Menu} object
15522 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15523 * be used to generate and return a new Menu instance.
15525 get : function(menu){
15526 if(typeof menu == "string"){ // menu id
15527 return menus[menu];
15528 }else if(menu.events){ // menu instance
15530 }else if(typeof menu.length == 'number'){ // array of menu items?
15531 return new Roo.menu.Menu({items:menu});
15532 }else{ // otherwise, must be a config
15533 return new Roo.menu.Menu(menu);
15538 unregister : function(menu){
15539 delete menus[menu.id];
15540 menu.un("beforehide", onBeforeHide);
15541 menu.un("hide", onHide);
15542 menu.un("beforeshow", onBeforeShow);
15543 menu.un("show", onShow);
15544 var g = menu.group;
15545 if(g && menu.events["checkchange"]){
15546 groups[g].remove(menu);
15547 menu.un("checkchange", onCheck);
15552 registerCheckable : function(menuItem){
15553 var g = menuItem.group;
15558 groups[g].push(menuItem);
15559 menuItem.on("beforecheckchange", onBeforeCheck);
15564 unregisterCheckable : function(menuItem){
15565 var g = menuItem.group;
15567 groups[g].remove(menuItem);
15568 menuItem.un("beforecheckchange", onBeforeCheck);
15574 * Ext JS Library 1.1.1
15575 * Copyright(c) 2006-2007, Ext JS, LLC.
15577 * Originally Released Under LGPL - original licence link has changed is not relivant.
15580 * <script type="text/javascript">
15585 * @class Roo.menu.BaseItem
15586 * @extends Roo.Component
15587 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
15588 * management and base configuration options shared by all menu components.
15590 * Creates a new BaseItem
15591 * @param {Object} config Configuration options
15593 Roo.menu.BaseItem = function(config){
15594 Roo.menu.BaseItem.superclass.constructor.call(this, config);
15599 * Fires when this item is clicked
15600 * @param {Roo.menu.BaseItem} this
15601 * @param {Roo.EventObject} e
15606 * Fires when this item is activated
15607 * @param {Roo.menu.BaseItem} this
15611 * @event deactivate
15612 * Fires when this item is deactivated
15613 * @param {Roo.menu.BaseItem} this
15619 this.on("click", this.handler, this.scope, true);
15623 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15625 * @cfg {Function} handler
15626 * A function that will handle the click event of this menu item (defaults to undefined)
15629 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15631 canActivate : false,
15634 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15639 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15641 activeClass : "x-menu-item-active",
15643 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15645 hideOnClick : true,
15647 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15652 ctype: "Roo.menu.BaseItem",
15655 actionMode : "container",
15658 render : function(container, parentMenu){
15659 this.parentMenu = parentMenu;
15660 Roo.menu.BaseItem.superclass.render.call(this, container);
15661 this.container.menuItemId = this.id;
15665 onRender : function(container, position){
15666 this.el = Roo.get(this.el);
15667 container.dom.appendChild(this.el.dom);
15671 onClick : function(e){
15672 if(!this.disabled && this.fireEvent("click", this, e) !== false
15673 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15674 this.handleClick(e);
15681 activate : function(){
15685 var li = this.container;
15686 li.addClass(this.activeClass);
15687 this.region = li.getRegion().adjust(2, 2, -2, -2);
15688 this.fireEvent("activate", this);
15693 deactivate : function(){
15694 this.container.removeClass(this.activeClass);
15695 this.fireEvent("deactivate", this);
15699 shouldDeactivate : function(e){
15700 return !this.region || !this.region.contains(e.getPoint());
15704 handleClick : function(e){
15705 if(this.hideOnClick){
15706 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15711 expandMenu : function(autoActivate){
15716 hideMenu : function(){
15721 * Ext JS Library 1.1.1
15722 * Copyright(c) 2006-2007, Ext JS, LLC.
15724 * Originally Released Under LGPL - original licence link has changed is not relivant.
15727 * <script type="text/javascript">
15731 * @class Roo.menu.Adapter
15732 * @extends Roo.menu.BaseItem
15733 * 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.
15734 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15736 * Creates a new Adapter
15737 * @param {Object} config Configuration options
15739 Roo.menu.Adapter = function(component, config){
15740 Roo.menu.Adapter.superclass.constructor.call(this, config);
15741 this.component = component;
15743 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15745 canActivate : true,
15748 onRender : function(container, position){
15749 this.component.render(container);
15750 this.el = this.component.getEl();
15754 activate : function(){
15758 this.component.focus();
15759 this.fireEvent("activate", this);
15764 deactivate : function(){
15765 this.fireEvent("deactivate", this);
15769 disable : function(){
15770 this.component.disable();
15771 Roo.menu.Adapter.superclass.disable.call(this);
15775 enable : function(){
15776 this.component.enable();
15777 Roo.menu.Adapter.superclass.enable.call(this);
15781 * Ext JS Library 1.1.1
15782 * Copyright(c) 2006-2007, Ext JS, LLC.
15784 * Originally Released Under LGPL - original licence link has changed is not relivant.
15787 * <script type="text/javascript">
15791 * @class Roo.menu.TextItem
15792 * @extends Roo.menu.BaseItem
15793 * Adds a static text string to a menu, usually used as either a heading or group separator.
15794 * Note: old style constructor with text is still supported.
15797 * Creates a new TextItem
15798 * @param {Object} cfg Configuration
15800 Roo.menu.TextItem = function(cfg){
15801 if (typeof(cfg) == 'string') {
15804 Roo.apply(this,cfg);
15807 Roo.menu.TextItem.superclass.constructor.call(this);
15810 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15812 * @cfg {Boolean} text Text to show on item.
15817 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15819 hideOnClick : false,
15821 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15823 itemCls : "x-menu-text",
15826 onRender : function(){
15827 var s = document.createElement("span");
15828 s.className = this.itemCls;
15829 s.innerHTML = this.text;
15831 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15835 * Ext JS Library 1.1.1
15836 * Copyright(c) 2006-2007, Ext JS, LLC.
15838 * Originally Released Under LGPL - original licence link has changed is not relivant.
15841 * <script type="text/javascript">
15845 * @class Roo.menu.Separator
15846 * @extends Roo.menu.BaseItem
15847 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15848 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15850 * @param {Object} config Configuration options
15852 Roo.menu.Separator = function(config){
15853 Roo.menu.Separator.superclass.constructor.call(this, config);
15856 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15858 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15860 itemCls : "x-menu-sep",
15862 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15864 hideOnClick : false,
15867 onRender : function(li){
15868 var s = document.createElement("span");
15869 s.className = this.itemCls;
15870 s.innerHTML = " ";
15872 li.addClass("x-menu-sep-li");
15873 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15877 * Ext JS Library 1.1.1
15878 * Copyright(c) 2006-2007, Ext JS, LLC.
15880 * Originally Released Under LGPL - original licence link has changed is not relivant.
15883 * <script type="text/javascript">
15886 * @class Roo.menu.Item
15887 * @extends Roo.menu.BaseItem
15888 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15889 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15890 * activation and click handling.
15892 * Creates a new Item
15893 * @param {Object} config Configuration options
15895 Roo.menu.Item = function(config){
15896 Roo.menu.Item.superclass.constructor.call(this, config);
15898 this.menu = Roo.menu.MenuMgr.get(this.menu);
15901 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15904 * @cfg {String} text
15905 * The text to show on the menu item.
15909 * @cfg {String} HTML to render in menu
15910 * The text to show on the menu item (HTML version).
15914 * @cfg {String} icon
15915 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15919 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15921 itemCls : "x-menu-item",
15923 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15925 canActivate : true,
15927 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15930 // doc'd in BaseItem
15934 ctype: "Roo.menu.Item",
15937 onRender : function(container, position){
15938 var el = document.createElement("a");
15939 el.hideFocus = true;
15940 el.unselectable = "on";
15941 el.href = this.href || "#";
15942 if(this.hrefTarget){
15943 el.target = this.hrefTarget;
15945 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
15947 var html = this.html.length ? this.html : String.format('{0}',this.text);
15949 el.innerHTML = String.format(
15950 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15951 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15953 Roo.menu.Item.superclass.onRender.call(this, container, position);
15957 * Sets the text to display in this menu item
15958 * @param {String} text The text to display
15959 * @param {Boolean} isHTML true to indicate text is pure html.
15961 setText : function(text, isHTML){
15969 var html = this.html.length ? this.html : String.format('{0}',this.text);
15971 this.el.update(String.format(
15972 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15973 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15974 this.parentMenu.autoWidth();
15979 handleClick : function(e){
15980 if(!this.href){ // if no link defined, stop the event automatically
15983 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15987 activate : function(autoExpand){
15988 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15998 shouldDeactivate : function(e){
15999 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
16000 if(this.menu && this.menu.isVisible()){
16001 return !this.menu.getEl().getRegion().contains(e.getPoint());
16009 deactivate : function(){
16010 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
16015 expandMenu : function(autoActivate){
16016 if(!this.disabled && this.menu){
16017 clearTimeout(this.hideTimer);
16018 delete this.hideTimer;
16019 if(!this.menu.isVisible() && !this.showTimer){
16020 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
16021 }else if (this.menu.isVisible() && autoActivate){
16022 this.menu.tryActivate(0, 1);
16028 deferExpand : function(autoActivate){
16029 delete this.showTimer;
16030 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
16032 this.menu.tryActivate(0, 1);
16037 hideMenu : function(){
16038 clearTimeout(this.showTimer);
16039 delete this.showTimer;
16040 if(!this.hideTimer && this.menu && this.menu.isVisible()){
16041 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
16046 deferHide : function(){
16047 delete this.hideTimer;
16052 * Ext JS Library 1.1.1
16053 * Copyright(c) 2006-2007, Ext JS, LLC.
16055 * Originally Released Under LGPL - original licence link has changed is not relivant.
16058 * <script type="text/javascript">
16062 * @class Roo.menu.CheckItem
16063 * @extends Roo.menu.Item
16064 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
16066 * Creates a new CheckItem
16067 * @param {Object} config Configuration options
16069 Roo.menu.CheckItem = function(config){
16070 Roo.menu.CheckItem.superclass.constructor.call(this, config);
16073 * @event beforecheckchange
16074 * Fires before the checked value is set, providing an opportunity to cancel if needed
16075 * @param {Roo.menu.CheckItem} this
16076 * @param {Boolean} checked The new checked value that will be set
16078 "beforecheckchange" : true,
16080 * @event checkchange
16081 * Fires after the checked value has been set
16082 * @param {Roo.menu.CheckItem} this
16083 * @param {Boolean} checked The checked value that was set
16085 "checkchange" : true
16087 if(this.checkHandler){
16088 this.on('checkchange', this.checkHandler, this.scope);
16091 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16093 * @cfg {String} group
16094 * All check items with the same group name will automatically be grouped into a single-select
16095 * radio button group (defaults to '')
16098 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16100 itemCls : "x-menu-item x-menu-check-item",
16102 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16104 groupClass : "x-menu-group-item",
16107 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
16108 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16109 * initialized with checked = true will be rendered as checked.
16114 ctype: "Roo.menu.CheckItem",
16117 onRender : function(c){
16118 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16120 this.el.addClass(this.groupClass);
16122 Roo.menu.MenuMgr.registerCheckable(this);
16124 this.checked = false;
16125 this.setChecked(true, true);
16130 destroy : function(){
16132 Roo.menu.MenuMgr.unregisterCheckable(this);
16134 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16138 * Set the checked state of this item
16139 * @param {Boolean} checked The new checked value
16140 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16142 setChecked : function(state, suppressEvent){
16143 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16144 if(this.container){
16145 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16147 this.checked = state;
16148 if(suppressEvent !== true){
16149 this.fireEvent("checkchange", this, state);
16155 handleClick : function(e){
16156 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16157 this.setChecked(!this.checked);
16159 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16163 * Ext JS Library 1.1.1
16164 * Copyright(c) 2006-2007, Ext JS, LLC.
16166 * Originally Released Under LGPL - original licence link has changed is not relivant.
16169 * <script type="text/javascript">
16173 * @class Roo.menu.DateItem
16174 * @extends Roo.menu.Adapter
16175 * A menu item that wraps the {@link Roo.DatPicker} component.
16177 * Creates a new DateItem
16178 * @param {Object} config Configuration options
16180 Roo.menu.DateItem = function(config){
16181 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16182 /** The Roo.DatePicker object @type Roo.DatePicker */
16183 this.picker = this.component;
16184 this.addEvents({select: true});
16186 this.picker.on("render", function(picker){
16187 picker.getEl().swallowEvent("click");
16188 picker.container.addClass("x-menu-date-item");
16191 this.picker.on("select", this.onSelect, this);
16194 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16196 onSelect : function(picker, date){
16197 this.fireEvent("select", this, date, picker);
16198 Roo.menu.DateItem.superclass.handleClick.call(this);
16202 * Ext JS Library 1.1.1
16203 * Copyright(c) 2006-2007, Ext JS, LLC.
16205 * Originally Released Under LGPL - original licence link has changed is not relivant.
16208 * <script type="text/javascript">
16212 * @class Roo.menu.ColorItem
16213 * @extends Roo.menu.Adapter
16214 * A menu item that wraps the {@link Roo.ColorPalette} component.
16216 * Creates a new ColorItem
16217 * @param {Object} config Configuration options
16219 Roo.menu.ColorItem = function(config){
16220 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16221 /** The Roo.ColorPalette object @type Roo.ColorPalette */
16222 this.palette = this.component;
16223 this.relayEvents(this.palette, ["select"]);
16224 if(this.selectHandler){
16225 this.on('select', this.selectHandler, this.scope);
16228 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16230 * Ext JS Library 1.1.1
16231 * Copyright(c) 2006-2007, Ext JS, LLC.
16233 * Originally Released Under LGPL - original licence link has changed is not relivant.
16236 * <script type="text/javascript">
16241 * @class Roo.menu.DateMenu
16242 * @extends Roo.menu.Menu
16243 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16245 * Creates a new DateMenu
16246 * @param {Object} config Configuration options
16248 Roo.menu.DateMenu = function(config){
16249 Roo.menu.DateMenu.superclass.constructor.call(this, config);
16251 var di = new Roo.menu.DateItem(config);
16254 * The {@link Roo.DatePicker} instance for this DateMenu
16257 this.picker = di.picker;
16260 * @param {DatePicker} picker
16261 * @param {Date} date
16263 this.relayEvents(di, ["select"]);
16264 this.on('beforeshow', function(){
16266 this.picker.hideMonthPicker(false);
16270 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16274 * Ext JS Library 1.1.1
16275 * Copyright(c) 2006-2007, Ext JS, LLC.
16277 * Originally Released Under LGPL - original licence link has changed is not relivant.
16280 * <script type="text/javascript">
16285 * @class Roo.menu.ColorMenu
16286 * @extends Roo.menu.Menu
16287 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16289 * Creates a new ColorMenu
16290 * @param {Object} config Configuration options
16292 Roo.menu.ColorMenu = function(config){
16293 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16295 var ci = new Roo.menu.ColorItem(config);
16298 * The {@link Roo.ColorPalette} instance for this ColorMenu
16299 * @type ColorPalette
16301 this.palette = ci.palette;
16304 * @param {ColorPalette} palette
16305 * @param {String} color
16307 this.relayEvents(ci, ["select"]);
16309 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16311 * Ext JS Library 1.1.1
16312 * Copyright(c) 2006-2007, Ext JS, LLC.
16314 * Originally Released Under LGPL - original licence link has changed is not relivant.
16317 * <script type="text/javascript">
16321 * @class Roo.form.TextItem
16322 * @extends Roo.BoxComponent
16323 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16325 * Creates a new TextItem
16326 * @param {Object} config Configuration options
16328 Roo.form.TextItem = function(config){
16329 Roo.form.TextItem.superclass.constructor.call(this, config);
16332 Roo.extend(Roo.form.TextItem, Roo.BoxComponent, {
16335 * @cfg {String} tag the tag for this item (default div)
16339 * @cfg {String} html the content for this item
16343 getAutoCreate : function()
16356 onRender : function(ct, position)
16358 Roo.form.TextItem.superclass.onRender.call(this, ct, position);
16361 var cfg = this.getAutoCreate();
16363 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16365 if (!cfg.name.length) {
16368 this.el = ct.createChild(cfg, position);
16374 * Ext JS Library 1.1.1
16375 * Copyright(c) 2006-2007, Ext JS, LLC.
16377 * Originally Released Under LGPL - original licence link has changed is not relivant.
16380 * <script type="text/javascript">
16384 * @class Roo.form.Field
16385 * @extends Roo.BoxComponent
16386 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16388 * Creates a new Field
16389 * @param {Object} config Configuration options
16391 Roo.form.Field = function(config){
16392 Roo.form.Field.superclass.constructor.call(this, config);
16395 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
16397 * @cfg {String} fieldLabel Label to use when rendering a form.
16400 * @cfg {String} qtip Mouse over tip
16404 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16406 invalidClass : "x-form-invalid",
16408 * @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")
16410 invalidText : "The value in this field is invalid",
16412 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16414 focusClass : "x-form-focus",
16416 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16417 automatic validation (defaults to "keyup").
16419 validationEvent : "keyup",
16421 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16423 validateOnBlur : true,
16425 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16427 validationDelay : 250,
16429 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16430 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16432 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16434 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16436 fieldClass : "x-form-field",
16438 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
16441 ----------- ----------------------------------------------------------------------
16442 qtip Display a quick tip when the user hovers over the field
16443 title Display a default browser title attribute popup
16444 under Add a block div beneath the field containing the error text
16445 side Add an error icon to the right of the field with a popup on hover
16446 [element id] Add the error text directly to the innerHTML of the specified element
16449 msgTarget : 'qtip',
16451 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16456 * @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.
16461 * @cfg {Boolean} disabled True to disable the field (defaults to false).
16466 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16468 inputType : undefined,
16471 * @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).
16473 tabIndex : undefined,
16476 isFormField : true,
16481 * @property {Roo.Element} fieldEl
16482 * Element Containing the rendered Field (with label etc.)
16485 * @cfg {Mixed} value A value to initialize this field with.
16490 * @cfg {String} name The field's HTML name attribute.
16493 * @cfg {String} cls A CSS class to apply to the field's underlying element.
16496 loadedValue : false,
16500 initComponent : function(){
16501 Roo.form.Field.superclass.initComponent.call(this);
16505 * Fires when this field receives input focus.
16506 * @param {Roo.form.Field} this
16511 * Fires when this field loses input focus.
16512 * @param {Roo.form.Field} this
16516 * @event specialkey
16517 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
16518 * {@link Roo.EventObject#getKey} to determine which key was pressed.
16519 * @param {Roo.form.Field} this
16520 * @param {Roo.EventObject} e The event object
16525 * Fires just before the field blurs if the field value has changed.
16526 * @param {Roo.form.Field} this
16527 * @param {Mixed} newValue The new value
16528 * @param {Mixed} oldValue The original value
16533 * Fires after the field has been marked as invalid.
16534 * @param {Roo.form.Field} this
16535 * @param {String} msg The validation message
16540 * Fires after the field has been validated with no errors.
16541 * @param {Roo.form.Field} this
16546 * Fires after the key up
16547 * @param {Roo.form.Field} this
16548 * @param {Roo.EventObject} e The event Object
16555 * Returns the name attribute of the field if available
16556 * @return {String} name The field name
16558 getName: function(){
16559 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16563 onRender : function(ct, position){
16564 Roo.form.Field.superclass.onRender.call(this, ct, position);
16566 var cfg = this.getAutoCreate();
16568 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16570 if (!cfg.name.length) {
16573 if(this.inputType){
16574 cfg.type = this.inputType;
16576 this.el = ct.createChild(cfg, position);
16578 var type = this.el.dom.type;
16580 if(type == 'password'){
16583 this.el.addClass('x-form-'+type);
16586 this.el.dom.readOnly = true;
16588 if(this.tabIndex !== undefined){
16589 this.el.dom.setAttribute('tabIndex', this.tabIndex);
16592 this.el.addClass([this.fieldClass, this.cls]);
16597 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16598 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16599 * @return {Roo.form.Field} this
16601 applyTo : function(target){
16602 this.allowDomMove = false;
16603 this.el = Roo.get(target);
16604 this.render(this.el.dom.parentNode);
16609 initValue : function(){
16610 if(this.value !== undefined){
16611 this.setValue(this.value);
16612 }else if(this.el.dom.value.length > 0){
16613 this.setValue(this.el.dom.value);
16618 * Returns true if this field has been changed since it was originally loaded and is not disabled.
16619 * DEPRICATED - it never worked well - use hasChanged/resetHasChanged.
16621 isDirty : function() {
16622 if(this.disabled) {
16625 return String(this.getValue()) !== String(this.originalValue);
16629 * stores the current value in loadedValue
16631 resetHasChanged : function()
16633 this.loadedValue = String(this.getValue());
16636 * checks the current value against the 'loaded' value.
16637 * Note - will return false if 'resetHasChanged' has not been called first.
16639 hasChanged : function()
16641 if(this.disabled || this.readOnly) {
16644 return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16650 afterRender : function(){
16651 Roo.form.Field.superclass.afterRender.call(this);
16656 fireKey : function(e){
16657 //Roo.log('field ' + e.getKey());
16658 if(e.isNavKeyPress()){
16659 this.fireEvent("specialkey", this, e);
16664 * Resets the current field value to the originally loaded value and clears any validation messages
16666 reset : function(){
16667 this.setValue(this.resetValue);
16668 this.originalValue = this.getValue();
16669 this.clearInvalid();
16673 initEvents : function(){
16674 // safari killled keypress - so keydown is now used..
16675 this.el.on("keydown" , this.fireKey, this);
16676 this.el.on("focus", this.onFocus, this);
16677 this.el.on("blur", this.onBlur, this);
16678 this.el.relayEvent('keyup', this);
16680 // reference to original value for reset
16681 this.originalValue = this.getValue();
16682 this.resetValue = this.getValue();
16686 onFocus : function(){
16687 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16688 this.el.addClass(this.focusClass);
16690 if(!this.hasFocus){
16691 this.hasFocus = true;
16692 this.startValue = this.getValue();
16693 this.fireEvent("focus", this);
16697 beforeBlur : Roo.emptyFn,
16700 onBlur : function(){
16702 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16703 this.el.removeClass(this.focusClass);
16705 this.hasFocus = false;
16706 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16709 var v = this.getValue();
16710 if(String(v) !== String(this.startValue)){
16711 this.fireEvent('change', this, v, this.startValue);
16713 this.fireEvent("blur", this);
16717 * Returns whether or not the field value is currently valid
16718 * @param {Boolean} preventMark True to disable marking the field invalid
16719 * @return {Boolean} True if the value is valid, else false
16721 isValid : function(preventMark){
16725 var restore = this.preventMark;
16726 this.preventMark = preventMark === true;
16727 var v = this.validateValue(this.processValue(this.getRawValue()));
16728 this.preventMark = restore;
16733 * Validates the field value
16734 * @return {Boolean} True if the value is valid, else false
16736 validate : function(){
16737 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16738 this.clearInvalid();
16744 processValue : function(value){
16749 // Subclasses should provide the validation implementation by overriding this
16750 validateValue : function(value){
16755 * Mark this field as invalid
16756 * @param {String} msg The validation message
16758 markInvalid : function(msg){
16759 if(!this.rendered || this.preventMark){ // not rendered
16763 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16765 obj.el.addClass(this.invalidClass);
16766 msg = msg || this.invalidText;
16767 switch(this.msgTarget){
16769 obj.el.dom.qtip = msg;
16770 obj.el.dom.qclass = 'x-form-invalid-tip';
16771 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16772 Roo.QuickTips.enable();
16776 this.el.dom.title = msg;
16780 var elp = this.el.findParent('.x-form-element', 5, true);
16781 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16782 this.errorEl.setWidth(elp.getWidth(true)-20);
16784 this.errorEl.update(msg);
16785 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16788 if(!this.errorIcon){
16789 var elp = this.el.findParent('.x-form-element', 5, true);
16790 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16792 this.alignErrorIcon();
16793 this.errorIcon.dom.qtip = msg;
16794 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16795 this.errorIcon.show();
16796 this.on('resize', this.alignErrorIcon, this);
16799 var t = Roo.getDom(this.msgTarget);
16801 t.style.display = this.msgDisplay;
16804 this.fireEvent('invalid', this, msg);
16808 alignErrorIcon : function(){
16809 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16813 * Clear any invalid styles/messages for this field
16815 clearInvalid : function(){
16816 if(!this.rendered || this.preventMark){ // not rendered
16819 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16821 obj.el.removeClass(this.invalidClass);
16822 switch(this.msgTarget){
16824 obj.el.dom.qtip = '';
16827 this.el.dom.title = '';
16831 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16835 if(this.errorIcon){
16836 this.errorIcon.dom.qtip = '';
16837 this.errorIcon.hide();
16838 this.un('resize', this.alignErrorIcon, this);
16842 var t = Roo.getDom(this.msgTarget);
16844 t.style.display = 'none';
16847 this.fireEvent('valid', this);
16851 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
16852 * @return {Mixed} value The field value
16854 getRawValue : function(){
16855 var v = this.el.getValue();
16861 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16862 * @return {Mixed} value The field value
16864 getValue : function(){
16865 var v = this.el.getValue();
16871 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
16872 * @param {Mixed} value The value to set
16874 setRawValue : function(v){
16875 return this.el.dom.value = (v === null || v === undefined ? '' : v);
16879 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
16880 * @param {Mixed} value The value to set
16882 setValue : function(v){
16885 this.el.dom.value = (v === null || v === undefined ? '' : v);
16890 adjustSize : function(w, h){
16891 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16892 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16896 adjustWidth : function(tag, w){
16897 tag = tag.toLowerCase();
16898 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16899 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16900 if(tag == 'input'){
16903 if(tag == 'textarea'){
16906 }else if(Roo.isOpera){
16907 if(tag == 'input'){
16910 if(tag == 'textarea'){
16920 // anything other than normal should be considered experimental
16921 Roo.form.Field.msgFx = {
16923 show: function(msgEl, f){
16924 msgEl.setDisplayed('block');
16927 hide : function(msgEl, f){
16928 msgEl.setDisplayed(false).update('');
16933 show: function(msgEl, f){
16934 msgEl.slideIn('t', {stopFx:true});
16937 hide : function(msgEl, f){
16938 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16943 show: function(msgEl, f){
16944 msgEl.fixDisplay();
16945 msgEl.alignTo(f.el, 'tl-tr');
16946 msgEl.slideIn('l', {stopFx:true});
16949 hide : function(msgEl, f){
16950 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16955 * Ext JS Library 1.1.1
16956 * Copyright(c) 2006-2007, Ext JS, LLC.
16958 * Originally Released Under LGPL - original licence link has changed is not relivant.
16961 * <script type="text/javascript">
16966 * @class Roo.form.TextField
16967 * @extends Roo.form.Field
16968 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
16969 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16971 * Creates a new TextField
16972 * @param {Object} config Configuration options
16974 Roo.form.TextField = function(config){
16975 Roo.form.TextField.superclass.constructor.call(this, config);
16979 * Fires when the autosize function is triggered. The field may or may not have actually changed size
16980 * according to the default logic, but this event provides a hook for the developer to apply additional
16981 * logic at runtime to resize the field if needed.
16982 * @param {Roo.form.Field} this This text field
16983 * @param {Number} width The new field width
16989 Roo.extend(Roo.form.TextField, Roo.form.Field, {
16991 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16995 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16999 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
17003 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
17007 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
17011 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
17013 disableKeyFilter : false,
17015 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
17019 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
17023 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
17025 maxLength : Number.MAX_VALUE,
17027 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
17029 minLengthText : "The minimum length for this field is {0}",
17031 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
17033 maxLengthText : "The maximum length for this field is {0}",
17035 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
17037 selectOnFocus : false,
17039 * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space
17041 allowLeadingSpace : false,
17043 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
17045 blankText : "This field is required",
17047 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
17048 * If available, this function will be called only after the basic validators all return true, and will be passed the
17049 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
17053 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
17054 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
17055 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
17059 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
17063 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
17069 initEvents : function()
17071 if (this.emptyText) {
17072 this.el.attr('placeholder', this.emptyText);
17075 Roo.form.TextField.superclass.initEvents.call(this);
17076 if(this.validationEvent == 'keyup'){
17077 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
17078 this.el.on('keyup', this.filterValidation, this);
17080 else if(this.validationEvent !== false){
17081 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
17084 if(this.selectOnFocus){
17085 this.on("focus", this.preFocus, this);
17087 if (!this.allowLeadingSpace) {
17088 this.on('blur', this.cleanLeadingSpace, this);
17091 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
17092 this.el.on("keypress", this.filterKeys, this);
17095 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
17096 this.el.on("click", this.autoSize, this);
17098 if(this.el.is('input[type=password]') && Roo.isSafari){
17099 this.el.on('keydown', this.SafariOnKeyDown, this);
17103 processValue : function(value){
17104 if(this.stripCharsRe){
17105 var newValue = value.replace(this.stripCharsRe, '');
17106 if(newValue !== value){
17107 this.setRawValue(newValue);
17114 filterValidation : function(e){
17115 if(!e.isNavKeyPress()){
17116 this.validationTask.delay(this.validationDelay);
17121 onKeyUp : function(e){
17122 if(!e.isNavKeyPress()){
17126 // private - clean the leading white space
17127 cleanLeadingSpace : function(e)
17129 if ( this.inputType == 'file') {
17133 this.setValue((this.getValue() + '').replace(/^\s+/,''));
17136 * Resets the current field value to the originally-loaded value and clears any validation messages.
17139 reset : function(){
17140 Roo.form.TextField.superclass.reset.call(this);
17144 preFocus : function(){
17146 if(this.selectOnFocus){
17147 this.el.dom.select();
17153 filterKeys : function(e){
17154 var k = e.getKey();
17155 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17158 var c = e.getCharCode(), cc = String.fromCharCode(c);
17159 if(Roo.isIE && (e.isSpecialKey() || !cc)){
17162 if(!this.maskRe.test(cc)){
17167 setValue : function(v){
17169 Roo.form.TextField.superclass.setValue.apply(this, arguments);
17175 * Validates a value according to the field's validation rules and marks the field as invalid
17176 * if the validation fails
17177 * @param {Mixed} value The value to validate
17178 * @return {Boolean} True if the value is valid, else false
17180 validateValue : function(value){
17181 if(value.length < 1) { // if it's blank
17182 if(this.allowBlank){
17183 this.clearInvalid();
17186 this.markInvalid(this.blankText);
17190 if(value.length < this.minLength){
17191 this.markInvalid(String.format(this.minLengthText, this.minLength));
17194 if(value.length > this.maxLength){
17195 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17199 var vt = Roo.form.VTypes;
17200 if(!vt[this.vtype](value, this)){
17201 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17205 if(typeof this.validator == "function"){
17206 var msg = this.validator(value);
17208 this.markInvalid(msg);
17212 if(this.regex && !this.regex.test(value)){
17213 this.markInvalid(this.regexText);
17220 * Selects text in this field
17221 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17222 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17224 selectText : function(start, end){
17225 var v = this.getRawValue();
17227 start = start === undefined ? 0 : start;
17228 end = end === undefined ? v.length : end;
17229 var d = this.el.dom;
17230 if(d.setSelectionRange){
17231 d.setSelectionRange(start, end);
17232 }else if(d.createTextRange){
17233 var range = d.createTextRange();
17234 range.moveStart("character", start);
17235 range.moveEnd("character", v.length-end);
17242 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17243 * This only takes effect if grow = true, and fires the autosize event.
17245 autoSize : function(){
17246 if(!this.grow || !this.rendered){
17250 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17253 var v = el.dom.value;
17254 var d = document.createElement('div');
17255 d.appendChild(document.createTextNode(v));
17259 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17260 this.el.setWidth(w);
17261 this.fireEvent("autosize", this, w);
17265 SafariOnKeyDown : function(event)
17267 // this is a workaround for a password hang bug on chrome/ webkit.
17269 var isSelectAll = false;
17271 if(this.el.dom.selectionEnd > 0){
17272 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17274 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17275 event.preventDefault();
17280 if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17282 event.preventDefault();
17283 // this is very hacky as keydown always get's upper case.
17285 var cc = String.fromCharCode(event.getCharCode());
17288 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
17296 * Ext JS Library 1.1.1
17297 * Copyright(c) 2006-2007, Ext JS, LLC.
17299 * Originally Released Under LGPL - original licence link has changed is not relivant.
17302 * <script type="text/javascript">
17306 * @class Roo.form.Hidden
17307 * @extends Roo.form.TextField
17308 * Simple Hidden element used on forms
17310 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17313 * Creates a new Hidden form element.
17314 * @param {Object} config Configuration options
17319 // easy hidden field...
17320 Roo.form.Hidden = function(config){
17321 Roo.form.Hidden.superclass.constructor.call(this, config);
17324 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17326 inputType: 'hidden',
17329 labelSeparator: '',
17331 itemCls : 'x-form-item-display-none'
17339 * Ext JS Library 1.1.1
17340 * Copyright(c) 2006-2007, Ext JS, LLC.
17342 * Originally Released Under LGPL - original licence link has changed is not relivant.
17345 * <script type="text/javascript">
17349 * @class Roo.form.TriggerField
17350 * @extends Roo.form.TextField
17351 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17352 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17353 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17354 * for which you can provide a custom implementation. For example:
17356 var trigger = new Roo.form.TriggerField();
17357 trigger.onTriggerClick = myTriggerFn;
17358 trigger.applyTo('my-field');
17361 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17362 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17363 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
17364 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17366 * Create a new TriggerField.
17367 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17368 * to the base TextField)
17370 Roo.form.TriggerField = function(config){
17371 this.mimicing = false;
17372 Roo.form.TriggerField.superclass.constructor.call(this, config);
17375 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
17377 * @cfg {String} triggerClass A CSS class to apply to the trigger
17380 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17381 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17383 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17385 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17389 /** @cfg {Boolean} grow @hide */
17390 /** @cfg {Number} growMin @hide */
17391 /** @cfg {Number} growMax @hide */
17397 autoSize: Roo.emptyFn,
17401 deferHeight : true,
17404 actionMode : 'wrap',
17406 onResize : function(w, h){
17407 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17408 if(typeof w == 'number'){
17409 var x = w - this.trigger.getWidth();
17410 this.el.setWidth(this.adjustWidth('input', x));
17411 this.trigger.setStyle('left', x+'px');
17416 adjustSize : Roo.BoxComponent.prototype.adjustSize,
17419 getResizeEl : function(){
17424 getPositionEl : function(){
17429 alignErrorIcon : function(){
17430 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17434 onRender : function(ct, position){
17435 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17436 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17437 this.trigger = this.wrap.createChild(this.triggerConfig ||
17438 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17439 if(this.hideTrigger){
17440 this.trigger.setDisplayed(false);
17442 this.initTrigger();
17444 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17449 initTrigger : function(){
17450 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17451 this.trigger.addClassOnOver('x-form-trigger-over');
17452 this.trigger.addClassOnClick('x-form-trigger-click');
17456 onDestroy : function(){
17458 this.trigger.removeAllListeners();
17459 this.trigger.remove();
17462 this.wrap.remove();
17464 Roo.form.TriggerField.superclass.onDestroy.call(this);
17468 onFocus : function(){
17469 Roo.form.TriggerField.superclass.onFocus.call(this);
17470 if(!this.mimicing){
17471 this.wrap.addClass('x-trigger-wrap-focus');
17472 this.mimicing = true;
17473 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17474 if(this.monitorTab){
17475 this.el.on("keydown", this.checkTab, this);
17481 checkTab : function(e){
17482 if(e.getKey() == e.TAB){
17483 this.triggerBlur();
17488 onBlur : function(){
17493 mimicBlur : function(e, t){
17494 if(!this.wrap.contains(t) && this.validateBlur()){
17495 this.triggerBlur();
17500 triggerBlur : function(){
17501 this.mimicing = false;
17502 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17503 if(this.monitorTab){
17504 this.el.un("keydown", this.checkTab, this);
17506 this.wrap.removeClass('x-trigger-wrap-focus');
17507 Roo.form.TriggerField.superclass.onBlur.call(this);
17511 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17512 validateBlur : function(e, t){
17517 onDisable : function(){
17518 Roo.form.TriggerField.superclass.onDisable.call(this);
17520 this.wrap.addClass('x-item-disabled');
17525 onEnable : function(){
17526 Roo.form.TriggerField.superclass.onEnable.call(this);
17528 this.wrap.removeClass('x-item-disabled');
17533 onShow : function(){
17534 var ae = this.getActionEl();
17537 ae.dom.style.display = '';
17538 ae.dom.style.visibility = 'visible';
17544 onHide : function(){
17545 var ae = this.getActionEl();
17546 ae.dom.style.display = 'none';
17550 * The function that should handle the trigger's click event. This method does nothing by default until overridden
17551 * by an implementing function.
17553 * @param {EventObject} e
17555 onTriggerClick : Roo.emptyFn
17558 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
17559 // to be extended by an implementing class. For an example of implementing this class, see the custom
17560 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17561 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17562 initComponent : function(){
17563 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17565 this.triggerConfig = {
17566 tag:'span', cls:'x-form-twin-triggers', cn:[
17567 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17568 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17572 getTrigger : function(index){
17573 return this.triggers[index];
17576 initTrigger : function(){
17577 var ts = this.trigger.select('.x-form-trigger', true);
17578 this.wrap.setStyle('overflow', 'hidden');
17579 var triggerField = this;
17580 ts.each(function(t, all, index){
17581 t.hide = function(){
17582 var w = triggerField.wrap.getWidth();
17583 this.dom.style.display = 'none';
17584 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17586 t.show = function(){
17587 var w = triggerField.wrap.getWidth();
17588 this.dom.style.display = '';
17589 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17591 var triggerIndex = 'Trigger'+(index+1);
17593 if(this['hide'+triggerIndex]){
17594 t.dom.style.display = 'none';
17596 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17597 t.addClassOnOver('x-form-trigger-over');
17598 t.addClassOnClick('x-form-trigger-click');
17600 this.triggers = ts.elements;
17603 onTrigger1Click : Roo.emptyFn,
17604 onTrigger2Click : Roo.emptyFn
17607 * Ext JS Library 1.1.1
17608 * Copyright(c) 2006-2007, Ext JS, LLC.
17610 * Originally Released Under LGPL - original licence link has changed is not relivant.
17613 * <script type="text/javascript">
17617 * @class Roo.form.TextArea
17618 * @extends Roo.form.TextField
17619 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
17620 * support for auto-sizing.
17622 * Creates a new TextArea
17623 * @param {Object} config Configuration options
17625 Roo.form.TextArea = function(config){
17626 Roo.form.TextArea.superclass.constructor.call(this, config);
17627 // these are provided exchanges for backwards compat
17628 // minHeight/maxHeight were replaced by growMin/growMax to be
17629 // compatible with TextField growing config values
17630 if(this.minHeight !== undefined){
17631 this.growMin = this.minHeight;
17633 if(this.maxHeight !== undefined){
17634 this.growMax = this.maxHeight;
17638 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
17640 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17644 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17648 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17649 * in the field (equivalent to setting overflow: hidden, defaults to false)
17651 preventScrollbars: false,
17653 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17654 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17658 onRender : function(ct, position){
17660 this.defaultAutoCreate = {
17662 style:"width:300px;height:60px;",
17663 autocomplete: "new-password"
17666 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17668 this.textSizeEl = Roo.DomHelper.append(document.body, {
17669 tag: "pre", cls: "x-form-grow-sizer"
17671 if(this.preventScrollbars){
17672 this.el.setStyle("overflow", "hidden");
17674 this.el.setHeight(this.growMin);
17678 onDestroy : function(){
17679 if(this.textSizeEl){
17680 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17682 Roo.form.TextArea.superclass.onDestroy.call(this);
17686 onKeyUp : function(e){
17687 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17693 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17694 * This only takes effect if grow = true, and fires the autosize event if the height changes.
17696 autoSize : function(){
17697 if(!this.grow || !this.textSizeEl){
17701 var v = el.dom.value;
17702 var ts = this.textSizeEl;
17705 ts.appendChild(document.createTextNode(v));
17708 Roo.fly(ts).setWidth(this.el.getWidth());
17710 v = "  ";
17713 v = v.replace(/\n/g, '<p> </p>');
17715 v += " \n ";
17718 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17719 if(h != this.lastHeight){
17720 this.lastHeight = h;
17721 this.el.setHeight(h);
17722 this.fireEvent("autosize", this, h);
17727 * Ext JS Library 1.1.1
17728 * Copyright(c) 2006-2007, Ext JS, LLC.
17730 * Originally Released Under LGPL - original licence link has changed is not relivant.
17733 * <script type="text/javascript">
17738 * @class Roo.form.NumberField
17739 * @extends Roo.form.TextField
17740 * Numeric text field that provides automatic keystroke filtering and numeric validation.
17742 * Creates a new NumberField
17743 * @param {Object} config Configuration options
17745 Roo.form.NumberField = function(config){
17746 Roo.form.NumberField.superclass.constructor.call(this, config);
17749 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
17751 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17753 fieldClass: "x-form-field x-form-num-field",
17755 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17757 allowDecimals : true,
17759 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17761 decimalSeparator : ".",
17763 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17765 decimalPrecision : 2,
17767 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17769 allowNegative : true,
17771 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17773 minValue : Number.NEGATIVE_INFINITY,
17775 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17777 maxValue : Number.MAX_VALUE,
17779 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17781 minText : "The minimum value for this field is {0}",
17783 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17785 maxText : "The maximum value for this field is {0}",
17787 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
17788 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17790 nanText : "{0} is not a valid number",
17793 initEvents : function(){
17794 Roo.form.NumberField.superclass.initEvents.call(this);
17795 var allowed = "0123456789";
17796 if(this.allowDecimals){
17797 allowed += this.decimalSeparator;
17799 if(this.allowNegative){
17802 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17803 var keyPress = function(e){
17804 var k = e.getKey();
17805 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17808 var c = e.getCharCode();
17809 if(allowed.indexOf(String.fromCharCode(c)) === -1){
17813 this.el.on("keypress", keyPress, this);
17817 validateValue : function(value){
17818 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17821 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17824 var num = this.parseValue(value);
17826 this.markInvalid(String.format(this.nanText, value));
17829 if(num < this.minValue){
17830 this.markInvalid(String.format(this.minText, this.minValue));
17833 if(num > this.maxValue){
17834 this.markInvalid(String.format(this.maxText, this.maxValue));
17840 getValue : function(){
17841 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17845 parseValue : function(value){
17846 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17847 return isNaN(value) ? '' : value;
17851 fixPrecision : function(value){
17852 var nan = isNaN(value);
17853 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17854 return nan ? '' : value;
17856 return parseFloat(value).toFixed(this.decimalPrecision);
17859 setValue : function(v){
17860 v = this.fixPrecision(v);
17861 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17865 decimalPrecisionFcn : function(v){
17866 return Math.floor(v);
17869 beforeBlur : function(){
17870 var v = this.parseValue(this.getRawValue());
17877 * Ext JS Library 1.1.1
17878 * Copyright(c) 2006-2007, Ext JS, LLC.
17880 * Originally Released Under LGPL - original licence link has changed is not relivant.
17883 * <script type="text/javascript">
17887 * @class Roo.form.DateField
17888 * @extends Roo.form.TriggerField
17889 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17891 * Create a new DateField
17892 * @param {Object} config
17894 Roo.form.DateField = function(config)
17896 Roo.form.DateField.superclass.constructor.call(this, config);
17902 * Fires when a date is selected
17903 * @param {Roo.form.DateField} combo This combo box
17904 * @param {Date} date The date selected
17911 if(typeof this.minValue == "string") {
17912 this.minValue = this.parseDate(this.minValue);
17914 if(typeof this.maxValue == "string") {
17915 this.maxValue = this.parseDate(this.maxValue);
17917 this.ddMatch = null;
17918 if(this.disabledDates){
17919 var dd = this.disabledDates;
17921 for(var i = 0; i < dd.length; i++){
17923 if(i != dd.length-1) {
17927 this.ddMatch = new RegExp(re + ")");
17931 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
17933 * @cfg {String} format
17934 * The default date format string which can be overriden for localization support. The format must be
17935 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17939 * @cfg {String} altFormats
17940 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17941 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17943 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17945 * @cfg {Array} disabledDays
17946 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17948 disabledDays : null,
17950 * @cfg {String} disabledDaysText
17951 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17953 disabledDaysText : "Disabled",
17955 * @cfg {Array} disabledDates
17956 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17957 * expression so they are very powerful. Some examples:
17959 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17960 * <li>["03/08", "09/16"] would disable those days for every year</li>
17961 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17962 * <li>["03/../2006"] would disable every day in March 2006</li>
17963 * <li>["^03"] would disable every day in every March</li>
17965 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17966 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17968 disabledDates : null,
17970 * @cfg {String} disabledDatesText
17971 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17973 disabledDatesText : "Disabled",
17975 * @cfg {Date/String} minValue
17976 * The minimum allowed date. Can be either a Javascript date object or a string date in a
17977 * valid format (defaults to null).
17981 * @cfg {Date/String} maxValue
17982 * The maximum allowed date. Can be either a Javascript date object or a string date in a
17983 * valid format (defaults to null).
17987 * @cfg {String} minText
17988 * The error text to display when the date in the cell is before minValue (defaults to
17989 * 'The date in this field must be after {minValue}').
17991 minText : "The date in this field must be equal to or after {0}",
17993 * @cfg {String} maxText
17994 * The error text to display when the date in the cell is after maxValue (defaults to
17995 * 'The date in this field must be before {maxValue}').
17997 maxText : "The date in this field must be equal to or before {0}",
17999 * @cfg {String} invalidText
18000 * The error text to display when the date in the field is invalid (defaults to
18001 * '{value} is not a valid date - it must be in the format {format}').
18003 invalidText : "{0} is not a valid date - it must be in the format {1}",
18005 * @cfg {String} triggerClass
18006 * An additional CSS class used to style the trigger button. The trigger will always get the
18007 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18008 * which displays a calendar icon).
18010 triggerClass : 'x-form-date-trigger',
18014 * @cfg {Boolean} useIso
18015 * if enabled, then the date field will use a hidden field to store the
18016 * real value as iso formated date. default (false)
18020 * @cfg {String/Object} autoCreate
18021 * A DomHelper element spec, or true for a default element spec (defaults to
18022 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18025 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
18028 hiddenField: false,
18030 onRender : function(ct, position)
18032 Roo.form.DateField.superclass.onRender.call(this, ct, position);
18034 //this.el.dom.removeAttribute('name');
18035 Roo.log("Changing name?");
18036 this.el.dom.setAttribute('name', this.name + '____hidden___' );
18037 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18039 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18040 // prevent input submission
18041 this.hiddenName = this.name;
18048 validateValue : function(value)
18050 value = this.formatDate(value);
18051 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
18052 Roo.log('super failed');
18055 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18058 var svalue = value;
18059 value = this.parseDate(value);
18061 Roo.log('parse date failed' + svalue);
18062 this.markInvalid(String.format(this.invalidText, svalue, this.format));
18065 var time = value.getTime();
18066 if(this.minValue && time < this.minValue.getTime()){
18067 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18070 if(this.maxValue && time > this.maxValue.getTime()){
18071 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18074 if(this.disabledDays){
18075 var day = value.getDay();
18076 for(var i = 0; i < this.disabledDays.length; i++) {
18077 if(day === this.disabledDays[i]){
18078 this.markInvalid(this.disabledDaysText);
18083 var fvalue = this.formatDate(value);
18084 if(this.ddMatch && this.ddMatch.test(fvalue)){
18085 this.markInvalid(String.format(this.disabledDatesText, fvalue));
18092 // Provides logic to override the default TriggerField.validateBlur which just returns true
18093 validateBlur : function(){
18094 return !this.menu || !this.menu.isVisible();
18097 getName: function()
18099 // returns hidden if it's set..
18100 if (!this.rendered) {return ''};
18101 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
18106 * Returns the current date value of the date field.
18107 * @return {Date} The date value
18109 getValue : function(){
18111 return this.hiddenField ?
18112 this.hiddenField.value :
18113 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
18117 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
18118 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
18119 * (the default format used is "m/d/y").
18122 //All of these calls set the same date value (May 4, 2006)
18124 //Pass a date object:
18125 var dt = new Date('5/4/06');
18126 dateField.setValue(dt);
18128 //Pass a date string (default format):
18129 dateField.setValue('5/4/06');
18131 //Pass a date string (custom format):
18132 dateField.format = 'Y-m-d';
18133 dateField.setValue('2006-5-4');
18135 * @param {String/Date} date The date or valid date string
18137 setValue : function(date){
18138 if (this.hiddenField) {
18139 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18141 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18142 // make sure the value field is always stored as a date..
18143 this.value = this.parseDate(date);
18149 parseDate : function(value){
18150 if(!value || value instanceof Date){
18153 var v = Date.parseDate(value, this.format);
18154 if (!v && this.useIso) {
18155 v = Date.parseDate(value, 'Y-m-d');
18157 if(!v && this.altFormats){
18158 if(!this.altFormatsArray){
18159 this.altFormatsArray = this.altFormats.split("|");
18161 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18162 v = Date.parseDate(value, this.altFormatsArray[i]);
18169 formatDate : function(date, fmt){
18170 return (!date || !(date instanceof Date)) ?
18171 date : date.dateFormat(fmt || this.format);
18176 select: function(m, d){
18179 this.fireEvent('select', this, d);
18181 show : function(){ // retain focus styling
18185 this.focus.defer(10, this);
18186 var ml = this.menuListeners;
18187 this.menu.un("select", ml.select, this);
18188 this.menu.un("show", ml.show, this);
18189 this.menu.un("hide", ml.hide, this);
18194 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18195 onTriggerClick : function(){
18199 if(this.menu == null){
18200 this.menu = new Roo.menu.DateMenu();
18202 Roo.apply(this.menu.picker, {
18203 showClear: this.allowBlank,
18204 minDate : this.minValue,
18205 maxDate : this.maxValue,
18206 disabledDatesRE : this.ddMatch,
18207 disabledDatesText : this.disabledDatesText,
18208 disabledDays : this.disabledDays,
18209 disabledDaysText : this.disabledDaysText,
18210 format : this.useIso ? 'Y-m-d' : this.format,
18211 minText : String.format(this.minText, this.formatDate(this.minValue)),
18212 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18214 this.menu.on(Roo.apply({}, this.menuListeners, {
18217 this.menu.picker.setValue(this.getValue() || new Date());
18218 this.menu.show(this.el, "tl-bl?");
18221 beforeBlur : function(){
18222 var v = this.parseDate(this.getRawValue());
18232 isDirty : function() {
18233 if(this.disabled) {
18237 if(typeof(this.startValue) === 'undefined'){
18241 return String(this.getValue()) !== String(this.startValue);
18245 cleanLeadingSpace : function(e)
18252 * Ext JS Library 1.1.1
18253 * Copyright(c) 2006-2007, Ext JS, LLC.
18255 * Originally Released Under LGPL - original licence link has changed is not relivant.
18258 * <script type="text/javascript">
18262 * @class Roo.form.MonthField
18263 * @extends Roo.form.TriggerField
18264 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18266 * Create a new MonthField
18267 * @param {Object} config
18269 Roo.form.MonthField = function(config){
18271 Roo.form.MonthField.superclass.constructor.call(this, config);
18277 * Fires when a date is selected
18278 * @param {Roo.form.MonthFieeld} combo This combo box
18279 * @param {Date} date The date selected
18286 if(typeof this.minValue == "string") {
18287 this.minValue = this.parseDate(this.minValue);
18289 if(typeof this.maxValue == "string") {
18290 this.maxValue = this.parseDate(this.maxValue);
18292 this.ddMatch = null;
18293 if(this.disabledDates){
18294 var dd = this.disabledDates;
18296 for(var i = 0; i < dd.length; i++){
18298 if(i != dd.length-1) {
18302 this.ddMatch = new RegExp(re + ")");
18306 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
18308 * @cfg {String} format
18309 * The default date format string which can be overriden for localization support. The format must be
18310 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18314 * @cfg {String} altFormats
18315 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18316 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18318 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18320 * @cfg {Array} disabledDays
18321 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18323 disabledDays : [0,1,2,3,4,5,6],
18325 * @cfg {String} disabledDaysText
18326 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18328 disabledDaysText : "Disabled",
18330 * @cfg {Array} disabledDates
18331 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18332 * expression so they are very powerful. Some examples:
18334 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18335 * <li>["03/08", "09/16"] would disable those days for every year</li>
18336 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18337 * <li>["03/../2006"] would disable every day in March 2006</li>
18338 * <li>["^03"] would disable every day in every March</li>
18340 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18341 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18343 disabledDates : null,
18345 * @cfg {String} disabledDatesText
18346 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18348 disabledDatesText : "Disabled",
18350 * @cfg {Date/String} minValue
18351 * The minimum allowed date. Can be either a Javascript date object or a string date in a
18352 * valid format (defaults to null).
18356 * @cfg {Date/String} maxValue
18357 * The maximum allowed date. Can be either a Javascript date object or a string date in a
18358 * valid format (defaults to null).
18362 * @cfg {String} minText
18363 * The error text to display when the date in the cell is before minValue (defaults to
18364 * 'The date in this field must be after {minValue}').
18366 minText : "The date in this field must be equal to or after {0}",
18368 * @cfg {String} maxTextf
18369 * The error text to display when the date in the cell is after maxValue (defaults to
18370 * 'The date in this field must be before {maxValue}').
18372 maxText : "The date in this field must be equal to or before {0}",
18374 * @cfg {String} invalidText
18375 * The error text to display when the date in the field is invalid (defaults to
18376 * '{value} is not a valid date - it must be in the format {format}').
18378 invalidText : "{0} is not a valid date - it must be in the format {1}",
18380 * @cfg {String} triggerClass
18381 * An additional CSS class used to style the trigger button. The trigger will always get the
18382 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18383 * which displays a calendar icon).
18385 triggerClass : 'x-form-date-trigger',
18389 * @cfg {Boolean} useIso
18390 * if enabled, then the date field will use a hidden field to store the
18391 * real value as iso formated date. default (true)
18395 * @cfg {String/Object} autoCreate
18396 * A DomHelper element spec, or true for a default element spec (defaults to
18397 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18400 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18403 hiddenField: false,
18405 hideMonthPicker : false,
18407 onRender : function(ct, position)
18409 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18411 this.el.dom.removeAttribute('name');
18412 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18414 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18415 // prevent input submission
18416 this.hiddenName = this.name;
18423 validateValue : function(value)
18425 value = this.formatDate(value);
18426 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18429 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18432 var svalue = value;
18433 value = this.parseDate(value);
18435 this.markInvalid(String.format(this.invalidText, svalue, this.format));
18438 var time = value.getTime();
18439 if(this.minValue && time < this.minValue.getTime()){
18440 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18443 if(this.maxValue && time > this.maxValue.getTime()){
18444 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18447 /*if(this.disabledDays){
18448 var day = value.getDay();
18449 for(var i = 0; i < this.disabledDays.length; i++) {
18450 if(day === this.disabledDays[i]){
18451 this.markInvalid(this.disabledDaysText);
18457 var fvalue = this.formatDate(value);
18458 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18459 this.markInvalid(String.format(this.disabledDatesText, fvalue));
18467 // Provides logic to override the default TriggerField.validateBlur which just returns true
18468 validateBlur : function(){
18469 return !this.menu || !this.menu.isVisible();
18473 * Returns the current date value of the date field.
18474 * @return {Date} The date value
18476 getValue : function(){
18480 return this.hiddenField ?
18481 this.hiddenField.value :
18482 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18486 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
18487 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18488 * (the default format used is "m/d/y").
18491 //All of these calls set the same date value (May 4, 2006)
18493 //Pass a date object:
18494 var dt = new Date('5/4/06');
18495 monthField.setValue(dt);
18497 //Pass a date string (default format):
18498 monthField.setValue('5/4/06');
18500 //Pass a date string (custom format):
18501 monthField.format = 'Y-m-d';
18502 monthField.setValue('2006-5-4');
18504 * @param {String/Date} date The date or valid date string
18506 setValue : function(date){
18507 Roo.log('month setValue' + date);
18508 // can only be first of month..
18510 var val = this.parseDate(date);
18512 if (this.hiddenField) {
18513 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18515 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18516 this.value = this.parseDate(date);
18520 parseDate : function(value){
18521 if(!value || value instanceof Date){
18522 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18525 var v = Date.parseDate(value, this.format);
18526 if (!v && this.useIso) {
18527 v = Date.parseDate(value, 'Y-m-d');
18531 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18535 if(!v && this.altFormats){
18536 if(!this.altFormatsArray){
18537 this.altFormatsArray = this.altFormats.split("|");
18539 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18540 v = Date.parseDate(value, this.altFormatsArray[i]);
18547 formatDate : function(date, fmt){
18548 return (!date || !(date instanceof Date)) ?
18549 date : date.dateFormat(fmt || this.format);
18554 select: function(m, d){
18556 this.fireEvent('select', this, d);
18558 show : function(){ // retain focus styling
18562 this.focus.defer(10, this);
18563 var ml = this.menuListeners;
18564 this.menu.un("select", ml.select, this);
18565 this.menu.un("show", ml.show, this);
18566 this.menu.un("hide", ml.hide, this);
18570 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18571 onTriggerClick : function(){
18575 if(this.menu == null){
18576 this.menu = new Roo.menu.DateMenu();
18580 Roo.apply(this.menu.picker, {
18582 showClear: this.allowBlank,
18583 minDate : this.minValue,
18584 maxDate : this.maxValue,
18585 disabledDatesRE : this.ddMatch,
18586 disabledDatesText : this.disabledDatesText,
18588 format : this.useIso ? 'Y-m-d' : this.format,
18589 minText : String.format(this.minText, this.formatDate(this.minValue)),
18590 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18593 this.menu.on(Roo.apply({}, this.menuListeners, {
18601 // hide month picker get's called when we called by 'before hide';
18603 var ignorehide = true;
18604 p.hideMonthPicker = function(disableAnim){
18608 if(this.monthPicker){
18609 Roo.log("hideMonthPicker called");
18610 if(disableAnim === true){
18611 this.monthPicker.hide();
18613 this.monthPicker.slideOut('t', {duration:.2});
18614 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18615 p.fireEvent("select", this, this.value);
18621 Roo.log('picker set value');
18622 Roo.log(this.getValue());
18623 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18624 m.show(this.el, 'tl-bl?');
18625 ignorehide = false;
18626 // this will trigger hideMonthPicker..
18629 // hidden the day picker
18630 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18636 p.showMonthPicker.defer(100, p);
18642 beforeBlur : function(){
18643 var v = this.parseDate(this.getRawValue());
18649 /** @cfg {Boolean} grow @hide */
18650 /** @cfg {Number} growMin @hide */
18651 /** @cfg {Number} growMax @hide */
18658 * Ext JS Library 1.1.1
18659 * Copyright(c) 2006-2007, Ext JS, LLC.
18661 * Originally Released Under LGPL - original licence link has changed is not relivant.
18664 * <script type="text/javascript">
18669 * @class Roo.form.ComboBox
18670 * @extends Roo.form.TriggerField
18671 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18673 * Create a new ComboBox.
18674 * @param {Object} config Configuration options
18676 Roo.form.ComboBox = function(config){
18677 Roo.form.ComboBox.superclass.constructor.call(this, config);
18681 * Fires when the dropdown list is expanded
18682 * @param {Roo.form.ComboBox} combo This combo box
18687 * Fires when the dropdown list is collapsed
18688 * @param {Roo.form.ComboBox} combo This combo box
18692 * @event beforeselect
18693 * Fires before a list item is selected. Return false to cancel the selection.
18694 * @param {Roo.form.ComboBox} combo This combo box
18695 * @param {Roo.data.Record} record The data record returned from the underlying store
18696 * @param {Number} index The index of the selected item in the dropdown list
18698 'beforeselect' : true,
18701 * Fires when a list item is selected
18702 * @param {Roo.form.ComboBox} combo This combo box
18703 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18704 * @param {Number} index The index of the selected item in the dropdown list
18708 * @event beforequery
18709 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18710 * The event object passed has these properties:
18711 * @param {Roo.form.ComboBox} combo This combo box
18712 * @param {String} query The query
18713 * @param {Boolean} forceAll true to force "all" query
18714 * @param {Boolean} cancel true to cancel the query
18715 * @param {Object} e The query event object
18717 'beforequery': true,
18720 * Fires when the 'add' icon is pressed (add a listener to enable add button)
18721 * @param {Roo.form.ComboBox} combo This combo box
18726 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18727 * @param {Roo.form.ComboBox} combo This combo box
18728 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18734 if(this.transform){
18735 this.allowDomMove = false;
18736 var s = Roo.getDom(this.transform);
18737 if(!this.hiddenName){
18738 this.hiddenName = s.name;
18741 this.mode = 'local';
18742 var d = [], opts = s.options;
18743 for(var i = 0, len = opts.length;i < len; i++){
18745 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18747 this.value = value;
18749 d.push([value, o.text]);
18751 this.store = new Roo.data.SimpleStore({
18753 fields: ['value', 'text'],
18756 this.valueField = 'value';
18757 this.displayField = 'text';
18759 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18760 if(!this.lazyRender){
18761 this.target = true;
18762 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18763 s.parentNode.removeChild(s); // remove it
18764 this.render(this.el.parentNode);
18766 s.parentNode.removeChild(s); // remove it
18771 this.store = Roo.factory(this.store, Roo.data);
18774 this.selectedIndex = -1;
18775 if(this.mode == 'local'){
18776 if(config.queryDelay === undefined){
18777 this.queryDelay = 10;
18779 if(config.minChars === undefined){
18785 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18787 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18790 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18791 * rendering into an Roo.Editor, defaults to false)
18794 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18795 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18798 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18801 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18802 * the dropdown list (defaults to undefined, with no header element)
18806 * @cfg {String/Roo.Template} tpl The template to use to render the output
18810 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18812 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18814 listWidth: undefined,
18816 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18817 * mode = 'remote' or 'text' if mode = 'local')
18819 displayField: undefined,
18821 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18822 * mode = 'remote' or 'value' if mode = 'local').
18823 * Note: use of a valueField requires the user make a selection
18824 * in order for a value to be mapped.
18826 valueField: undefined,
18830 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18831 * field's data value (defaults to the underlying DOM element's name)
18833 hiddenName: undefined,
18835 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18839 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18841 selectedClass: 'x-combo-selected',
18843 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
18844 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18845 * which displays a downward arrow icon).
18847 triggerClass : 'x-form-arrow-trigger',
18849 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18853 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18854 * anchor positions (defaults to 'tl-bl')
18856 listAlign: 'tl-bl?',
18858 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18862 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
18863 * query specified by the allQuery config option (defaults to 'query')
18865 triggerAction: 'query',
18867 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18868 * (defaults to 4, does not apply if editable = false)
18872 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18873 * delay (typeAheadDelay) if it matches a known value (defaults to false)
18877 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18878 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18882 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18883 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
18887 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
18888 * when editable = true (defaults to false)
18890 selectOnFocus:false,
18892 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18894 queryParam: 'query',
18896 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
18897 * when mode = 'remote' (defaults to 'Loading...')
18899 loadingText: 'Loading...',
18901 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18905 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18909 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18910 * traditional select (defaults to true)
18914 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18918 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18922 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18923 * listWidth has a higher value)
18927 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18928 * allow the user to set arbitrary text into the field (defaults to false)
18930 forceSelection:false,
18932 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18933 * if typeAhead = true (defaults to 250)
18935 typeAheadDelay : 250,
18937 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18938 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18940 valueNotFoundText : undefined,
18942 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18944 blockFocus : false,
18947 * @cfg {Boolean} disableClear Disable showing of clear button.
18949 disableClear : false,
18951 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
18953 alwaysQuery : false,
18959 // element that contains real text value.. (when hidden is used..)
18962 onRender : function(ct, position)
18964 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18966 if(this.hiddenName){
18967 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
18969 this.hiddenField.value =
18970 this.hiddenValue !== undefined ? this.hiddenValue :
18971 this.value !== undefined ? this.value : '';
18973 // prevent input submission
18974 this.el.dom.removeAttribute('name');
18980 this.el.dom.setAttribute('autocomplete', 'off');
18983 var cls = 'x-combo-list';
18985 this.list = new Roo.Layer({
18986 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18989 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18990 this.list.setWidth(lw);
18991 this.list.swallowEvent('mousewheel');
18992 this.assetHeight = 0;
18995 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18996 this.assetHeight += this.header.getHeight();
18999 this.innerList = this.list.createChild({cls:cls+'-inner'});
19000 this.innerList.on('mouseover', this.onViewOver, this);
19001 this.innerList.on('mousemove', this.onViewMove, this);
19002 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19004 if(this.allowBlank && !this.pageSize && !this.disableClear){
19005 this.footer = this.list.createChild({cls:cls+'-ft'});
19006 this.pageTb = new Roo.Toolbar(this.footer);
19010 this.footer = this.list.createChild({cls:cls+'-ft'});
19011 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
19012 {pageSize: this.pageSize});
19016 if (this.pageTb && this.allowBlank && !this.disableClear) {
19018 this.pageTb.add(new Roo.Toolbar.Fill(), {
19019 cls: 'x-btn-icon x-btn-clear',
19021 handler: function()
19024 _this.clearValue();
19025 _this.onSelect(false, -1);
19030 this.assetHeight += this.footer.getHeight();
19035 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
19038 this.view = new Roo.View(this.innerList, this.tpl, {
19041 selectedClass: this.selectedClass
19044 this.view.on('click', this.onViewClick, this);
19046 this.store.on('beforeload', this.onBeforeLoad, this);
19047 this.store.on('load', this.onLoad, this);
19048 this.store.on('loadexception', this.onLoadException, this);
19050 if(this.resizable){
19051 this.resizer = new Roo.Resizable(this.list, {
19052 pinned:true, handles:'se'
19054 this.resizer.on('resize', function(r, w, h){
19055 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
19056 this.listWidth = w;
19057 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
19058 this.restrictHeight();
19060 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
19062 if(!this.editable){
19063 this.editable = true;
19064 this.setEditable(false);
19068 if (typeof(this.events.add.listeners) != 'undefined') {
19070 this.addicon = this.wrap.createChild(
19071 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
19073 this.addicon.on('click', function(e) {
19074 this.fireEvent('add', this);
19077 if (typeof(this.events.edit.listeners) != 'undefined') {
19079 this.editicon = this.wrap.createChild(
19080 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
19081 if (this.addicon) {
19082 this.editicon.setStyle('margin-left', '40px');
19084 this.editicon.on('click', function(e) {
19086 // we fire even if inothing is selected..
19087 this.fireEvent('edit', this, this.lastData );
19097 initEvents : function(){
19098 Roo.form.ComboBox.superclass.initEvents.call(this);
19100 this.keyNav = new Roo.KeyNav(this.el, {
19101 "up" : function(e){
19102 this.inKeyMode = true;
19106 "down" : function(e){
19107 if(!this.isExpanded()){
19108 this.onTriggerClick();
19110 this.inKeyMode = true;
19115 "enter" : function(e){
19116 this.onViewClick();
19120 "esc" : function(e){
19124 "tab" : function(e){
19125 this.onViewClick(false);
19126 this.fireEvent("specialkey", this, e);
19132 doRelay : function(foo, bar, hname){
19133 if(hname == 'down' || this.scope.isExpanded()){
19134 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
19141 this.queryDelay = Math.max(this.queryDelay || 10,
19142 this.mode == 'local' ? 10 : 250);
19143 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
19144 if(this.typeAhead){
19145 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
19147 if(this.editable !== false){
19148 this.el.on("keyup", this.onKeyUp, this);
19150 if(this.forceSelection){
19151 this.on('blur', this.doForce, this);
19155 onDestroy : function(){
19157 this.view.setStore(null);
19158 this.view.el.removeAllListeners();
19159 this.view.el.remove();
19160 this.view.purgeListeners();
19163 this.list.destroy();
19166 this.store.un('beforeload', this.onBeforeLoad, this);
19167 this.store.un('load', this.onLoad, this);
19168 this.store.un('loadexception', this.onLoadException, this);
19170 Roo.form.ComboBox.superclass.onDestroy.call(this);
19174 fireKey : function(e){
19175 if(e.isNavKeyPress() && !this.list.isVisible()){
19176 this.fireEvent("specialkey", this, e);
19181 onResize: function(w, h){
19182 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19184 if(typeof w != 'number'){
19185 // we do not handle it!?!?
19188 var tw = this.trigger.getWidth();
19189 tw += this.addicon ? this.addicon.getWidth() : 0;
19190 tw += this.editicon ? this.editicon.getWidth() : 0;
19192 this.el.setWidth( this.adjustWidth('input', x));
19194 this.trigger.setStyle('left', x+'px');
19196 if(this.list && this.listWidth === undefined){
19197 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19198 this.list.setWidth(lw);
19199 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19207 * Allow or prevent the user from directly editing the field text. If false is passed,
19208 * the user will only be able to select from the items defined in the dropdown list. This method
19209 * is the runtime equivalent of setting the 'editable' config option at config time.
19210 * @param {Boolean} value True to allow the user to directly edit the field text
19212 setEditable : function(value){
19213 if(value == this.editable){
19216 this.editable = value;
19218 this.el.dom.setAttribute('readOnly', true);
19219 this.el.on('mousedown', this.onTriggerClick, this);
19220 this.el.addClass('x-combo-noedit');
19222 this.el.dom.setAttribute('readOnly', false);
19223 this.el.un('mousedown', this.onTriggerClick, this);
19224 this.el.removeClass('x-combo-noedit');
19229 onBeforeLoad : function(){
19230 if(!this.hasFocus){
19233 this.innerList.update(this.loadingText ?
19234 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19235 this.restrictHeight();
19236 this.selectedIndex = -1;
19240 onLoad : function(){
19241 if(!this.hasFocus){
19244 if(this.store.getCount() > 0){
19246 this.restrictHeight();
19247 if(this.lastQuery == this.allQuery){
19249 this.el.dom.select();
19251 if(!this.selectByValue(this.value, true)){
19252 this.select(0, true);
19256 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19257 this.taTask.delay(this.typeAheadDelay);
19261 this.onEmptyResults();
19266 onLoadException : function()
19269 Roo.log(this.store.reader.jsonData);
19270 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19271 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19277 onTypeAhead : function(){
19278 if(this.store.getCount() > 0){
19279 var r = this.store.getAt(0);
19280 var newValue = r.data[this.displayField];
19281 var len = newValue.length;
19282 var selStart = this.getRawValue().length;
19283 if(selStart != len){
19284 this.setRawValue(newValue);
19285 this.selectText(selStart, newValue.length);
19291 onSelect : function(record, index){
19292 if(this.fireEvent('beforeselect', this, record, index) !== false){
19293 this.setFromData(index > -1 ? record.data : false);
19295 this.fireEvent('select', this, record, index);
19300 * Returns the currently selected field value or empty string if no value is set.
19301 * @return {String} value The selected value
19303 getValue : function(){
19304 if(this.valueField){
19305 return typeof this.value != 'undefined' ? this.value : '';
19307 return Roo.form.ComboBox.superclass.getValue.call(this);
19311 * Clears any text/value currently set in the field
19313 clearValue : function(){
19314 if(this.hiddenField){
19315 this.hiddenField.value = '';
19318 this.setRawValue('');
19319 this.lastSelectionText = '';
19324 * Sets the specified value into the field. If the value finds a match, the corresponding record text
19325 * will be displayed in the field. If the value does not match the data value of an existing item,
19326 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19327 * Otherwise the field will be blank (although the value will still be set).
19328 * @param {String} value The value to match
19330 setValue : function(v){
19332 if(this.valueField){
19333 var r = this.findRecord(this.valueField, v);
19335 text = r.data[this.displayField];
19336 }else if(this.valueNotFoundText !== undefined){
19337 text = this.valueNotFoundText;
19340 this.lastSelectionText = text;
19341 if(this.hiddenField){
19342 this.hiddenField.value = v;
19344 Roo.form.ComboBox.superclass.setValue.call(this, text);
19348 * @property {Object} the last set data for the element
19353 * Sets the value of the field based on a object which is related to the record format for the store.
19354 * @param {Object} value the value to set as. or false on reset?
19356 setFromData : function(o){
19357 var dv = ''; // display value
19358 var vv = ''; // value value..
19360 if (this.displayField) {
19361 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19363 // this is an error condition!!!
19364 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
19367 if(this.valueField){
19368 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19370 if(this.hiddenField){
19371 this.hiddenField.value = vv;
19373 this.lastSelectionText = dv;
19374 Roo.form.ComboBox.superclass.setValue.call(this, dv);
19378 // no hidden field.. - we store the value in 'value', but still display
19379 // display field!!!!
19380 this.lastSelectionText = dv;
19381 Roo.form.ComboBox.superclass.setValue.call(this, dv);
19387 reset : function(){
19388 // overridden so that last data is reset..
19389 this.setValue(this.resetValue);
19390 this.originalValue = this.getValue();
19391 this.clearInvalid();
19392 this.lastData = false;
19394 this.view.clearSelections();
19398 findRecord : function(prop, value){
19400 if(this.store.getCount() > 0){
19401 this.store.each(function(r){
19402 if(r.data[prop] == value){
19412 getName: function()
19414 // returns hidden if it's set..
19415 if (!this.rendered) {return ''};
19416 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
19420 onViewMove : function(e, t){
19421 this.inKeyMode = false;
19425 onViewOver : function(e, t){
19426 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19429 var item = this.view.findItemFromChild(t);
19431 var index = this.view.indexOf(item);
19432 this.select(index, false);
19437 onViewClick : function(doFocus)
19439 var index = this.view.getSelectedIndexes()[0];
19440 var r = this.store.getAt(index);
19442 this.onSelect(r, index);
19444 if(doFocus !== false && !this.blockFocus){
19450 restrictHeight : function(){
19451 this.innerList.dom.style.height = '';
19452 var inner = this.innerList.dom;
19453 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19454 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19455 this.list.beginUpdate();
19456 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19457 this.list.alignTo(this.el, this.listAlign);
19458 this.list.endUpdate();
19462 onEmptyResults : function(){
19467 * Returns true if the dropdown list is expanded, else false.
19469 isExpanded : function(){
19470 return this.list.isVisible();
19474 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19475 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19476 * @param {String} value The data value of the item to select
19477 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19478 * selected item if it is not currently in view (defaults to true)
19479 * @return {Boolean} True if the value matched an item in the list, else false
19481 selectByValue : function(v, scrollIntoView){
19482 if(v !== undefined && v !== null){
19483 var r = this.findRecord(this.valueField || this.displayField, v);
19485 this.select(this.store.indexOf(r), scrollIntoView);
19493 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19494 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19495 * @param {Number} index The zero-based index of the list item to select
19496 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19497 * selected item if it is not currently in view (defaults to true)
19499 select : function(index, scrollIntoView){
19500 this.selectedIndex = index;
19501 this.view.select(index);
19502 if(scrollIntoView !== false){
19503 var el = this.view.getNode(index);
19505 this.innerList.scrollChildIntoView(el, false);
19511 selectNext : function(){
19512 var ct = this.store.getCount();
19514 if(this.selectedIndex == -1){
19516 }else if(this.selectedIndex < ct-1){
19517 this.select(this.selectedIndex+1);
19523 selectPrev : function(){
19524 var ct = this.store.getCount();
19526 if(this.selectedIndex == -1){
19528 }else if(this.selectedIndex != 0){
19529 this.select(this.selectedIndex-1);
19535 onKeyUp : function(e){
19536 if(this.editable !== false && !e.isSpecialKey()){
19537 this.lastKey = e.getKey();
19538 this.dqTask.delay(this.queryDelay);
19543 validateBlur : function(){
19544 return !this.list || !this.list.isVisible();
19548 initQuery : function(){
19549 this.doQuery(this.getRawValue());
19553 doForce : function(){
19554 if(this.el.dom.value.length > 0){
19555 this.el.dom.value =
19556 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19562 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
19563 * query allowing the query action to be canceled if needed.
19564 * @param {String} query The SQL query to execute
19565 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19566 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
19567 * saved in the current store (defaults to false)
19569 doQuery : function(q, forceAll){
19570 if(q === undefined || q === null){
19575 forceAll: forceAll,
19579 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19583 forceAll = qe.forceAll;
19584 if(forceAll === true || (q.length >= this.minChars)){
19585 if(this.lastQuery != q || this.alwaysQuery){
19586 this.lastQuery = q;
19587 if(this.mode == 'local'){
19588 this.selectedIndex = -1;
19590 this.store.clearFilter();
19592 this.store.filter(this.displayField, q);
19596 this.store.baseParams[this.queryParam] = q;
19598 params: this.getParams(q)
19603 this.selectedIndex = -1;
19610 getParams : function(q){
19612 //p[this.queryParam] = q;
19615 p.limit = this.pageSize;
19621 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19623 collapse : function(){
19624 if(!this.isExpanded()){
19628 Roo.get(document).un('mousedown', this.collapseIf, this);
19629 Roo.get(document).un('mousewheel', this.collapseIf, this);
19630 if (!this.editable) {
19631 Roo.get(document).un('keydown', this.listKeyPress, this);
19633 this.fireEvent('collapse', this);
19637 collapseIf : function(e){
19638 if(!e.within(this.wrap) && !e.within(this.list)){
19644 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19646 expand : function(){
19647 if(this.isExpanded() || !this.hasFocus){
19650 this.list.alignTo(this.el, this.listAlign);
19652 Roo.get(document).on('mousedown', this.collapseIf, this);
19653 Roo.get(document).on('mousewheel', this.collapseIf, this);
19654 if (!this.editable) {
19655 Roo.get(document).on('keydown', this.listKeyPress, this);
19658 this.fireEvent('expand', this);
19662 // Implements the default empty TriggerField.onTriggerClick function
19663 onTriggerClick : function(){
19667 if(this.isExpanded()){
19669 if (!this.blockFocus) {
19674 this.hasFocus = true;
19675 if(this.triggerAction == 'all') {
19676 this.doQuery(this.allQuery, true);
19678 this.doQuery(this.getRawValue());
19680 if (!this.blockFocus) {
19685 listKeyPress : function(e)
19687 //Roo.log('listkeypress');
19688 // scroll to first matching element based on key pres..
19689 if (e.isSpecialKey()) {
19692 var k = String.fromCharCode(e.getKey()).toUpperCase();
19695 var csel = this.view.getSelectedNodes();
19696 var cselitem = false;
19698 var ix = this.view.indexOf(csel[0]);
19699 cselitem = this.store.getAt(ix);
19700 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19706 this.store.each(function(v) {
19708 // start at existing selection.
19709 if (cselitem.id == v.id) {
19715 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19716 match = this.store.indexOf(v);
19721 if (match === false) {
19722 return true; // no more action?
19725 this.view.select(match);
19726 var sn = Roo.get(this.view.getSelectedNodes()[0]);
19727 sn.scrollIntoView(sn.dom.parentNode, false);
19731 * @cfg {Boolean} grow
19735 * @cfg {Number} growMin
19739 * @cfg {Number} growMax
19747 * Copyright(c) 2010-2012, Roo J Solutions Limited
19754 * @class Roo.form.ComboBoxArray
19755 * @extends Roo.form.TextField
19756 * A facebook style adder... for lists of email / people / countries etc...
19757 * pick multiple items from a combo box, and shows each one.
19759 * Fred [x] Brian [x] [Pick another |v]
19762 * For this to work: it needs various extra information
19763 * - normal combo problay has
19765 * + displayField, valueField
19767 * For our purpose...
19770 * If we change from 'extends' to wrapping...
19777 * Create a new ComboBoxArray.
19778 * @param {Object} config Configuration options
19782 Roo.form.ComboBoxArray = function(config)
19786 * @event beforeremove
19787 * Fires before remove the value from the list
19788 * @param {Roo.form.ComboBoxArray} _self This combo box array
19789 * @param {Roo.form.ComboBoxArray.Item} item removed item
19791 'beforeremove' : true,
19794 * Fires when remove the value from the list
19795 * @param {Roo.form.ComboBoxArray} _self This combo box array
19796 * @param {Roo.form.ComboBoxArray.Item} item removed item
19803 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19805 this.items = new Roo.util.MixedCollection(false);
19807 // construct the child combo...
19817 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19820 * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19825 // behavies liek a hiddne field
19826 inputType: 'hidden',
19828 * @cfg {Number} width The width of the box that displays the selected element
19835 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
19839 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
19841 hiddenName : false,
19844 // private the array of items that are displayed..
19846 // private - the hidden field el.
19848 // private - the filed el..
19851 //validateValue : function() { return true; }, // all values are ok!
19852 //onAddClick: function() { },
19854 onRender : function(ct, position)
19857 // create the standard hidden element
19858 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19861 // give fake names to child combo;
19862 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19863 this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19865 this.combo = Roo.factory(this.combo, Roo.form);
19866 this.combo.onRender(ct, position);
19867 if (typeof(this.combo.width) != 'undefined') {
19868 this.combo.onResize(this.combo.width,0);
19871 this.combo.initEvents();
19873 // assigned so form know we need to do this..
19874 this.store = this.combo.store;
19875 this.valueField = this.combo.valueField;
19876 this.displayField = this.combo.displayField ;
19879 this.combo.wrap.addClass('x-cbarray-grp');
19881 var cbwrap = this.combo.wrap.createChild(
19882 {tag: 'div', cls: 'x-cbarray-cb'},
19887 this.hiddenEl = this.combo.wrap.createChild({
19888 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
19890 this.el = this.combo.wrap.createChild({
19891 tag: 'input', type:'hidden' , name: this.name, value : ''
19893 // this.el.dom.removeAttribute("name");
19896 this.outerWrap = this.combo.wrap;
19897 this.wrap = cbwrap;
19899 this.outerWrap.setWidth(this.width);
19900 this.outerWrap.dom.removeChild(this.el.dom);
19902 this.wrap.dom.appendChild(this.el.dom);
19903 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19904 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19906 this.combo.trigger.setStyle('position','relative');
19907 this.combo.trigger.setStyle('left', '0px');
19908 this.combo.trigger.setStyle('top', '2px');
19910 this.combo.el.setStyle('vertical-align', 'text-bottom');
19912 //this.trigger.setStyle('vertical-align', 'top');
19914 // this should use the code from combo really... on('add' ....)
19918 this.adder = this.outerWrap.createChild(
19919 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
19921 this.adder.on('click', function(e) {
19922 _t.fireEvent('adderclick', this, e);
19926 //this.adder.on('click', this.onAddClick, _t);
19929 this.combo.on('select', function(cb, rec, ix) {
19930 this.addItem(rec.data);
19933 cb.el.dom.value = '';
19934 //cb.lastData = rec.data;
19943 getName: function()
19945 // returns hidden if it's set..
19946 if (!this.rendered) {return ''};
19947 return this.hiddenName ? this.hiddenName : this.name;
19952 onResize: function(w, h){
19955 // not sure if this is needed..
19956 //this.combo.onResize(w,h);
19958 if(typeof w != 'number'){
19959 // we do not handle it!?!?
19962 var tw = this.combo.trigger.getWidth();
19963 tw += this.addicon ? this.addicon.getWidth() : 0;
19964 tw += this.editicon ? this.editicon.getWidth() : 0;
19966 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19968 this.combo.trigger.setStyle('left', '0px');
19970 if(this.list && this.listWidth === undefined){
19971 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19972 this.list.setWidth(lw);
19973 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19980 addItem: function(rec)
19982 var valueField = this.combo.valueField;
19983 var displayField = this.combo.displayField;
19985 if (this.items.indexOfKey(rec[valueField]) > -1) {
19986 //console.log("GOT " + rec.data.id);
19990 var x = new Roo.form.ComboBoxArray.Item({
19991 //id : rec[this.idField],
19993 displayField : displayField ,
19994 tipField : displayField ,
19998 this.items.add(rec[valueField],x);
19999 // add it before the element..
20000 this.updateHiddenEl();
20001 x.render(this.outerWrap, this.wrap.dom);
20002 // add the image handler..
20005 updateHiddenEl : function()
20008 if (!this.hiddenEl) {
20012 var idField = this.combo.valueField;
20014 this.items.each(function(f) {
20015 ar.push(f.data[idField]);
20017 this.hiddenEl.dom.value = ar.join(',');
20023 this.items.clear();
20025 Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
20029 this.el.dom.value = '';
20030 if (this.hiddenEl) {
20031 this.hiddenEl.dom.value = '';
20035 getValue: function()
20037 return this.hiddenEl ? this.hiddenEl.dom.value : '';
20039 setValue: function(v) // not a valid action - must use addItems..
20044 if (this.store.isLocal && (typeof(v) == 'string')) {
20045 // then we can use the store to find the values..
20046 // comma seperated at present.. this needs to allow JSON based encoding..
20047 this.hiddenEl.value = v;
20049 Roo.each(v.split(','), function(k) {
20050 Roo.log("CHECK " + this.valueField + ',' + k);
20051 var li = this.store.query(this.valueField, k);
20056 add[this.valueField] = k;
20057 add[this.displayField] = li.item(0).data[this.displayField];
20063 if (typeof(v) == 'object' ) {
20064 // then let's assume it's an array of objects..
20065 Roo.each(v, function(l) {
20073 setFromData: function(v)
20075 // this recieves an object, if setValues is called.
20077 this.el.dom.value = v[this.displayField];
20078 this.hiddenEl.dom.value = v[this.valueField];
20079 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
20082 var kv = v[this.valueField];
20083 var dv = v[this.displayField];
20084 kv = typeof(kv) != 'string' ? '' : kv;
20085 dv = typeof(dv) != 'string' ? '' : dv;
20088 var keys = kv.split(',');
20089 var display = dv.split(',');
20090 for (var i = 0 ; i < keys.length; i++) {
20093 add[this.valueField] = keys[i];
20094 add[this.displayField] = display[i];
20102 * Validates the combox array value
20103 * @return {Boolean} True if the value is valid, else false
20105 validate : function(){
20106 if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
20107 this.clearInvalid();
20113 validateValue : function(value){
20114 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
20122 isDirty : function() {
20123 if(this.disabled) {
20128 var d = Roo.decode(String(this.originalValue));
20130 return String(this.getValue()) !== String(this.originalValue);
20133 var originalValue = [];
20135 for (var i = 0; i < d.length; i++){
20136 originalValue.push(d[i][this.valueField]);
20139 return String(this.getValue()) !== String(originalValue.join(','));
20148 * @class Roo.form.ComboBoxArray.Item
20149 * @extends Roo.BoxComponent
20150 * A selected item in the list
20151 * Fred [x] Brian [x] [Pick another |v]
20154 * Create a new item.
20155 * @param {Object} config Configuration options
20158 Roo.form.ComboBoxArray.Item = function(config) {
20159 config.id = Roo.id();
20160 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20163 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20166 displayField : false,
20170 defaultAutoCreate : {
20172 cls: 'x-cbarray-item',
20179 src : Roo.BLANK_IMAGE_URL ,
20187 onRender : function(ct, position)
20189 Roo.form.Field.superclass.onRender.call(this, ct, position);
20192 var cfg = this.getAutoCreate();
20193 this.el = ct.createChild(cfg, position);
20196 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20198 this.el.child('div').dom.innerHTML = this.cb.renderer ?
20199 this.cb.renderer(this.data) :
20200 String.format('{0}',this.data[this.displayField]);
20203 this.el.child('div').dom.setAttribute('qtip',
20204 String.format('{0}',this.data[this.tipField])
20207 this.el.child('img').on('click', this.remove, this);
20211 remove : function()
20213 if(this.cb.disabled){
20217 if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20218 this.cb.items.remove(this);
20219 this.el.child('img').un('click', this.remove, this);
20221 this.cb.updateHiddenEl();
20223 this.cb.fireEvent('remove', this.cb, this);
20228 * RooJS Library 1.1.1
20229 * Copyright(c) 2008-2011 Alan Knowles
20236 * @class Roo.form.ComboNested
20237 * @extends Roo.form.ComboBox
20238 * A combobox for that allows selection of nested items in a list,
20253 * Create a new ComboNested
20254 * @param {Object} config Configuration options
20256 Roo.form.ComboNested = function(config){
20257 Roo.form.ComboCheck.superclass.constructor.call(this, config);
20258 // should verify some data...
20260 // hiddenName = required..
20261 // displayField = required
20262 // valudField == required
20263 var req= [ 'hiddenName', 'displayField', 'valueField' ];
20265 Roo.each(req, function(e) {
20266 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
20267 throw "Roo.form.ComboNested : missing value for: " + e;
20274 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
20277 * @config {Number} max Number of columns to show
20282 list : null, // the outermost div..
20283 innerLists : null, // the
20287 onRender : function(ct, position)
20289 Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
20291 if(this.hiddenName){
20292 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
20294 this.hiddenField.value =
20295 this.hiddenValue !== undefined ? this.hiddenValue :
20296 this.value !== undefined ? this.value : '';
20298 // prevent input submission
20299 this.el.dom.removeAttribute('name');
20305 this.el.dom.setAttribute('autocomplete', 'off');
20308 var cls = 'x-combo-list';
20310 this.list = new Roo.Layer({
20311 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
20314 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
20315 this.list.setWidth(lw);
20316 this.list.swallowEvent('mousewheel');
20317 this.assetHeight = 0;
20320 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
20321 this.assetHeight += this.header.getHeight();
20323 this.innerLists = [];
20326 for (var i =0 ; i < this.maxColumns; i++) {
20327 this.onRenderList( cls, i);
20330 // always needs footer, as we are going to have an 'OK' button.
20331 this.footer = this.list.createChild({cls:cls+'-ft'});
20332 this.pageTb = new Roo.Toolbar(this.footer);
20337 handler: function()
20343 if ( this.allowBlank && !this.disableClear) {
20345 this.pageTb.add(new Roo.Toolbar.Fill(), {
20346 cls: 'x-btn-icon x-btn-clear',
20348 handler: function()
20351 _this.clearValue();
20352 _this.onSelect(false, -1);
20357 this.assetHeight += this.footer.getHeight();
20361 onRenderList : function ( cls, i)
20364 var lw = Math.floor(
20365 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20368 this.list.setWidth(lw); // default to '1'
20370 var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20371 //il.on('mouseover', this.onViewOver, this, { list: i });
20372 //il.on('mousemove', this.onViewMove, this, { list: i });
20374 il.setStyle({ 'overflow-x' : 'hidden'});
20377 this.tpl = new Roo.Template({
20378 html : '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20379 isEmpty: function (value, allValues) {
20381 var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20382 return dl ? 'has-children' : 'no-children'
20387 var store = this.store;
20389 store = new Roo.data.SimpleStore({
20390 //fields : this.store.reader.meta.fields,
20391 reader : this.store.reader,
20395 this.stores[i] = store;
20399 var view = this.views[i] = new Roo.View(
20405 selectedClass: this.selectedClass
20408 view.getEl().setWidth(lw);
20409 view.getEl().setStyle({
20410 position: i < 1 ? 'relative' : 'absolute',
20412 left: (i * lw ) + 'px',
20413 display : i > 0 ? 'none' : 'block'
20415 view.on('selectionchange', this.onSelectChange, this, {list : i });
20416 view.on('dblclick', this.onDoubleClick, this, {list : i });
20417 //view.on('click', this.onViewClick, this, { list : i });
20419 store.on('beforeload', this.onBeforeLoad, this);
20420 store.on('load', this.onLoad, this, { list : i});
20421 store.on('loadexception', this.onLoadException, this);
20423 // hide the other vies..
20428 onResize : function() {},
20430 restrictHeight : function()
20433 Roo.each(this.innerLists, function(il,i) {
20434 var el = this.views[i].getEl();
20435 el.dom.style.height = '';
20436 var inner = el.dom;
20437 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
20438 // only adjust heights on other ones..
20441 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20442 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20443 mh = Math.max(el.getHeight(), mh);
20449 this.list.beginUpdate();
20450 this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20451 this.list.alignTo(this.el, this.listAlign);
20452 this.list.endUpdate();
20457 // -- store handlers..
20459 onBeforeLoad : function()
20461 if(!this.hasFocus){
20464 this.innerLists[0].update(this.loadingText ?
20465 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20466 this.restrictHeight();
20467 this.selectedIndex = -1;
20470 onLoad : function(a,b,c,d)
20473 if(!this.hasFocus){
20477 if(this.store.getCount() > 0) {
20479 this.restrictHeight();
20481 this.onEmptyResults();
20484 this.stores[1].loadData([]);
20485 this.stores[2].loadData([]);
20494 onLoadException : function()
20497 Roo.log(this.store.reader.jsonData);
20498 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20499 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20507 onSelectChange : function (view, sels, opts )
20509 var ix = view.getSelectedIndexes();
20512 if (opts.list > this.maxColumns - 2) {
20514 this.setFromData(ix.length ? view.store.getAt(ix[0]).data : {});
20519 this.setFromData({});
20520 var str = this.stores[opts.list+1];
20525 var rec = view.store.getAt(ix[0]);
20526 this.setFromData(rec.data);
20528 var lw = Math.floor(
20529 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20532 this.stores[opts.list+1].loadDataFromChildren( rec );
20533 var dl = this.stores[opts.list+1]. getTotalCount();
20534 this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20535 this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20536 this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20537 this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20539 onDoubleClick : function()
20541 this.collapse(); //??
20546 findRecord : function (prop,value)
20548 return this.findRecordInStore(this.store, prop,value);
20552 findRecordInStore : function(store, prop, value)
20554 var cstore = new Roo.data.SimpleStore({
20555 //fields : this.store.reader.meta.fields, // we need array reader.. for
20556 reader : this.store.reader,
20560 var record = false;
20561 if(store.getCount() > 0){
20562 store.each(function(r){
20563 if(r.data[prop] == value){
20567 if (r.data.cn && r.data.cn.length) {
20568 cstore.loadDataFromChildren( r);
20569 var cret = _this.findRecordInStore(cstore, prop, value);
20570 if (cret !== false) {
20587 * Ext JS Library 1.1.1
20588 * Copyright(c) 2006-2007, Ext JS, LLC.
20590 * Originally Released Under LGPL - original licence link has changed is not relivant.
20593 * <script type="text/javascript">
20596 * @class Roo.form.Checkbox
20597 * @extends Roo.form.Field
20598 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
20600 * Creates a new Checkbox
20601 * @param {Object} config Configuration options
20603 Roo.form.Checkbox = function(config){
20604 Roo.form.Checkbox.superclass.constructor.call(this, config);
20608 * Fires when the checkbox is checked or unchecked.
20609 * @param {Roo.form.Checkbox} this This checkbox
20610 * @param {Boolean} checked The new checked value
20616 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
20618 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20620 focusClass : undefined,
20622 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20624 fieldClass: "x-form-field",
20626 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20630 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20631 * {tag: "input", type: "checkbox", autocomplete: "off"})
20633 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20635 * @cfg {String} boxLabel The text that appears beside the checkbox
20639 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20643 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20645 valueOff: '0', // value when not checked..
20647 actionMode : 'viewEl',
20650 itemCls : 'x-menu-check-item x-form-item',
20651 groupClass : 'x-menu-group-item',
20652 inputType : 'hidden',
20655 inSetChecked: false, // check that we are not calling self...
20657 inputElement: false, // real input element?
20658 basedOn: false, // ????
20660 isFormField: true, // not sure where this is needed!!!!
20662 onResize : function(){
20663 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20664 if(!this.boxLabel){
20665 this.el.alignTo(this.wrap, 'c-c');
20669 initEvents : function(){
20670 Roo.form.Checkbox.superclass.initEvents.call(this);
20671 this.el.on("click", this.onClick, this);
20672 this.el.on("change", this.onClick, this);
20676 getResizeEl : function(){
20680 getPositionEl : function(){
20685 onRender : function(ct, position){
20686 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20688 if(this.inputValue !== undefined){
20689 this.el.dom.value = this.inputValue;
20692 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20693 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20694 var viewEl = this.wrap.createChild({
20695 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20696 this.viewEl = viewEl;
20697 this.wrap.on('click', this.onClick, this);
20699 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20700 this.el.on('propertychange', this.setFromHidden, this); //ie
20705 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20706 // viewEl.on('click', this.onClick, this);
20708 //if(this.checked){
20709 this.setChecked(this.checked);
20711 //this.checked = this.el.dom;
20717 initValue : Roo.emptyFn,
20720 * Returns the checked state of the checkbox.
20721 * @return {Boolean} True if checked, else false
20723 getValue : function(){
20725 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20727 return this.valueOff;
20732 onClick : function(){
20733 if (this.disabled) {
20736 this.setChecked(!this.checked);
20738 //if(this.el.dom.checked != this.checked){
20739 // this.setValue(this.el.dom.checked);
20744 * Sets the checked state of the checkbox.
20745 * On is always based on a string comparison between inputValue and the param.
20746 * @param {Boolean/String} value - the value to set
20747 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20749 setValue : function(v,suppressEvent){
20752 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20753 //if(this.el && this.el.dom){
20754 // this.el.dom.checked = this.checked;
20755 // this.el.dom.defaultChecked = this.checked;
20757 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20758 //this.fireEvent("check", this, this.checked);
20761 setChecked : function(state,suppressEvent)
20763 if (this.inSetChecked) {
20764 this.checked = state;
20770 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20772 this.checked = state;
20773 if(suppressEvent !== true){
20774 this.fireEvent('check', this, state);
20776 this.inSetChecked = true;
20777 this.el.dom.value = state ? this.inputValue : this.valueOff;
20778 this.inSetChecked = false;
20781 // handle setting of hidden value by some other method!!?!?
20782 setFromHidden: function()
20787 //console.log("SET FROM HIDDEN");
20788 //alert('setFrom hidden');
20789 this.setValue(this.el.dom.value);
20792 onDestroy : function()
20795 Roo.get(this.viewEl).remove();
20798 Roo.form.Checkbox.superclass.onDestroy.call(this);
20801 setBoxLabel : function(str)
20803 this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20808 * Ext JS Library 1.1.1
20809 * Copyright(c) 2006-2007, Ext JS, LLC.
20811 * Originally Released Under LGPL - original licence link has changed is not relivant.
20814 * <script type="text/javascript">
20818 * @class Roo.form.Radio
20819 * @extends Roo.form.Checkbox
20820 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
20821 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20823 * Creates a new Radio
20824 * @param {Object} config Configuration options
20826 Roo.form.Radio = function(){
20827 Roo.form.Radio.superclass.constructor.apply(this, arguments);
20829 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20830 inputType: 'radio',
20833 * If this radio is part of a group, it will return the selected value
20836 getGroupValue : function(){
20837 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20841 onRender : function(ct, position){
20842 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20844 if(this.inputValue !== undefined){
20845 this.el.dom.value = this.inputValue;
20848 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20849 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20850 //var viewEl = this.wrap.createChild({
20851 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20852 //this.viewEl = viewEl;
20853 //this.wrap.on('click', this.onClick, this);
20855 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20856 //this.el.on('propertychange', this.setFromHidden, this); //ie
20861 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20862 // viewEl.on('click', this.onClick, this);
20865 this.el.dom.checked = 'checked' ;
20871 });//<script type="text/javascript">
20874 * Based Ext JS Library 1.1.1
20875 * Copyright(c) 2006-2007, Ext JS, LLC.
20881 * @class Roo.HtmlEditorCore
20882 * @extends Roo.Component
20883 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20885 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20888 Roo.HtmlEditorCore = function(config){
20891 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20896 * @event initialize
20897 * Fires when the editor is fully initialized (including the iframe)
20898 * @param {Roo.HtmlEditorCore} this
20903 * Fires when the editor is first receives the focus. Any insertion must wait
20904 * until after this event.
20905 * @param {Roo.HtmlEditorCore} this
20909 * @event beforesync
20910 * Fires before the textarea is updated with content from the editor iframe. Return false
20911 * to cancel the sync.
20912 * @param {Roo.HtmlEditorCore} this
20913 * @param {String} html
20917 * @event beforepush
20918 * Fires before the iframe editor is updated with content from the textarea. Return false
20919 * to cancel the push.
20920 * @param {Roo.HtmlEditorCore} this
20921 * @param {String} html
20926 * Fires when the textarea is updated with content from the editor iframe.
20927 * @param {Roo.HtmlEditorCore} this
20928 * @param {String} html
20933 * Fires when the iframe editor is updated with content from the textarea.
20934 * @param {Roo.HtmlEditorCore} this
20935 * @param {String} html
20940 * @event editorevent
20941 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20942 * @param {Roo.HtmlEditorCore} this
20948 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20950 // defaults : white / black...
20951 this.applyBlacklists();
20958 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20962 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20968 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20973 * @cfg {Number} height (in pixels)
20977 * @cfg {Number} width (in pixels)
20982 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20985 stylesheets: false,
20990 // private properties
20991 validationEvent : false,
20993 initialized : false,
20995 sourceEditMode : false,
20996 onFocus : Roo.emptyFn,
20998 hideMode:'offsets',
21002 // blacklist + whitelisted elements..
21009 * Protected method that will not generally be called directly. It
21010 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21011 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21013 getDocMarkup : function(){
21017 // inherit styels from page...??
21018 if (this.stylesheets === false) {
21020 Roo.get(document.head).select('style').each(function(node) {
21021 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21024 Roo.get(document.head).select('link').each(function(node) {
21025 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21028 } else if (!this.stylesheets.length) {
21030 st = '<style type="text/css">' +
21031 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21034 st = '<style type="text/css">' +
21039 st += '<style type="text/css">' +
21040 'IMG { cursor: pointer } ' +
21043 var cls = 'roo-htmleditor-body';
21045 if(this.bodyCls.length){
21046 cls += ' ' + this.bodyCls;
21049 return '<html><head>' + st +
21050 //<style type="text/css">' +
21051 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21053 ' </head><body class="' + cls + '"></body></html>';
21057 onRender : function(ct, position)
21060 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21061 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21064 this.el.dom.style.border = '0 none';
21065 this.el.dom.setAttribute('tabIndex', -1);
21066 this.el.addClass('x-hidden hide');
21070 if(Roo.isIE){ // fix IE 1px bogus margin
21071 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21075 this.frameId = Roo.id();
21079 var iframe = this.owner.wrap.createChild({
21081 cls: 'form-control', // bootstrap..
21083 name: this.frameId,
21084 frameBorder : 'no',
21085 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21090 this.iframe = iframe.dom;
21092 this.assignDocWin();
21094 this.doc.designMode = 'on';
21097 this.doc.write(this.getDocMarkup());
21101 var task = { // must defer to wait for browser to be ready
21103 //console.log("run task?" + this.doc.readyState);
21104 this.assignDocWin();
21105 if(this.doc.body || this.doc.readyState == 'complete'){
21107 this.doc.designMode="on";
21111 Roo.TaskMgr.stop(task);
21112 this.initEditor.defer(10, this);
21119 Roo.TaskMgr.start(task);
21124 onResize : function(w, h)
21126 Roo.log('resize: ' +w + ',' + h );
21127 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21131 if(typeof w == 'number'){
21133 this.iframe.style.width = w + 'px';
21135 if(typeof h == 'number'){
21137 this.iframe.style.height = h + 'px';
21139 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21146 * Toggles the editor between standard and source edit mode.
21147 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21149 toggleSourceEdit : function(sourceEditMode){
21151 this.sourceEditMode = sourceEditMode === true;
21153 if(this.sourceEditMode){
21155 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21158 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21159 //this.iframe.className = '';
21162 //this.setSize(this.owner.wrap.getSize());
21163 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21170 * Protected method that will not generally be called directly. If you need/want
21171 * custom HTML cleanup, this is the method you should override.
21172 * @param {String} html The HTML to be cleaned
21173 * return {String} The cleaned HTML
21175 cleanHtml : function(html){
21176 html = String(html);
21177 if(html.length > 5){
21178 if(Roo.isSafari){ // strip safari nonsense
21179 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21182 if(html == ' '){
21189 * HTML Editor -> Textarea
21190 * Protected method that will not generally be called directly. Syncs the contents
21191 * of the editor iframe with the textarea.
21193 syncValue : function(){
21194 if(this.initialized){
21195 var bd = (this.doc.body || this.doc.documentElement);
21196 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21197 var html = bd.innerHTML;
21199 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21200 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21202 html = '<div style="'+m[0]+'">' + html + '</div>';
21205 html = this.cleanHtml(html);
21206 // fix up the special chars.. normaly like back quotes in word...
21207 // however we do not want to do this with chinese..
21208 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
21210 var cc = match.charCodeAt();
21212 // Get the character value, handling surrogate pairs
21213 if (match.length == 2) {
21214 // It's a surrogate pair, calculate the Unicode code point
21215 var high = match.charCodeAt(0) - 0xD800;
21216 var low = match.charCodeAt(1) - 0xDC00;
21217 cc = (high * 0x400) + low + 0x10000;
21219 (cc >= 0x4E00 && cc < 0xA000 ) ||
21220 (cc >= 0x3400 && cc < 0x4E00 ) ||
21221 (cc >= 0xf900 && cc < 0xfb00 )
21226 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
21227 return "&#" + cc + ";";
21234 if(this.owner.fireEvent('beforesync', this, html) !== false){
21235 this.el.dom.value = html;
21236 this.owner.fireEvent('sync', this, html);
21242 * Protected method that will not generally be called directly. Pushes the value of the textarea
21243 * into the iframe editor.
21245 pushValue : function(){
21246 if(this.initialized){
21247 var v = this.el.dom.value.trim();
21249 // if(v.length < 1){
21253 if(this.owner.fireEvent('beforepush', this, v) !== false){
21254 var d = (this.doc.body || this.doc.documentElement);
21256 this.cleanUpPaste();
21257 this.el.dom.value = d.innerHTML;
21258 this.owner.fireEvent('push', this, v);
21264 deferFocus : function(){
21265 this.focus.defer(10, this);
21269 focus : function(){
21270 if(this.win && !this.sourceEditMode){
21277 assignDocWin: function()
21279 var iframe = this.iframe;
21282 this.doc = iframe.contentWindow.document;
21283 this.win = iframe.contentWindow;
21285 // if (!Roo.get(this.frameId)) {
21288 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21289 // this.win = Roo.get(this.frameId).dom.contentWindow;
21291 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21295 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21296 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21301 initEditor : function(){
21302 //console.log("INIT EDITOR");
21303 this.assignDocWin();
21307 this.doc.designMode="on";
21309 this.doc.write(this.getDocMarkup());
21312 var dbody = (this.doc.body || this.doc.documentElement);
21313 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21314 // this copies styles from the containing element into thsi one..
21315 // not sure why we need all of this..
21316 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21318 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21319 //ss['background-attachment'] = 'fixed'; // w3c
21320 dbody.bgProperties = 'fixed'; // ie
21321 //Roo.DomHelper.applyStyles(dbody, ss);
21322 Roo.EventManager.on(this.doc, {
21323 //'mousedown': this.onEditorEvent,
21324 'mouseup': this.onEditorEvent,
21325 'dblclick': this.onEditorEvent,
21326 'click': this.onEditorEvent,
21327 'keyup': this.onEditorEvent,
21332 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21334 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21335 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21337 this.initialized = true;
21339 this.owner.fireEvent('initialize', this);
21344 onDestroy : function(){
21350 //for (var i =0; i < this.toolbars.length;i++) {
21351 // // fixme - ask toolbars for heights?
21352 // this.toolbars[i].onDestroy();
21355 //this.wrap.dom.innerHTML = '';
21356 //this.wrap.remove();
21361 onFirstFocus : function(){
21363 this.assignDocWin();
21366 this.activated = true;
21369 if(Roo.isGecko){ // prevent silly gecko errors
21371 var s = this.win.getSelection();
21372 if(!s.focusNode || s.focusNode.nodeType != 3){
21373 var r = s.getRangeAt(0);
21374 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21379 this.execCmd('useCSS', true);
21380 this.execCmd('styleWithCSS', false);
21383 this.owner.fireEvent('activate', this);
21387 adjustFont: function(btn){
21388 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21389 //if(Roo.isSafari){ // safari
21392 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21393 if(Roo.isSafari){ // safari
21394 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21395 v = (v < 10) ? 10 : v;
21396 v = (v > 48) ? 48 : v;
21397 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21402 v = Math.max(1, v+adjust);
21404 this.execCmd('FontSize', v );
21407 onEditorEvent : function(e)
21409 this.owner.fireEvent('editorevent', this, e);
21410 // this.updateToolbar();
21411 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21414 insertTag : function(tg)
21416 // could be a bit smarter... -> wrap the current selected tRoo..
21417 if (tg.toLowerCase() == 'span' ||
21418 tg.toLowerCase() == 'code' ||
21419 tg.toLowerCase() == 'sup' ||
21420 tg.toLowerCase() == 'sub'
21423 range = this.createRange(this.getSelection());
21424 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21425 wrappingNode.appendChild(range.extractContents());
21426 range.insertNode(wrappingNode);
21433 this.execCmd("formatblock", tg);
21437 insertText : function(txt)
21441 var range = this.createRange();
21442 range.deleteContents();
21443 //alert(Sender.getAttribute('label'));
21445 range.insertNode(this.doc.createTextNode(txt));
21451 * Executes a Midas editor command on the editor document and performs necessary focus and
21452 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21453 * @param {String} cmd The Midas command
21454 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21456 relayCmd : function(cmd, value){
21458 this.execCmd(cmd, value);
21459 this.owner.fireEvent('editorevent', this);
21460 //this.updateToolbar();
21461 this.owner.deferFocus();
21465 * Executes a Midas editor command directly on the editor document.
21466 * For visual commands, you should use {@link #relayCmd} instead.
21467 * <b>This should only be called after the editor is initialized.</b>
21468 * @param {String} cmd The Midas command
21469 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21471 execCmd : function(cmd, value){
21472 this.doc.execCommand(cmd, false, value === undefined ? null : value);
21479 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21481 * @param {String} text | dom node..
21483 insertAtCursor : function(text)
21486 if(!this.activated){
21492 var r = this.doc.selection.createRange();
21503 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21507 // from jquery ui (MIT licenced)
21509 var win = this.win;
21511 if (win.getSelection && win.getSelection().getRangeAt) {
21512 range = win.getSelection().getRangeAt(0);
21513 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21514 range.insertNode(node);
21515 } else if (win.document.selection && win.document.selection.createRange) {
21516 // no firefox support
21517 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21518 win.document.selection.createRange().pasteHTML(txt);
21520 // no firefox support
21521 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21522 this.execCmd('InsertHTML', txt);
21531 mozKeyPress : function(e){
21533 var c = e.getCharCode(), cmd;
21536 c = String.fromCharCode(c).toLowerCase();
21550 this.cleanUpPaste.defer(100, this);
21558 e.preventDefault();
21566 fixKeys : function(){ // load time branching for fastest keydown performance
21568 return function(e){
21569 var k = e.getKey(), r;
21572 r = this.doc.selection.createRange();
21575 r.pasteHTML('    ');
21582 r = this.doc.selection.createRange();
21584 var target = r.parentElement();
21585 if(!target || target.tagName.toLowerCase() != 'li'){
21587 r.pasteHTML('<br />');
21593 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21594 this.cleanUpPaste.defer(100, this);
21600 }else if(Roo.isOpera){
21601 return function(e){
21602 var k = e.getKey();
21606 this.execCmd('InsertHTML','    ');
21609 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21610 this.cleanUpPaste.defer(100, this);
21615 }else if(Roo.isSafari){
21616 return function(e){
21617 var k = e.getKey();
21621 this.execCmd('InsertText','\t');
21625 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21626 this.cleanUpPaste.defer(100, this);
21634 getAllAncestors: function()
21636 var p = this.getSelectedNode();
21639 a.push(p); // push blank onto stack..
21640 p = this.getParentElement();
21644 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21648 a.push(this.doc.body);
21652 lastSelNode : false,
21655 getSelection : function()
21657 this.assignDocWin();
21658 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21661 getSelectedNode: function()
21663 // this may only work on Gecko!!!
21665 // should we cache this!!!!
21670 var range = this.createRange(this.getSelection()).cloneRange();
21673 var parent = range.parentElement();
21675 var testRange = range.duplicate();
21676 testRange.moveToElementText(parent);
21677 if (testRange.inRange(range)) {
21680 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21683 parent = parent.parentElement;
21688 // is ancestor a text element.
21689 var ac = range.commonAncestorContainer;
21690 if (ac.nodeType == 3) {
21691 ac = ac.parentNode;
21694 var ar = ac.childNodes;
21697 var other_nodes = [];
21698 var has_other_nodes = false;
21699 for (var i=0;i<ar.length;i++) {
21700 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21703 // fullly contained node.
21705 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21710 // probably selected..
21711 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21712 other_nodes.push(ar[i]);
21716 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21721 has_other_nodes = true;
21723 if (!nodes.length && other_nodes.length) {
21724 nodes= other_nodes;
21726 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21732 createRange: function(sel)
21734 // this has strange effects when using with
21735 // top toolbar - not sure if it's a great idea.
21736 //this.editor.contentWindow.focus();
21737 if (typeof sel != "undefined") {
21739 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21741 return this.doc.createRange();
21744 return this.doc.createRange();
21747 getParentElement: function()
21750 this.assignDocWin();
21751 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21753 var range = this.createRange(sel);
21756 var p = range.commonAncestorContainer;
21757 while (p.nodeType == 3) { // text node
21768 * Range intersection.. the hard stuff...
21772 * [ -- selected range --- ]
21776 * if end is before start or hits it. fail.
21777 * if start is after end or hits it fail.
21779 * if either hits (but other is outside. - then it's not
21785 // @see http://www.thismuchiknow.co.uk/?p=64.
21786 rangeIntersectsNode : function(range, node)
21788 var nodeRange = node.ownerDocument.createRange();
21790 nodeRange.selectNode(node);
21792 nodeRange.selectNodeContents(node);
21795 var rangeStartRange = range.cloneRange();
21796 rangeStartRange.collapse(true);
21798 var rangeEndRange = range.cloneRange();
21799 rangeEndRange.collapse(false);
21801 var nodeStartRange = nodeRange.cloneRange();
21802 nodeStartRange.collapse(true);
21804 var nodeEndRange = nodeRange.cloneRange();
21805 nodeEndRange.collapse(false);
21807 return rangeStartRange.compareBoundaryPoints(
21808 Range.START_TO_START, nodeEndRange) == -1 &&
21809 rangeEndRange.compareBoundaryPoints(
21810 Range.START_TO_START, nodeStartRange) == 1;
21814 rangeCompareNode : function(range, node)
21816 var nodeRange = node.ownerDocument.createRange();
21818 nodeRange.selectNode(node);
21820 nodeRange.selectNodeContents(node);
21824 range.collapse(true);
21826 nodeRange.collapse(true);
21828 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21829 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21831 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21833 var nodeIsBefore = ss == 1;
21834 var nodeIsAfter = ee == -1;
21836 if (nodeIsBefore && nodeIsAfter) {
21839 if (!nodeIsBefore && nodeIsAfter) {
21840 return 1; //right trailed.
21843 if (nodeIsBefore && !nodeIsAfter) {
21844 return 2; // left trailed.
21850 // private? - in a new class?
21851 cleanUpPaste : function()
21853 // cleans up the whole document..
21854 Roo.log('cleanuppaste');
21856 this.cleanUpChildren(this.doc.body);
21857 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21858 if (clean != this.doc.body.innerHTML) {
21859 this.doc.body.innerHTML = clean;
21864 cleanWordChars : function(input) {// change the chars to hex code
21865 var he = Roo.HtmlEditorCore;
21867 var output = input;
21868 Roo.each(he.swapCodes, function(sw) {
21869 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21871 output = output.replace(swapper, sw[1]);
21878 cleanUpChildren : function (n)
21880 if (!n.childNodes.length) {
21883 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21884 this.cleanUpChild(n.childNodes[i]);
21891 cleanUpChild : function (node)
21894 //console.log(node);
21895 if (node.nodeName == "#text") {
21896 // clean up silly Windows -- stuff?
21899 if (node.nodeName == "#comment") {
21900 node.parentNode.removeChild(node);
21901 // clean up silly Windows -- stuff?
21904 var lcname = node.tagName.toLowerCase();
21905 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21906 // whitelist of tags..
21908 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21910 node.parentNode.removeChild(node);
21915 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21917 // spans with no attributes - just remove them..
21918 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
21919 remove_keep_children = true;
21922 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21923 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21925 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21926 // remove_keep_children = true;
21929 if (remove_keep_children) {
21930 this.cleanUpChildren(node);
21931 // inserts everything just before this node...
21932 while (node.childNodes.length) {
21933 var cn = node.childNodes[0];
21934 node.removeChild(cn);
21935 node.parentNode.insertBefore(cn, node);
21937 node.parentNode.removeChild(node);
21941 if (!node.attributes || !node.attributes.length) {
21946 this.cleanUpChildren(node);
21950 function cleanAttr(n,v)
21953 if (v.match(/^\./) || v.match(/^\//)) {
21956 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21959 if (v.match(/^#/)) {
21962 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21963 node.removeAttribute(n);
21967 var cwhite = this.cwhite;
21968 var cblack = this.cblack;
21970 function cleanStyle(n,v)
21972 if (v.match(/expression/)) { //XSS?? should we even bother..
21973 node.removeAttribute(n);
21977 var parts = v.split(/;/);
21980 Roo.each(parts, function(p) {
21981 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21985 var l = p.split(':').shift().replace(/\s+/g,'');
21986 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21988 if ( cwhite.length && cblack.indexOf(l) > -1) {
21989 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21990 //node.removeAttribute(n);
21994 // only allow 'c whitelisted system attributes'
21995 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21996 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21997 //node.removeAttribute(n);
22007 if (clean.length) {
22008 node.setAttribute(n, clean.join(';'));
22010 node.removeAttribute(n);
22016 for (var i = node.attributes.length-1; i > -1 ; i--) {
22017 var a = node.attributes[i];
22020 if (a.name.toLowerCase().substr(0,2)=='on') {
22021 node.removeAttribute(a.name);
22024 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22025 node.removeAttribute(a.name);
22028 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22029 cleanAttr(a.name,a.value); // fixme..
22032 if (a.name == 'style') {
22033 cleanStyle(a.name,a.value);
22036 /// clean up MS crap..
22037 // tecnically this should be a list of valid class'es..
22040 if (a.name == 'class') {
22041 if (a.value.match(/^Mso/)) {
22042 node.removeAttribute('class');
22045 if (a.value.match(/^body$/)) {
22046 node.removeAttribute('class');
22057 this.cleanUpChildren(node);
22063 * Clean up MS wordisms...
22065 cleanWord : function(node)
22068 this.cleanWord(this.doc.body);
22073 node.nodeName == 'SPAN' &&
22074 !node.hasAttributes() &&
22075 node.childNodes.length == 1 &&
22076 node.firstChild.nodeName == "#text"
22078 var textNode = node.firstChild;
22079 node.removeChild(textNode);
22080 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
22081 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
22083 node.parentNode.insertBefore(textNode, node);
22084 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
22085 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
22087 node.parentNode.removeChild(node);
22090 if (node.nodeName == "#text") {
22091 // clean up silly Windows -- stuff?
22094 if (node.nodeName == "#comment") {
22095 node.parentNode.removeChild(node);
22096 // clean up silly Windows -- stuff?
22100 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22101 node.parentNode.removeChild(node);
22104 //Roo.log(node.tagName);
22105 // remove - but keep children..
22106 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
22107 //Roo.log('-- removed');
22108 while (node.childNodes.length) {
22109 var cn = node.childNodes[0];
22110 node.removeChild(cn);
22111 node.parentNode.insertBefore(cn, node);
22112 // move node to parent - and clean it..
22113 this.cleanWord(cn);
22115 node.parentNode.removeChild(node);
22116 /// no need to iterate chidlren = it's got none..
22117 //this.iterateChildren(node, this.cleanWord);
22121 if (node.className.length) {
22123 var cn = node.className.split(/\W+/);
22125 Roo.each(cn, function(cls) {
22126 if (cls.match(/Mso[a-zA-Z]+/)) {
22131 node.className = cna.length ? cna.join(' ') : '';
22133 node.removeAttribute("class");
22137 if (node.hasAttribute("lang")) {
22138 node.removeAttribute("lang");
22141 if (node.hasAttribute("style")) {
22143 var styles = node.getAttribute("style").split(";");
22145 Roo.each(styles, function(s) {
22146 if (!s.match(/:/)) {
22149 var kv = s.split(":");
22150 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22153 // what ever is left... we allow.
22156 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22157 if (!nstyle.length) {
22158 node.removeAttribute('style');
22161 this.iterateChildren(node, this.cleanWord);
22167 * iterateChildren of a Node, calling fn each time, using this as the scole..
22168 * @param {DomNode} node node to iterate children of.
22169 * @param {Function} fn method of this class to call on each item.
22171 iterateChildren : function(node, fn)
22173 if (!node.childNodes.length) {
22176 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22177 fn.call(this, node.childNodes[i])
22183 * cleanTableWidths.
22185 * Quite often pasting from word etc.. results in tables with column and widths.
22186 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22189 cleanTableWidths : function(node)
22194 this.cleanTableWidths(this.doc.body);
22199 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22202 Roo.log(node.tagName);
22203 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22204 this.iterateChildren(node, this.cleanTableWidths);
22207 if (node.hasAttribute('width')) {
22208 node.removeAttribute('width');
22212 if (node.hasAttribute("style")) {
22215 var styles = node.getAttribute("style").split(";");
22217 Roo.each(styles, function(s) {
22218 if (!s.match(/:/)) {
22221 var kv = s.split(":");
22222 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22225 // what ever is left... we allow.
22228 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22229 if (!nstyle.length) {
22230 node.removeAttribute('style');
22234 this.iterateChildren(node, this.cleanTableWidths);
22242 domToHTML : function(currentElement, depth, nopadtext) {
22244 depth = depth || 0;
22245 nopadtext = nopadtext || false;
22247 if (!currentElement) {
22248 return this.domToHTML(this.doc.body);
22251 //Roo.log(currentElement);
22253 var allText = false;
22254 var nodeName = currentElement.nodeName;
22255 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22257 if (nodeName == '#text') {
22259 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22264 if (nodeName != 'BODY') {
22267 // Prints the node tagName, such as <A>, <IMG>, etc
22270 for(i = 0; i < currentElement.attributes.length;i++) {
22272 var aname = currentElement.attributes.item(i).name;
22273 if (!currentElement.attributes.item(i).value.length) {
22276 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22279 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22288 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22291 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22296 // Traverse the tree
22298 var currentElementChild = currentElement.childNodes.item(i);
22299 var allText = true;
22300 var innerHTML = '';
22302 while (currentElementChild) {
22303 // Formatting code (indent the tree so it looks nice on the screen)
22304 var nopad = nopadtext;
22305 if (lastnode == 'SPAN') {
22309 if (currentElementChild.nodeName == '#text') {
22310 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22311 toadd = nopadtext ? toadd : toadd.trim();
22312 if (!nopad && toadd.length > 80) {
22313 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22315 innerHTML += toadd;
22318 currentElementChild = currentElement.childNodes.item(i);
22324 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22326 // Recursively traverse the tree structure of the child node
22327 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22328 lastnode = currentElementChild.nodeName;
22330 currentElementChild=currentElement.childNodes.item(i);
22336 // The remaining code is mostly for formatting the tree
22337 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22342 ret+= "</"+tagName+">";
22348 applyBlacklists : function()
22350 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22351 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22355 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22356 if (b.indexOf(tag) > -1) {
22359 this.white.push(tag);
22363 Roo.each(w, function(tag) {
22364 if (b.indexOf(tag) > -1) {
22367 if (this.white.indexOf(tag) > -1) {
22370 this.white.push(tag);
22375 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22376 if (w.indexOf(tag) > -1) {
22379 this.black.push(tag);
22383 Roo.each(b, function(tag) {
22384 if (w.indexOf(tag) > -1) {
22387 if (this.black.indexOf(tag) > -1) {
22390 this.black.push(tag);
22395 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22396 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22400 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22401 if (b.indexOf(tag) > -1) {
22404 this.cwhite.push(tag);
22408 Roo.each(w, function(tag) {
22409 if (b.indexOf(tag) > -1) {
22412 if (this.cwhite.indexOf(tag) > -1) {
22415 this.cwhite.push(tag);
22420 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22421 if (w.indexOf(tag) > -1) {
22424 this.cblack.push(tag);
22428 Roo.each(b, function(tag) {
22429 if (w.indexOf(tag) > -1) {
22432 if (this.cblack.indexOf(tag) > -1) {
22435 this.cblack.push(tag);
22440 setStylesheets : function(stylesheets)
22442 if(typeof(stylesheets) == 'string'){
22443 Roo.get(this.iframe.contentDocument.head).createChild({
22445 rel : 'stylesheet',
22454 Roo.each(stylesheets, function(s) {
22459 Roo.get(_this.iframe.contentDocument.head).createChild({
22461 rel : 'stylesheet',
22470 removeStylesheets : function()
22474 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22479 setStyle : function(style)
22481 Roo.get(this.iframe.contentDocument.head).createChild({
22490 // hide stuff that is not compatible
22504 * @event specialkey
22508 * @cfg {String} fieldClass @hide
22511 * @cfg {String} focusClass @hide
22514 * @cfg {String} autoCreate @hide
22517 * @cfg {String} inputType @hide
22520 * @cfg {String} invalidClass @hide
22523 * @cfg {String} invalidText @hide
22526 * @cfg {String} msgFx @hide
22529 * @cfg {String} validateOnBlur @hide
22533 Roo.HtmlEditorCore.white = [
22534 'area', 'br', 'img', 'input', 'hr', 'wbr',
22536 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
22537 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
22538 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
22539 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
22540 'table', 'ul', 'xmp',
22542 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
22545 'dir', 'menu', 'ol', 'ul', 'dl',
22551 Roo.HtmlEditorCore.black = [
22552 // 'embed', 'object', // enable - backend responsiblity to clean thiese
22554 'base', 'basefont', 'bgsound', 'blink', 'body',
22555 'frame', 'frameset', 'head', 'html', 'ilayer',
22556 'iframe', 'layer', 'link', 'meta', 'object',
22557 'script', 'style' ,'title', 'xml' // clean later..
22559 Roo.HtmlEditorCore.clean = [
22560 'script', 'style', 'title', 'xml'
22562 Roo.HtmlEditorCore.remove = [
22567 Roo.HtmlEditorCore.ablack = [
22571 Roo.HtmlEditorCore.aclean = [
22572 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
22576 Roo.HtmlEditorCore.pwhite= [
22577 'http', 'https', 'mailto'
22580 // white listed style attributes.
22581 Roo.HtmlEditorCore.cwhite= [
22582 // 'text-align', /// default is to allow most things..
22588 // black listed style attributes.
22589 Roo.HtmlEditorCore.cblack= [
22590 // 'font-size' -- this can be set by the project
22594 Roo.HtmlEditorCore.swapCodes =[
22605 //<script type="text/javascript">
22608 * Ext JS Library 1.1.1
22609 * Copyright(c) 2006-2007, Ext JS, LLC.
22615 Roo.form.HtmlEditor = function(config){
22619 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22621 if (!this.toolbars) {
22622 this.toolbars = [];
22624 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22630 * @class Roo.form.HtmlEditor
22631 * @extends Roo.form.Field
22632 * Provides a lightweight HTML Editor component.
22634 * This has been tested on Fireforx / Chrome.. IE may not be so great..
22636 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22637 * supported by this editor.</b><br/><br/>
22638 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22639 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22641 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22643 * @cfg {Boolean} clearUp
22647 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22652 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22657 * @cfg {Number} height (in pixels)
22661 * @cfg {Number} width (in pixels)
22666 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22669 stylesheets: false,
22673 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22678 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22684 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22689 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22697 // private properties
22698 validationEvent : false,
22700 initialized : false,
22703 onFocus : Roo.emptyFn,
22705 hideMode:'offsets',
22707 actionMode : 'container', // defaults to hiding it...
22709 defaultAutoCreate : { // modified by initCompnoent..
22711 style:"width:500px;height:300px;",
22712 autocomplete: "new-password"
22716 initComponent : function(){
22719 * @event initialize
22720 * Fires when the editor is fully initialized (including the iframe)
22721 * @param {HtmlEditor} this
22726 * Fires when the editor is first receives the focus. Any insertion must wait
22727 * until after this event.
22728 * @param {HtmlEditor} this
22732 * @event beforesync
22733 * Fires before the textarea is updated with content from the editor iframe. Return false
22734 * to cancel the sync.
22735 * @param {HtmlEditor} this
22736 * @param {String} html
22740 * @event beforepush
22741 * Fires before the iframe editor is updated with content from the textarea. Return false
22742 * to cancel the push.
22743 * @param {HtmlEditor} this
22744 * @param {String} html
22749 * Fires when the textarea is updated with content from the editor iframe.
22750 * @param {HtmlEditor} this
22751 * @param {String} html
22756 * Fires when the iframe editor is updated with content from the textarea.
22757 * @param {HtmlEditor} this
22758 * @param {String} html
22762 * @event editmodechange
22763 * Fires when the editor switches edit modes
22764 * @param {HtmlEditor} this
22765 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22767 editmodechange: true,
22769 * @event editorevent
22770 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22771 * @param {HtmlEditor} this
22775 * @event firstfocus
22776 * Fires when on first focus - needed by toolbars..
22777 * @param {HtmlEditor} this
22782 * Auto save the htmlEditor value as a file into Events
22783 * @param {HtmlEditor} this
22787 * @event savedpreview
22788 * preview the saved version of htmlEditor
22789 * @param {HtmlEditor} this
22791 savedpreview: true,
22794 * @event stylesheetsclick
22795 * Fires when press the Sytlesheets button
22796 * @param {Roo.HtmlEditorCore} this
22798 stylesheetsclick: true
22800 this.defaultAutoCreate = {
22802 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22803 autocomplete: "new-password"
22808 * Protected method that will not generally be called directly. It
22809 * is called when the editor creates its toolbar. Override this method if you need to
22810 * add custom toolbar buttons.
22811 * @param {HtmlEditor} editor
22813 createToolbar : function(editor){
22814 Roo.log("create toolbars");
22815 if (!editor.toolbars || !editor.toolbars.length) {
22816 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22819 for (var i =0 ; i < editor.toolbars.length;i++) {
22820 editor.toolbars[i] = Roo.factory(
22821 typeof(editor.toolbars[i]) == 'string' ?
22822 { xtype: editor.toolbars[i]} : editor.toolbars[i],
22823 Roo.form.HtmlEditor);
22824 editor.toolbars[i].init(editor);
22832 onRender : function(ct, position)
22835 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22837 this.wrap = this.el.wrap({
22838 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22841 this.editorcore.onRender(ct, position);
22843 if (this.resizable) {
22844 this.resizeEl = new Roo.Resizable(this.wrap, {
22848 minHeight : this.height,
22849 height: this.height,
22850 handles : this.resizable,
22853 resize : function(r, w, h) {
22854 _t.onResize(w,h); // -something
22860 this.createToolbar(this);
22864 this.setSize(this.wrap.getSize());
22866 if (this.resizeEl) {
22867 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22868 // should trigger onReize..
22871 this.keyNav = new Roo.KeyNav(this.el, {
22873 "tab" : function(e){
22874 e.preventDefault();
22876 var value = this.getValue();
22878 var start = this.el.dom.selectionStart;
22879 var end = this.el.dom.selectionEnd;
22883 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22884 this.el.dom.setSelectionRange(end + 1, end + 1);
22888 var f = value.substring(0, start).split("\t");
22890 if(f.pop().length != 0){
22894 this.setValue(f.join("\t") + value.substring(end));
22895 this.el.dom.setSelectionRange(start - 1, start - 1);
22899 "home" : function(e){
22900 e.preventDefault();
22902 var curr = this.el.dom.selectionStart;
22903 var lines = this.getValue().split("\n");
22910 this.el.dom.setSelectionRange(0, 0);
22916 for (var i = 0; i < lines.length;i++) {
22917 pos += lines[i].length;
22927 pos -= lines[i].length;
22933 this.el.dom.setSelectionRange(pos, pos);
22937 this.el.dom.selectionStart = pos;
22938 this.el.dom.selectionEnd = curr;
22941 "end" : function(e){
22942 e.preventDefault();
22944 var curr = this.el.dom.selectionStart;
22945 var lines = this.getValue().split("\n");
22952 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22958 for (var i = 0; i < lines.length;i++) {
22960 pos += lines[i].length;
22974 this.el.dom.setSelectionRange(pos, pos);
22978 this.el.dom.selectionStart = curr;
22979 this.el.dom.selectionEnd = pos;
22984 doRelay : function(foo, bar, hname){
22985 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22991 // if(this.autosave && this.w){
22992 // this.autoSaveFn = setInterval(this.autosave, 1000);
22997 onResize : function(w, h)
22999 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
23004 if(typeof w == 'number'){
23005 var aw = w - this.wrap.getFrameWidth('lr');
23006 this.el.setWidth(this.adjustWidth('textarea', aw));
23009 if(typeof h == 'number'){
23011 for (var i =0; i < this.toolbars.length;i++) {
23012 // fixme - ask toolbars for heights?
23013 tbh += this.toolbars[i].tb.el.getHeight();
23014 if (this.toolbars[i].footer) {
23015 tbh += this.toolbars[i].footer.el.getHeight();
23022 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23023 ah -= 5; // knock a few pixes off for look..
23025 this.el.setHeight(this.adjustWidth('textarea', ah));
23029 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23030 this.editorcore.onResize(ew,eh);
23035 * Toggles the editor between standard and source edit mode.
23036 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23038 toggleSourceEdit : function(sourceEditMode)
23040 this.editorcore.toggleSourceEdit(sourceEditMode);
23042 if(this.editorcore.sourceEditMode){
23043 Roo.log('editor - showing textarea');
23046 // Roo.log(this.syncValue());
23047 this.editorcore.syncValue();
23048 this.el.removeClass('x-hidden');
23049 this.el.dom.removeAttribute('tabIndex');
23052 for (var i = 0; i < this.toolbars.length; i++) {
23053 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23054 this.toolbars[i].tb.hide();
23055 this.toolbars[i].footer.hide();
23060 Roo.log('editor - hiding textarea');
23062 // Roo.log(this.pushValue());
23063 this.editorcore.pushValue();
23065 this.el.addClass('x-hidden');
23066 this.el.dom.setAttribute('tabIndex', -1);
23068 for (var i = 0; i < this.toolbars.length; i++) {
23069 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23070 this.toolbars[i].tb.show();
23071 this.toolbars[i].footer.show();
23075 //this.deferFocus();
23078 this.setSize(this.wrap.getSize());
23079 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
23081 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23084 // private (for BoxComponent)
23085 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23087 // private (for BoxComponent)
23088 getResizeEl : function(){
23092 // private (for BoxComponent)
23093 getPositionEl : function(){
23098 initEvents : function(){
23099 this.originalValue = this.getValue();
23103 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23106 markInvalid : Roo.emptyFn,
23108 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23111 clearInvalid : Roo.emptyFn,
23113 setValue : function(v){
23114 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
23115 this.editorcore.pushValue();
23120 deferFocus : function(){
23121 this.focus.defer(10, this);
23125 focus : function(){
23126 this.editorcore.focus();
23132 onDestroy : function(){
23138 for (var i =0; i < this.toolbars.length;i++) {
23139 // fixme - ask toolbars for heights?
23140 this.toolbars[i].onDestroy();
23143 this.wrap.dom.innerHTML = '';
23144 this.wrap.remove();
23149 onFirstFocus : function(){
23150 //Roo.log("onFirstFocus");
23151 this.editorcore.onFirstFocus();
23152 for (var i =0; i < this.toolbars.length;i++) {
23153 this.toolbars[i].onFirstFocus();
23159 syncValue : function()
23161 this.editorcore.syncValue();
23164 pushValue : function()
23166 this.editorcore.pushValue();
23169 setStylesheets : function(stylesheets)
23171 this.editorcore.setStylesheets(stylesheets);
23174 removeStylesheets : function()
23176 this.editorcore.removeStylesheets();
23180 // hide stuff that is not compatible
23194 * @event specialkey
23198 * @cfg {String} fieldClass @hide
23201 * @cfg {String} focusClass @hide
23204 * @cfg {String} autoCreate @hide
23207 * @cfg {String} inputType @hide
23210 * @cfg {String} invalidClass @hide
23213 * @cfg {String} invalidText @hide
23216 * @cfg {String} msgFx @hide
23219 * @cfg {String} validateOnBlur @hide
23223 // <script type="text/javascript">
23226 * Ext JS Library 1.1.1
23227 * Copyright(c) 2006-2007, Ext JS, LLC.
23233 * @class Roo.form.HtmlEditorToolbar1
23238 new Roo.form.HtmlEditor({
23241 new Roo.form.HtmlEditorToolbar1({
23242 disable : { fonts: 1 , format: 1, ..., ... , ...],
23248 * @cfg {Object} disable List of elements to disable..
23249 * @cfg {Array} btns List of additional buttons.
23253 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23256 Roo.form.HtmlEditor.ToolbarStandard = function(config)
23259 Roo.apply(this, config);
23261 // default disabled, based on 'good practice'..
23262 this.disable = this.disable || {};
23263 Roo.applyIf(this.disable, {
23266 specialElements : true
23270 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23271 // dont call parent... till later.
23274 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype, {
23281 editorcore : false,
23283 * @cfg {Object} disable List of toolbar elements to disable
23290 * @cfg {String} createLinkText The default text for the create link prompt
23292 createLinkText : 'Please enter the URL for the link:',
23294 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23296 defaultLinkValue : 'http:/'+'/',
23300 * @cfg {Array} fontFamilies An array of available font families
23318 // "á" , ?? a acute?
23323 "°" // , // degrees
23325 // "é" , // e ecute
23326 // "ú" , // u ecute?
23329 specialElements : [
23331 text: "Insert Table",
23334 ihtml : '<table><tr><td>Cell</td></tr></table>'
23338 text: "Insert Image",
23341 ihtml : '<img src="about:blank"/>'
23350 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
23351 "input:submit", "input:button", "select", "textarea", "label" ],
23354 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
23356 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23365 * @cfg {String} defaultFont default font to use.
23367 defaultFont: 'tahoma',
23369 fontSelect : false,
23372 formatCombo : false,
23374 init : function(editor)
23376 this.editor = editor;
23377 this.editorcore = editor.editorcore ? editor.editorcore : editor;
23378 var editorcore = this.editorcore;
23382 var fid = editorcore.frameId;
23384 function btn(id, toggle, handler){
23385 var xid = fid + '-'+ id ;
23389 cls : 'x-btn-icon x-edit-'+id,
23390 enableToggle:toggle !== false,
23391 scope: _t, // was editor...
23392 handler:handler||_t.relayBtnCmd,
23393 clickEvent:'mousedown',
23394 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23401 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23403 // stop form submits
23404 tb.el.on('click', function(e){
23405 e.preventDefault(); // what does this do?
23408 if(!this.disable.font) { // && !Roo.isSafari){
23409 /* why no safari for fonts
23410 editor.fontSelect = tb.el.createChild({
23413 cls:'x-font-select',
23414 html: this.createFontOptions()
23417 editor.fontSelect.on('change', function(){
23418 var font = editor.fontSelect.dom.value;
23419 editor.relayCmd('fontname', font);
23420 editor.deferFocus();
23424 editor.fontSelect.dom,
23430 if(!this.disable.formats){
23431 this.formatCombo = new Roo.form.ComboBox({
23432 store: new Roo.data.SimpleStore({
23435 data : this.formats // from states.js
23439 //autoCreate : {tag: "div", size: "20"},
23440 displayField:'tag',
23444 triggerAction: 'all',
23445 emptyText:'Add tag',
23446 selectOnFocus:true,
23449 'select': function(c, r, i) {
23450 editorcore.insertTag(r.get('tag'));
23456 tb.addField(this.formatCombo);
23460 if(!this.disable.format){
23465 btn('strikethrough')
23468 if(!this.disable.fontSize){
23473 btn('increasefontsize', false, editorcore.adjustFont),
23474 btn('decreasefontsize', false, editorcore.adjustFont)
23479 if(!this.disable.colors){
23482 id:editorcore.frameId +'-forecolor',
23483 cls:'x-btn-icon x-edit-forecolor',
23484 clickEvent:'mousedown',
23485 tooltip: this.buttonTips['forecolor'] || undefined,
23487 menu : new Roo.menu.ColorMenu({
23488 allowReselect: true,
23489 focus: Roo.emptyFn,
23492 selectHandler: function(cp, color){
23493 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23494 editor.deferFocus();
23497 clickEvent:'mousedown'
23500 id:editorcore.frameId +'backcolor',
23501 cls:'x-btn-icon x-edit-backcolor',
23502 clickEvent:'mousedown',
23503 tooltip: this.buttonTips['backcolor'] || undefined,
23505 menu : new Roo.menu.ColorMenu({
23506 focus: Roo.emptyFn,
23509 allowReselect: true,
23510 selectHandler: function(cp, color){
23512 editorcore.execCmd('useCSS', false);
23513 editorcore.execCmd('hilitecolor', color);
23514 editorcore.execCmd('useCSS', true);
23515 editor.deferFocus();
23517 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
23518 Roo.isSafari || Roo.isIE ? '#'+color : color);
23519 editor.deferFocus();
23523 clickEvent:'mousedown'
23528 // now add all the items...
23531 if(!this.disable.alignments){
23534 btn('justifyleft'),
23535 btn('justifycenter'),
23536 btn('justifyright')
23540 //if(!Roo.isSafari){
23541 if(!this.disable.links){
23544 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
23548 if(!this.disable.lists){
23551 btn('insertorderedlist'),
23552 btn('insertunorderedlist')
23555 if(!this.disable.sourceEdit){
23558 btn('sourceedit', true, function(btn){
23559 this.toggleSourceEdit(btn.pressed);
23566 // special menu.. - needs to be tidied up..
23567 if (!this.disable.special) {
23570 cls: 'x-edit-none',
23576 for (var i =0; i < this.specialChars.length; i++) {
23577 smenu.menu.items.push({
23579 html: this.specialChars[i],
23580 handler: function(a,b) {
23581 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23582 //editor.insertAtCursor(a.html);
23596 if (!this.disable.cleanStyles) {
23598 cls: 'x-btn-icon x-btn-clear',
23604 for (var i =0; i < this.cleanStyles.length; i++) {
23605 cmenu.menu.items.push({
23606 actiontype : this.cleanStyles[i],
23607 html: 'Remove ' + this.cleanStyles[i],
23608 handler: function(a,b) {
23611 var c = Roo.get(editorcore.doc.body);
23612 c.select('[style]').each(function(s) {
23613 s.dom.style.removeProperty(a.actiontype);
23615 editorcore.syncValue();
23620 cmenu.menu.items.push({
23621 actiontype : 'tablewidths',
23622 html: 'Remove Table Widths',
23623 handler: function(a,b) {
23624 editorcore.cleanTableWidths();
23625 editorcore.syncValue();
23629 cmenu.menu.items.push({
23630 actiontype : 'word',
23631 html: 'Remove MS Word Formating',
23632 handler: function(a,b) {
23633 editorcore.cleanWord();
23634 editorcore.syncValue();
23639 cmenu.menu.items.push({
23640 actiontype : 'all',
23641 html: 'Remove All Styles',
23642 handler: function(a,b) {
23644 var c = Roo.get(editorcore.doc.body);
23645 c.select('[style]').each(function(s) {
23646 s.dom.removeAttribute('style');
23648 editorcore.syncValue();
23653 cmenu.menu.items.push({
23654 actiontype : 'all',
23655 html: 'Remove All CSS Classes',
23656 handler: function(a,b) {
23658 var c = Roo.get(editorcore.doc.body);
23659 c.select('[class]').each(function(s) {
23660 s.dom.removeAttribute('class');
23662 editorcore.cleanWord();
23663 editorcore.syncValue();
23668 cmenu.menu.items.push({
23669 actiontype : 'tidy',
23670 html: 'Tidy HTML Source',
23671 handler: function(a,b) {
23672 editorcore.doc.body.innerHTML = editorcore.domToHTML();
23673 editorcore.syncValue();
23682 if (!this.disable.specialElements) {
23685 cls: 'x-edit-none',
23690 for (var i =0; i < this.specialElements.length; i++) {
23691 semenu.menu.items.push(
23693 handler: function(a,b) {
23694 editor.insertAtCursor(this.ihtml);
23696 }, this.specialElements[i])
23708 for(var i =0; i< this.btns.length;i++) {
23709 var b = Roo.factory(this.btns[i],Roo.form);
23710 b.cls = 'x-edit-none';
23712 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23713 b.cls += ' x-init-enable';
23716 b.scope = editorcore;
23724 // disable everything...
23726 this.tb.items.each(function(item){
23729 item.id != editorcore.frameId+ '-sourceedit' &&
23730 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23736 this.rendered = true;
23738 // the all the btns;
23739 editor.on('editorevent', this.updateToolbar, this);
23740 // other toolbars need to implement this..
23741 //editor.on('editmodechange', this.updateToolbar, this);
23745 relayBtnCmd : function(btn) {
23746 this.editorcore.relayCmd(btn.cmd);
23748 // private used internally
23749 createLink : function(){
23750 Roo.log("create link?");
23751 var url = prompt(this.createLinkText, this.defaultLinkValue);
23752 if(url && url != 'http:/'+'/'){
23753 this.editorcore.relayCmd('createlink', url);
23759 * Protected method that will not generally be called directly. It triggers
23760 * a toolbar update by reading the markup state of the current selection in the editor.
23762 updateToolbar: function(){
23764 if(!this.editorcore.activated){
23765 this.editor.onFirstFocus();
23769 var btns = this.tb.items.map,
23770 doc = this.editorcore.doc,
23771 frameId = this.editorcore.frameId;
23773 if(!this.disable.font && !Roo.isSafari){
23775 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23776 if(name != this.fontSelect.dom.value){
23777 this.fontSelect.dom.value = name;
23781 if(!this.disable.format){
23782 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23783 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23784 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23785 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23787 if(!this.disable.alignments){
23788 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23789 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23790 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23792 if(!Roo.isSafari && !this.disable.lists){
23793 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23794 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23797 var ans = this.editorcore.getAllAncestors();
23798 if (this.formatCombo) {
23801 var store = this.formatCombo.store;
23802 this.formatCombo.setValue("");
23803 for (var i =0; i < ans.length;i++) {
23804 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23806 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23814 // hides menus... - so this cant be on a menu...
23815 Roo.menu.MenuMgr.hideAll();
23817 //this.editorsyncValue();
23821 createFontOptions : function(){
23822 var buf = [], fs = this.fontFamilies, ff, lc;
23826 for(var i = 0, len = fs.length; i< len; i++){
23828 lc = ff.toLowerCase();
23830 '<option value="',lc,'" style="font-family:',ff,';"',
23831 (this.defaultFont == lc ? ' selected="true">' : '>'),
23836 return buf.join('');
23839 toggleSourceEdit : function(sourceEditMode){
23841 Roo.log("toolbar toogle");
23842 if(sourceEditMode === undefined){
23843 sourceEditMode = !this.sourceEditMode;
23845 this.sourceEditMode = sourceEditMode === true;
23846 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23847 // just toggle the button?
23848 if(btn.pressed !== this.sourceEditMode){
23849 btn.toggle(this.sourceEditMode);
23853 if(sourceEditMode){
23854 Roo.log("disabling buttons");
23855 this.tb.items.each(function(item){
23856 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23862 Roo.log("enabling buttons");
23863 if(this.editorcore.initialized){
23864 this.tb.items.each(function(item){
23870 Roo.log("calling toggole on editor");
23871 // tell the editor that it's been pressed..
23872 this.editor.toggleSourceEdit(sourceEditMode);
23876 * Object collection of toolbar tooltips for the buttons in the editor. The key
23877 * is the command id associated with that button and the value is a valid QuickTips object.
23882 title: 'Bold (Ctrl+B)',
23883 text: 'Make the selected text bold.',
23884 cls: 'x-html-editor-tip'
23887 title: 'Italic (Ctrl+I)',
23888 text: 'Make the selected text italic.',
23889 cls: 'x-html-editor-tip'
23897 title: 'Bold (Ctrl+B)',
23898 text: 'Make the selected text bold.',
23899 cls: 'x-html-editor-tip'
23902 title: 'Italic (Ctrl+I)',
23903 text: 'Make the selected text italic.',
23904 cls: 'x-html-editor-tip'
23907 title: 'Underline (Ctrl+U)',
23908 text: 'Underline the selected text.',
23909 cls: 'x-html-editor-tip'
23912 title: 'Strikethrough',
23913 text: 'Strikethrough the selected text.',
23914 cls: 'x-html-editor-tip'
23916 increasefontsize : {
23917 title: 'Grow Text',
23918 text: 'Increase the font size.',
23919 cls: 'x-html-editor-tip'
23921 decreasefontsize : {
23922 title: 'Shrink Text',
23923 text: 'Decrease the font size.',
23924 cls: 'x-html-editor-tip'
23927 title: 'Text Highlight Color',
23928 text: 'Change the background color of the selected text.',
23929 cls: 'x-html-editor-tip'
23932 title: 'Font Color',
23933 text: 'Change the color of the selected text.',
23934 cls: 'x-html-editor-tip'
23937 title: 'Align Text Left',
23938 text: 'Align text to the left.',
23939 cls: 'x-html-editor-tip'
23942 title: 'Center Text',
23943 text: 'Center text in the editor.',
23944 cls: 'x-html-editor-tip'
23947 title: 'Align Text Right',
23948 text: 'Align text to the right.',
23949 cls: 'x-html-editor-tip'
23951 insertunorderedlist : {
23952 title: 'Bullet List',
23953 text: 'Start a bulleted list.',
23954 cls: 'x-html-editor-tip'
23956 insertorderedlist : {
23957 title: 'Numbered List',
23958 text: 'Start a numbered list.',
23959 cls: 'x-html-editor-tip'
23962 title: 'Hyperlink',
23963 text: 'Make the selected text a hyperlink.',
23964 cls: 'x-html-editor-tip'
23967 title: 'Source Edit',
23968 text: 'Switch to source editing mode.',
23969 cls: 'x-html-editor-tip'
23973 onDestroy : function(){
23976 this.tb.items.each(function(item){
23978 item.menu.removeAll();
23980 item.menu.el.destroy();
23988 onFirstFocus: function() {
23989 this.tb.items.each(function(item){
23998 // <script type="text/javascript">
24001 * Ext JS Library 1.1.1
24002 * Copyright(c) 2006-2007, Ext JS, LLC.
24009 * @class Roo.form.HtmlEditor.ToolbarContext
24014 new Roo.form.HtmlEditor({
24017 { xtype: 'ToolbarStandard', styles : {} }
24018 { xtype: 'ToolbarContext', disable : {} }
24024 * @config : {Object} disable List of elements to disable.. (not done yet.)
24025 * @config : {Object} styles Map of styles available.
24029 Roo.form.HtmlEditor.ToolbarContext = function(config)
24032 Roo.apply(this, config);
24033 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24034 // dont call parent... till later.
24035 this.styles = this.styles || {};
24040 Roo.form.HtmlEditor.ToolbarContext.types = {
24052 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
24118 opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
24123 opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
24133 style : 'fontFamily',
24134 displayField: 'display',
24135 optname : 'font-family',
24184 // should we really allow this??
24185 // should this just be
24196 style : 'fontFamily',
24197 displayField: 'display',
24198 optname : 'font-family',
24205 style : 'fontFamily',
24206 displayField: 'display',
24207 optname : 'font-family',
24214 style : 'fontFamily',
24215 displayField: 'display',
24216 optname : 'font-family',
24227 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
24228 Roo.form.HtmlEditor.ToolbarContext.stores = false;
24230 Roo.form.HtmlEditor.ToolbarContext.options = {
24232 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
24233 [ 'Courier New', 'Courier New'],
24234 [ 'Tahoma', 'Tahoma'],
24235 [ 'Times New Roman,serif', 'Times'],
24236 [ 'Verdana','Verdana' ]
24240 // fixme - these need to be configurable..
24243 //Roo.form.HtmlEditor.ToolbarContext.types
24246 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
24253 editorcore : false,
24255 * @cfg {Object} disable List of toolbar elements to disable
24260 * @cfg {Object} styles List of styles
24261 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
24263 * These must be defined in the page, so they get rendered correctly..
24274 init : function(editor)
24276 this.editor = editor;
24277 this.editorcore = editor.editorcore ? editor.editorcore : editor;
24278 var editorcore = this.editorcore;
24280 var fid = editorcore.frameId;
24282 function btn(id, toggle, handler){
24283 var xid = fid + '-'+ id ;
24287 cls : 'x-btn-icon x-edit-'+id,
24288 enableToggle:toggle !== false,
24289 scope: editorcore, // was editor...
24290 handler:handler||editorcore.relayBtnCmd,
24291 clickEvent:'mousedown',
24292 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24296 // create a new element.
24297 var wdiv = editor.wrap.createChild({
24299 }, editor.wrap.dom.firstChild.nextSibling, true);
24301 // can we do this more than once??
24303 // stop form submits
24306 // disable everything...
24307 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24308 this.toolbars = {};
24310 for (var i in ty) {
24312 this.toolbars[i] = this.buildToolbar(ty[i],i);
24314 this.tb = this.toolbars.BODY;
24316 this.buildFooter();
24317 this.footer.show();
24318 editor.on('hide', function( ) { this.footer.hide() }, this);
24319 editor.on('show', function( ) { this.footer.show() }, this);
24322 this.rendered = true;
24324 // the all the btns;
24325 editor.on('editorevent', this.updateToolbar, this);
24326 // other toolbars need to implement this..
24327 //editor.on('editmodechange', this.updateToolbar, this);
24333 * Protected method that will not generally be called directly. It triggers
24334 * a toolbar update by reading the markup state of the current selection in the editor.
24336 * Note you can force an update by calling on('editorevent', scope, false)
24338 updateToolbar: function(editor,ev,sel){
24341 // capture mouse up - this is handy for selecting images..
24342 // perhaps should go somewhere else...
24343 if(!this.editorcore.activated){
24344 this.editor.onFirstFocus();
24350 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24351 // selectNode - might want to handle IE?
24353 (ev.type == 'mouseup' || ev.type == 'click' ) &&
24354 ev.target && ev.target.tagName == 'IMG') {
24355 // they have click on an image...
24356 // let's see if we can change the selection...
24359 var nodeRange = sel.ownerDocument.createRange();
24361 nodeRange.selectNode(sel);
24363 nodeRange.selectNodeContents(sel);
24365 //nodeRange.collapse(true);
24366 var s = this.editorcore.win.getSelection();
24367 s.removeAllRanges();
24368 s.addRange(nodeRange);
24372 var updateFooter = sel ? false : true;
24375 var ans = this.editorcore.getAllAncestors();
24378 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24381 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
24382 sel = sel ? sel : this.editorcore.doc.body;
24383 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24386 // pick a menu that exists..
24387 var tn = sel.tagName.toUpperCase();
24388 //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24390 tn = sel.tagName.toUpperCase();
24392 var lastSel = this.tb.selectedNode;
24394 this.tb.selectedNode = sel;
24396 // if current menu does not match..
24398 if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24401 ///console.log("show: " + tn);
24402 this.tb = typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24405 this.tb.items.first().el.innerHTML = tn + ': ';
24408 // update attributes
24409 if (this.tb.fields) {
24410 this.tb.fields.each(function(e) {
24412 e.setValue(sel.style[e.stylename]);
24415 e.setValue(sel.getAttribute(e.attrname));
24419 var hasStyles = false;
24420 for(var i in this.styles) {
24427 var st = this.tb.fields.item(0);
24429 st.store.removeAll();
24432 var cn = sel.className.split(/\s+/);
24435 if (this.styles['*']) {
24437 Roo.each(this.styles['*'], function(v) {
24438 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
24441 if (this.styles[tn]) {
24442 Roo.each(this.styles[tn], function(v) {
24443 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
24447 st.store.loadData(avs);
24451 // flag our selected Node.
24452 this.tb.selectedNode = sel;
24455 Roo.menu.MenuMgr.hideAll();
24459 if (!updateFooter) {
24460 //this.footDisp.dom.innerHTML = '';
24463 // update the footer
24467 this.footerEls = ans.reverse();
24468 Roo.each(this.footerEls, function(a,i) {
24469 if (!a) { return; }
24470 html += html.length ? ' > ' : '';
24472 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24477 var sz = this.footDisp.up('td').getSize();
24478 this.footDisp.dom.style.width = (sz.width -10) + 'px';
24479 this.footDisp.dom.style.marginLeft = '5px';
24481 this.footDisp.dom.style.overflow = 'hidden';
24483 this.footDisp.dom.innerHTML = html;
24485 //this.editorsyncValue();
24492 onDestroy : function(){
24495 this.tb.items.each(function(item){
24497 item.menu.removeAll();
24499 item.menu.el.destroy();
24507 onFirstFocus: function() {
24508 // need to do this for all the toolbars..
24509 this.tb.items.each(function(item){
24513 buildToolbar: function(tlist, nm)
24515 var editor = this.editor;
24516 var editorcore = this.editorcore;
24517 // create a new element.
24518 var wdiv = editor.wrap.createChild({
24520 }, editor.wrap.dom.firstChild.nextSibling, true);
24523 var tb = new Roo.Toolbar(wdiv);
24526 tb.add(nm+ ": ");
24529 for(var i in this.styles) {
24534 if (styles && styles.length) {
24536 // this needs a multi-select checkbox...
24537 tb.addField( new Roo.form.ComboBox({
24538 store: new Roo.data.SimpleStore({
24540 fields: ['val', 'selected'],
24543 name : '-roo-edit-className',
24544 attrname : 'className',
24545 displayField: 'val',
24549 triggerAction: 'all',
24550 emptyText:'Select Style',
24551 selectOnFocus:true,
24554 'select': function(c, r, i) {
24555 // initial support only for on class per el..
24556 tb.selectedNode.className = r ? r.get('val') : '';
24557 editorcore.syncValue();
24564 var tbc = Roo.form.HtmlEditor.ToolbarContext;
24565 var tbops = tbc.options;
24567 for (var i in tlist) {
24569 var item = tlist[i];
24570 tb.add(item.title + ": ");
24573 //optname == used so you can configure the options available..
24574 var opts = item.opts ? item.opts : false;
24575 if (item.optname) {
24576 opts = tbops[item.optname];
24581 // opts == pulldown..
24582 tb.addField( new Roo.form.ComboBox({
24583 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24585 fields: ['val', 'display'],
24588 name : '-roo-edit-' + i,
24590 stylename : item.style ? item.style : false,
24591 displayField: item.displayField ? item.displayField : 'val',
24592 valueField : 'val',
24594 mode: typeof(tbc.stores[i]) != 'undefined' ? 'remote' : 'local',
24596 triggerAction: 'all',
24597 emptyText:'Select',
24598 selectOnFocus:true,
24599 width: item.width ? item.width : 130,
24601 'select': function(c, r, i) {
24603 tb.selectedNode.style[c.stylename] = r.get('val');
24606 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24615 tb.addField( new Roo.form.TextField({
24618 //allowBlank:false,
24623 tb.addField( new Roo.form.TextField({
24624 name: '-roo-edit-' + i,
24631 'change' : function(f, nv, ov) {
24632 tb.selectedNode.setAttribute(f.attrname, nv);
24633 editorcore.syncValue();
24646 text: 'Stylesheets',
24649 click : function ()
24651 _this.editor.fireEvent('stylesheetsclick', _this.editor);
24659 text: 'Remove Tag',
24662 click : function ()
24665 // undo does not work.
24667 var sn = tb.selectedNode;
24669 var pn = sn.parentNode;
24671 var stn = sn.childNodes[0];
24672 var en = sn.childNodes[sn.childNodes.length - 1 ];
24673 while (sn.childNodes.length) {
24674 var node = sn.childNodes[0];
24675 sn.removeChild(node);
24677 pn.insertBefore(node, sn);
24680 pn.removeChild(sn);
24681 var range = editorcore.createRange();
24683 range.setStart(stn,0);
24684 range.setEnd(en,0); //????
24685 //range.selectNode(sel);
24688 var selection = editorcore.getSelection();
24689 selection.removeAllRanges();
24690 selection.addRange(range);
24694 //_this.updateToolbar(null, null, pn);
24695 _this.updateToolbar(null, null, null);
24696 _this.footDisp.dom.innerHTML = '';
24706 tb.el.on('click', function(e){
24707 e.preventDefault(); // what does this do?
24709 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24712 // dont need to disable them... as they will get hidden
24717 buildFooter : function()
24720 var fel = this.editor.wrap.createChild();
24721 this.footer = new Roo.Toolbar(fel);
24722 // toolbar has scrolly on left / right?
24723 var footDisp= new Roo.Toolbar.Fill();
24729 handler : function() {
24730 _t.footDisp.scrollTo('left',0,true)
24734 this.footer.add( footDisp );
24739 handler : function() {
24741 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24745 var fel = Roo.get(footDisp.el);
24746 fel.addClass('x-editor-context');
24747 this.footDispWrap = fel;
24748 this.footDispWrap.overflow = 'hidden';
24750 this.footDisp = fel.createChild();
24751 this.footDispWrap.on('click', this.onContextClick, this)
24755 onContextClick : function (ev,dom)
24757 ev.preventDefault();
24758 var cn = dom.className;
24760 if (!cn.match(/x-ed-loc-/)) {
24763 var n = cn.split('-').pop();
24764 var ans = this.footerEls;
24768 var range = this.editorcore.createRange();
24770 range.selectNodeContents(sel);
24771 //range.selectNode(sel);
24774 var selection = this.editorcore.getSelection();
24775 selection.removeAllRanges();
24776 selection.addRange(range);
24780 this.updateToolbar(null, null, sel);
24797 * Ext JS Library 1.1.1
24798 * Copyright(c) 2006-2007, Ext JS, LLC.
24800 * Originally Released Under LGPL - original licence link has changed is not relivant.
24803 * <script type="text/javascript">
24807 * @class Roo.form.BasicForm
24808 * @extends Roo.util.Observable
24809 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24811 * @param {String/HTMLElement/Roo.Element} el The form element or its id
24812 * @param {Object} config Configuration options
24814 Roo.form.BasicForm = function(el, config){
24815 this.allItems = [];
24816 this.childForms = [];
24817 Roo.apply(this, config);
24819 * The Roo.form.Field items in this form.
24820 * @type MixedCollection
24824 this.items = new Roo.util.MixedCollection(false, function(o){
24825 return o.id || (o.id = Roo.id());
24829 * @event beforeaction
24830 * Fires before any action is performed. Return false to cancel the action.
24831 * @param {Form} this
24832 * @param {Action} action The action to be performed
24834 beforeaction: true,
24836 * @event actionfailed
24837 * Fires when an action fails.
24838 * @param {Form} this
24839 * @param {Action} action The action that failed
24841 actionfailed : true,
24843 * @event actioncomplete
24844 * Fires when an action is completed.
24845 * @param {Form} this
24846 * @param {Action} action The action that completed
24848 actioncomplete : true
24853 Roo.form.BasicForm.superclass.constructor.call(this);
24855 Roo.form.BasicForm.popover.apply();
24858 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24860 * @cfg {String} method
24861 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24864 * @cfg {DataReader} reader
24865 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24866 * This is optional as there is built-in support for processing JSON.
24869 * @cfg {DataReader} errorReader
24870 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24871 * This is completely optional as there is built-in support for processing JSON.
24874 * @cfg {String} url
24875 * The URL to use for form actions if one isn't supplied in the action options.
24878 * @cfg {Boolean} fileUpload
24879 * Set to true if this form is a file upload.
24883 * @cfg {Object} baseParams
24884 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24889 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24894 activeAction : null,
24897 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24898 * or setValues() data instead of when the form was first created.
24900 trackResetOnLoad : false,
24904 * childForms - used for multi-tab forms
24907 childForms : false,
24910 * allItems - full list of fields.
24916 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24917 * element by passing it or its id or mask the form itself by passing in true.
24920 waitMsgTarget : false,
24925 disableMask : false,
24928 * @cfg {Boolean} errorMask (true|false) default false
24933 * @cfg {Number} maskOffset Default 100
24938 initEl : function(el){
24939 this.el = Roo.get(el);
24940 this.id = this.el.id || Roo.id();
24941 this.el.on('submit', this.onSubmit, this);
24942 this.el.addClass('x-form');
24946 onSubmit : function(e){
24951 * Returns true if client-side validation on the form is successful.
24954 isValid : function(){
24956 var target = false;
24957 this.items.each(function(f){
24964 if(!target && f.el.isVisible(true)){
24969 if(this.errorMask && !valid){
24970 Roo.form.BasicForm.popover.mask(this, target);
24977 * DEPRICATED Returns true if any fields in this form have changed since their original load.
24980 isDirty : function(){
24982 this.items.each(function(f){
24992 * Returns true if any fields in this form have changed since their original load. (New version)
24996 hasChanged : function()
24999 this.items.each(function(f){
25000 if(f.hasChanged()){
25009 * Resets all hasChanged to 'false' -
25010 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
25011 * So hasChanged storage is only to be used for this purpose
25014 resetHasChanged : function()
25016 this.items.each(function(f){
25017 f.resetHasChanged();
25024 * Performs a predefined action (submit or load) or custom actions you define on this form.
25025 * @param {String} actionName The name of the action type
25026 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
25027 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
25028 * accept other config options):
25030 Property Type Description
25031 ---------------- --------------- ----------------------------------------------------------------------------------
25032 url String The url for the action (defaults to the form's url)
25033 method String The form method to use (defaults to the form's method, or POST if not defined)
25034 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
25035 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
25036 validate the form on the client (defaults to false)
25038 * @return {BasicForm} this
25040 doAction : function(action, options){
25041 if(typeof action == 'string'){
25042 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
25044 if(this.fireEvent('beforeaction', this, action) !== false){
25045 this.beforeAction(action);
25046 action.run.defer(100, action);
25052 * Shortcut to do a submit action.
25053 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25054 * @return {BasicForm} this
25056 submit : function(options){
25057 this.doAction('submit', options);
25062 * Shortcut to do a load action.
25063 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25064 * @return {BasicForm} this
25066 load : function(options){
25067 this.doAction('load', options);
25072 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
25073 * @param {Record} record The record to edit
25074 * @return {BasicForm} this
25076 updateRecord : function(record){
25077 record.beginEdit();
25078 var fs = record.fields;
25079 fs.each(function(f){
25080 var field = this.findField(f.name);
25082 record.set(f.name, field.getValue());
25090 * Loads an Roo.data.Record into this form.
25091 * @param {Record} record The record to load
25092 * @return {BasicForm} this
25094 loadRecord : function(record){
25095 this.setValues(record.data);
25100 beforeAction : function(action){
25101 var o = action.options;
25103 if(!this.disableMask) {
25104 if(this.waitMsgTarget === true){
25105 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
25106 }else if(this.waitMsgTarget){
25107 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
25108 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
25110 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
25118 afterAction : function(action, success){
25119 this.activeAction = null;
25120 var o = action.options;
25122 if(!this.disableMask) {
25123 if(this.waitMsgTarget === true){
25125 }else if(this.waitMsgTarget){
25126 this.waitMsgTarget.unmask();
25128 Roo.MessageBox.updateProgress(1);
25129 Roo.MessageBox.hide();
25137 Roo.callback(o.success, o.scope, [this, action]);
25138 this.fireEvent('actioncomplete', this, action);
25142 // failure condition..
25143 // we have a scenario where updates need confirming.
25144 // eg. if a locking scenario exists..
25145 // we look for { errors : { needs_confirm : true }} in the response.
25147 (typeof(action.result) != 'undefined') &&
25148 (typeof(action.result.errors) != 'undefined') &&
25149 (typeof(action.result.errors.needs_confirm) != 'undefined')
25152 Roo.MessageBox.confirm(
25153 "Change requires confirmation",
25154 action.result.errorMsg,
25159 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
25169 Roo.callback(o.failure, o.scope, [this, action]);
25170 // show an error message if no failed handler is set..
25171 if (!this.hasListener('actionfailed')) {
25172 Roo.MessageBox.alert("Error",
25173 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
25174 action.result.errorMsg :
25175 "Saving Failed, please check your entries or try again"
25179 this.fireEvent('actionfailed', this, action);
25185 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
25186 * @param {String} id The value to search for
25189 findField : function(id){
25190 var field = this.items.get(id);
25192 this.items.each(function(f){
25193 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
25199 return field || null;
25203 * Add a secondary form to this one,
25204 * Used to provide tabbed forms. One form is primary, with hidden values
25205 * which mirror the elements from the other forms.
25207 * @param {Roo.form.Form} form to add.
25210 addForm : function(form)
25213 if (this.childForms.indexOf(form) > -1) {
25217 this.childForms.push(form);
25219 Roo.each(form.allItems, function (fe) {
25221 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
25222 if (this.findField(n)) { // already added..
25225 var add = new Roo.form.Hidden({
25228 add.render(this.el);
25235 * Mark fields in this form invalid in bulk.
25236 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
25237 * @return {BasicForm} this
25239 markInvalid : function(errors){
25240 if(errors instanceof Array){
25241 for(var i = 0, len = errors.length; i < len; i++){
25242 var fieldError = errors[i];
25243 var f = this.findField(fieldError.id);
25245 f.markInvalid(fieldError.msg);
25251 if(typeof errors[id] != 'function' && (field = this.findField(id))){
25252 field.markInvalid(errors[id]);
25256 Roo.each(this.childForms || [], function (f) {
25257 f.markInvalid(errors);
25264 * Set values for fields in this form in bulk.
25265 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
25266 * @return {BasicForm} this
25268 setValues : function(values){
25269 if(values instanceof Array){ // array of objects
25270 for(var i = 0, len = values.length; i < len; i++){
25272 var f = this.findField(v.id);
25274 f.setValue(v.value);
25275 if(this.trackResetOnLoad){
25276 f.originalValue = f.getValue();
25280 }else{ // object hash
25283 if(typeof values[id] != 'function' && (field = this.findField(id))){
25285 if (field.setFromData &&
25286 field.valueField &&
25287 field.displayField &&
25288 // combos' with local stores can
25289 // be queried via setValue()
25290 // to set their value..
25291 (field.store && !field.store.isLocal)
25295 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
25296 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
25297 field.setFromData(sd);
25300 field.setValue(values[id]);
25304 if(this.trackResetOnLoad){
25305 field.originalValue = field.getValue();
25310 this.resetHasChanged();
25313 Roo.each(this.childForms || [], function (f) {
25314 f.setValues(values);
25315 f.resetHasChanged();
25322 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25323 * they are returned as an array.
25324 * @param {Boolean} asString
25327 getValues : function(asString){
25328 if (this.childForms) {
25329 // copy values from the child forms
25330 Roo.each(this.childForms, function (f) {
25331 this.setValues(f.getValues());
25336 if (typeof(FormData) != 'undefined' && asString !== true) {
25337 var fd = (new FormData(this.el.dom)).entries();
25339 var ent = fd.next();
25340 while (!ent.done) {
25341 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25348 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25349 if(asString === true){
25352 return Roo.urlDecode(fs);
25356 * Returns the fields in this form as an object with key/value pairs.
25357 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25360 getFieldValues : function(with_hidden)
25362 if (this.childForms) {
25363 // copy values from the child forms
25364 // should this call getFieldValues - probably not as we do not currently copy
25365 // hidden fields when we generate..
25366 Roo.each(this.childForms, function (f) {
25367 this.setValues(f.getValues());
25372 this.items.each(function(f){
25373 if (!f.getName()) {
25376 var v = f.getValue();
25377 if (f.inputType =='radio') {
25378 if (typeof(ret[f.getName()]) == 'undefined') {
25379 ret[f.getName()] = ''; // empty..
25382 if (!f.el.dom.checked) {
25386 v = f.el.dom.value;
25390 // not sure if this supported any more..
25391 if ((typeof(v) == 'object') && f.getRawValue) {
25392 v = f.getRawValue() ; // dates..
25394 // combo boxes where name != hiddenName...
25395 if (f.name != f.getName()) {
25396 ret[f.name] = f.getRawValue();
25398 ret[f.getName()] = v;
25405 * Clears all invalid messages in this form.
25406 * @return {BasicForm} this
25408 clearInvalid : function(){
25409 this.items.each(function(f){
25413 Roo.each(this.childForms || [], function (f) {
25422 * Resets this form.
25423 * @return {BasicForm} this
25425 reset : function(){
25426 this.items.each(function(f){
25430 Roo.each(this.childForms || [], function (f) {
25433 this.resetHasChanged();
25439 * Add Roo.form components to this form.
25440 * @param {Field} field1
25441 * @param {Field} field2 (optional)
25442 * @param {Field} etc (optional)
25443 * @return {BasicForm} this
25446 this.items.addAll(Array.prototype.slice.call(arguments, 0));
25452 * Removes a field from the items collection (does NOT remove its markup).
25453 * @param {Field} field
25454 * @return {BasicForm} this
25456 remove : function(field){
25457 this.items.remove(field);
25462 * Looks at the fields in this form, checks them for an id attribute,
25463 * and calls applyTo on the existing dom element with that id.
25464 * @return {BasicForm} this
25466 render : function(){
25467 this.items.each(function(f){
25468 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25476 * Calls {@link Ext#apply} for all fields in this form with the passed object.
25477 * @param {Object} values
25478 * @return {BasicForm} this
25480 applyToFields : function(o){
25481 this.items.each(function(f){
25488 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25489 * @param {Object} values
25490 * @return {BasicForm} this
25492 applyIfToFields : function(o){
25493 this.items.each(function(f){
25501 Roo.BasicForm = Roo.form.BasicForm;
25503 Roo.apply(Roo.form.BasicForm, {
25517 intervalID : false,
25523 if(this.isApplied){
25528 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25529 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25530 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25531 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25534 this.maskEl.top.enableDisplayMode("block");
25535 this.maskEl.left.enableDisplayMode("block");
25536 this.maskEl.bottom.enableDisplayMode("block");
25537 this.maskEl.right.enableDisplayMode("block");
25539 Roo.get(document.body).on('click', function(){
25543 Roo.get(document.body).on('touchstart', function(){
25547 this.isApplied = true
25550 mask : function(form, target)
25554 this.target = target;
25556 if(!this.form.errorMask || !target.el){
25560 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25562 var ot = this.target.el.calcOffsetsTo(scrollable);
25564 var scrollTo = ot[1] - this.form.maskOffset;
25566 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25568 scrollable.scrollTo('top', scrollTo);
25570 var el = this.target.wrap || this.target.el;
25572 var box = el.getBox();
25574 this.maskEl.top.setStyle('position', 'absolute');
25575 this.maskEl.top.setStyle('z-index', 10000);
25576 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25577 this.maskEl.top.setLeft(0);
25578 this.maskEl.top.setTop(0);
25579 this.maskEl.top.show();
25581 this.maskEl.left.setStyle('position', 'absolute');
25582 this.maskEl.left.setStyle('z-index', 10000);
25583 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25584 this.maskEl.left.setLeft(0);
25585 this.maskEl.left.setTop(box.y - this.padding);
25586 this.maskEl.left.show();
25588 this.maskEl.bottom.setStyle('position', 'absolute');
25589 this.maskEl.bottom.setStyle('z-index', 10000);
25590 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25591 this.maskEl.bottom.setLeft(0);
25592 this.maskEl.bottom.setTop(box.bottom + this.padding);
25593 this.maskEl.bottom.show();
25595 this.maskEl.right.setStyle('position', 'absolute');
25596 this.maskEl.right.setStyle('z-index', 10000);
25597 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25598 this.maskEl.right.setLeft(box.right + this.padding);
25599 this.maskEl.right.setTop(box.y - this.padding);
25600 this.maskEl.right.show();
25602 this.intervalID = window.setInterval(function() {
25603 Roo.form.BasicForm.popover.unmask();
25606 window.onwheel = function(){ return false;};
25608 (function(){ this.isMasked = true; }).defer(500, this);
25612 unmask : function()
25614 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25618 this.maskEl.top.setStyle('position', 'absolute');
25619 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25620 this.maskEl.top.hide();
25622 this.maskEl.left.setStyle('position', 'absolute');
25623 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25624 this.maskEl.left.hide();
25626 this.maskEl.bottom.setStyle('position', 'absolute');
25627 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25628 this.maskEl.bottom.hide();
25630 this.maskEl.right.setStyle('position', 'absolute');
25631 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25632 this.maskEl.right.hide();
25634 window.onwheel = function(){ return true;};
25636 if(this.intervalID){
25637 window.clearInterval(this.intervalID);
25638 this.intervalID = false;
25641 this.isMasked = false;
25649 * Ext JS Library 1.1.1
25650 * Copyright(c) 2006-2007, Ext JS, LLC.
25652 * Originally Released Under LGPL - original licence link has changed is not relivant.
25655 * <script type="text/javascript">
25659 * @class Roo.form.Form
25660 * @extends Roo.form.BasicForm
25661 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25663 * @param {Object} config Configuration options
25665 Roo.form.Form = function(config){
25667 if (config.items) {
25668 xitems = config.items;
25669 delete config.items;
25673 Roo.form.Form.superclass.constructor.call(this, null, config);
25674 this.url = this.url || this.action;
25676 this.root = new Roo.form.Layout(Roo.applyIf({
25680 this.active = this.root;
25682 * Array of all the buttons that have been added to this form via {@link addButton}
25686 this.allItems = [];
25689 * @event clientvalidation
25690 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25691 * @param {Form} this
25692 * @param {Boolean} valid true if the form has passed client-side validation
25694 clientvalidation: true,
25697 * Fires when the form is rendered
25698 * @param {Roo.form.Form} form
25703 if (this.progressUrl) {
25704 // push a hidden field onto the list of fields..
25708 name : 'UPLOAD_IDENTIFIER'
25713 Roo.each(xitems, this.addxtype, this);
25717 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25719 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25722 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25725 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25727 buttonAlign:'center',
25730 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25735 * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25736 * This property cascades to child containers if not set.
25741 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25742 * fires a looping event with that state. This is required to bind buttons to the valid
25743 * state using the config value formBind:true on the button.
25745 monitorValid : false,
25748 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25753 * @cfg {String} progressUrl - Url to return progress data
25756 progressUrl : false,
25758 * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25759 * sending a formdata with extra parameters - eg uploaded elements.
25765 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25766 * fields are added and the column is closed. If no fields are passed the column remains open
25767 * until end() is called.
25768 * @param {Object} config The config to pass to the column
25769 * @param {Field} field1 (optional)
25770 * @param {Field} field2 (optional)
25771 * @param {Field} etc (optional)
25772 * @return Column The column container object
25774 column : function(c){
25775 var col = new Roo.form.Column(c);
25777 if(arguments.length > 1){ // duplicate code required because of Opera
25778 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25785 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25786 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25787 * until end() is called.
25788 * @param {Object} config The config to pass to the fieldset
25789 * @param {Field} field1 (optional)
25790 * @param {Field} field2 (optional)
25791 * @param {Field} etc (optional)
25792 * @return FieldSet The fieldset container object
25794 fieldset : function(c){
25795 var fs = new Roo.form.FieldSet(c);
25797 if(arguments.length > 1){ // duplicate code required because of Opera
25798 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25805 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25806 * fields are added and the container is closed. If no fields are passed the container remains open
25807 * until end() is called.
25808 * @param {Object} config The config to pass to the Layout
25809 * @param {Field} field1 (optional)
25810 * @param {Field} field2 (optional)
25811 * @param {Field} etc (optional)
25812 * @return Layout The container object
25814 container : function(c){
25815 var l = new Roo.form.Layout(c);
25817 if(arguments.length > 1){ // duplicate code required because of Opera
25818 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25825 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25826 * @param {Object} container A Roo.form.Layout or subclass of Layout
25827 * @return {Form} this
25829 start : function(c){
25830 // cascade label info
25831 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25832 this.active.stack.push(c);
25833 c.ownerCt = this.active;
25839 * Closes the current open container
25840 * @return {Form} this
25843 if(this.active == this.root){
25846 this.active = this.active.ownerCt;
25851 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
25852 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25853 * as the label of the field.
25854 * @param {Field} field1
25855 * @param {Field} field2 (optional)
25856 * @param {Field} etc. (optional)
25857 * @return {Form} this
25860 this.active.stack.push.apply(this.active.stack, arguments);
25861 this.allItems.push.apply(this.allItems,arguments);
25863 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25864 if(a[i].isFormField){
25869 Roo.form.Form.superclass.add.apply(this, r);
25879 * Find any element that has been added to a form, using it's ID or name
25880 * This can include framesets, columns etc. along with regular fields..
25881 * @param {String} id - id or name to find.
25883 * @return {Element} e - or false if nothing found.
25885 findbyId : function(id)
25891 Roo.each(this.allItems, function(f){
25892 if (f.id == id || f.name == id ){
25903 * Render this form into the passed container. This should only be called once!
25904 * @param {String/HTMLElement/Element} container The element this component should be rendered into
25905 * @return {Form} this
25907 render : function(ct)
25913 var o = this.autoCreate || {
25915 method : this.method || 'POST',
25916 id : this.id || Roo.id()
25918 this.initEl(ct.createChild(o));
25920 this.root.render(this.el);
25924 this.items.each(function(f){
25925 f.render('x-form-el-'+f.id);
25928 if(this.buttons.length > 0){
25929 // tables are required to maintain order and for correct IE layout
25930 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25931 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25932 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25934 var tr = tb.getElementsByTagName('tr')[0];
25935 for(var i = 0, len = this.buttons.length; i < len; i++) {
25936 var b = this.buttons[i];
25937 var td = document.createElement('td');
25938 td.className = 'x-form-btn-td';
25939 b.render(tr.appendChild(td));
25942 if(this.monitorValid){ // initialize after render
25943 this.startMonitoring();
25945 this.fireEvent('rendered', this);
25950 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25951 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25952 * object or a valid Roo.DomHelper element config
25953 * @param {Function} handler The function called when the button is clicked
25954 * @param {Object} scope (optional) The scope of the handler function
25955 * @return {Roo.Button}
25957 addButton : function(config, handler, scope){
25961 minWidth: this.minButtonWidth,
25964 if(typeof config == "string"){
25967 Roo.apply(bc, config);
25969 var btn = new Roo.Button(null, bc);
25970 this.buttons.push(btn);
25975 * Adds a series of form elements (using the xtype property as the factory method.
25976 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25977 * @param {Object} config
25980 addxtype : function()
25982 var ar = Array.prototype.slice.call(arguments, 0);
25984 for(var i = 0; i < ar.length; i++) {
25986 continue; // skip -- if this happends something invalid got sent, we
25987 // should ignore it, as basically that interface element will not show up
25988 // and that should be pretty obvious!!
25991 if (Roo.form[ar[i].xtype]) {
25993 var fe = Roo.factory(ar[i], Roo.form);
25999 fe.store.form = this;
26004 this.allItems.push(fe);
26005 if (fe.items && fe.addxtype) {
26006 fe.addxtype.apply(fe, fe.items);
26016 // console.log('adding ' + ar[i].xtype);
26018 if (ar[i].xtype == 'Button') {
26019 //console.log('adding button');
26020 //console.log(ar[i]);
26021 this.addButton(ar[i]);
26022 this.allItems.push(fe);
26026 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26027 alert('end is not supported on xtype any more, use items');
26029 // //console.log('adding end');
26037 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26038 * option "monitorValid"
26040 startMonitoring : function(){
26043 Roo.TaskMgr.start({
26044 run : this.bindHandler,
26045 interval : this.monitorPoll || 200,
26052 * Stops monitoring of the valid state of this form
26054 stopMonitoring : function(){
26055 this.bound = false;
26059 bindHandler : function(){
26061 return false; // stops binding
26064 this.items.each(function(f){
26065 if(!f.isValid(true)){
26070 for(var i = 0, len = this.buttons.length; i < len; i++){
26071 var btn = this.buttons[i];
26072 if(btn.formBind === true && btn.disabled === valid){
26073 btn.setDisabled(!valid);
26076 this.fireEvent('clientvalidation', this, valid);
26090 Roo.Form = Roo.form.Form;
26093 * Ext JS Library 1.1.1
26094 * Copyright(c) 2006-2007, Ext JS, LLC.
26096 * Originally Released Under LGPL - original licence link has changed is not relivant.
26099 * <script type="text/javascript">
26102 // as we use this in bootstrap.
26103 Roo.namespace('Roo.form');
26105 * @class Roo.form.Action
26106 * Internal Class used to handle form actions
26108 * @param {Roo.form.BasicForm} el The form element or its id
26109 * @param {Object} config Configuration options
26114 // define the action interface
26115 Roo.form.Action = function(form, options){
26117 this.options = options || {};
26120 * Client Validation Failed
26123 Roo.form.Action.CLIENT_INVALID = 'client';
26125 * Server Validation Failed
26128 Roo.form.Action.SERVER_INVALID = 'server';
26130 * Connect to Server Failed
26133 Roo.form.Action.CONNECT_FAILURE = 'connect';
26135 * Reading Data from Server Failed
26138 Roo.form.Action.LOAD_FAILURE = 'load';
26140 Roo.form.Action.prototype = {
26142 failureType : undefined,
26143 response : undefined,
26144 result : undefined,
26146 // interface method
26147 run : function(options){
26151 // interface method
26152 success : function(response){
26156 // interface method
26157 handleResponse : function(response){
26161 // default connection failure
26162 failure : function(response){
26164 this.response = response;
26165 this.failureType = Roo.form.Action.CONNECT_FAILURE;
26166 this.form.afterAction(this, false);
26169 processResponse : function(response){
26170 this.response = response;
26171 if(!response.responseText){
26174 this.result = this.handleResponse(response);
26175 return this.result;
26178 // utility functions used internally
26179 getUrl : function(appendParams){
26180 var url = this.options.url || this.form.url || this.form.el.dom.action;
26182 var p = this.getParams();
26184 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26190 getMethod : function(){
26191 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26194 getParams : function(){
26195 var bp = this.form.baseParams;
26196 var p = this.options.params;
26198 if(typeof p == "object"){
26199 p = Roo.urlEncode(Roo.applyIf(p, bp));
26200 }else if(typeof p == 'string' && bp){
26201 p += '&' + Roo.urlEncode(bp);
26204 p = Roo.urlEncode(bp);
26209 createCallback : function(){
26211 success: this.success,
26212 failure: this.failure,
26214 timeout: (this.form.timeout*1000),
26215 upload: this.form.fileUpload ? this.success : undefined
26220 Roo.form.Action.Submit = function(form, options){
26221 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
26224 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
26227 haveProgress : false,
26228 uploadComplete : false,
26230 // uploadProgress indicator.
26231 uploadProgress : function()
26233 if (!this.form.progressUrl) {
26237 if (!this.haveProgress) {
26238 Roo.MessageBox.progress("Uploading", "Uploading");
26240 if (this.uploadComplete) {
26241 Roo.MessageBox.hide();
26245 this.haveProgress = true;
26247 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
26249 var c = new Roo.data.Connection();
26251 url : this.form.progressUrl,
26256 success : function(req){
26257 //console.log(data);
26261 rdata = Roo.decode(req.responseText)
26263 Roo.log("Invalid data from server..");
26267 if (!rdata || !rdata.success) {
26269 Roo.MessageBox.alert(Roo.encode(rdata));
26272 var data = rdata.data;
26274 if (this.uploadComplete) {
26275 Roo.MessageBox.hide();
26280 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
26281 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
26284 this.uploadProgress.defer(2000,this);
26287 failure: function(data) {
26288 Roo.log('progress url failed ');
26299 // run get Values on the form, so it syncs any secondary forms.
26300 this.form.getValues();
26302 var o = this.options;
26303 var method = this.getMethod();
26304 var isPost = method == 'POST';
26305 if(o.clientValidation === false || this.form.isValid()){
26307 if (this.form.progressUrl) {
26308 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26309 (new Date() * 1) + '' + Math.random());
26314 Roo.Ajax.request(Roo.apply(this.createCallback(), {
26315 form:this.form.el.dom,
26316 url:this.getUrl(!isPost),
26318 params:isPost ? this.getParams() : null,
26319 isUpload: this.form.fileUpload,
26320 formData : this.form.formData
26323 this.uploadProgress();
26325 }else if (o.clientValidation !== false){ // client validation failed
26326 this.failureType = Roo.form.Action.CLIENT_INVALID;
26327 this.form.afterAction(this, false);
26331 success : function(response)
26333 this.uploadComplete= true;
26334 if (this.haveProgress) {
26335 Roo.MessageBox.hide();
26339 var result = this.processResponse(response);
26340 if(result === true || result.success){
26341 this.form.afterAction(this, true);
26345 this.form.markInvalid(result.errors);
26346 this.failureType = Roo.form.Action.SERVER_INVALID;
26348 this.form.afterAction(this, false);
26350 failure : function(response)
26352 this.uploadComplete= true;
26353 if (this.haveProgress) {
26354 Roo.MessageBox.hide();
26357 this.response = response;
26358 this.failureType = Roo.form.Action.CONNECT_FAILURE;
26359 this.form.afterAction(this, false);
26362 handleResponse : function(response){
26363 if(this.form.errorReader){
26364 var rs = this.form.errorReader.read(response);
26367 for(var i = 0, len = rs.records.length; i < len; i++) {
26368 var r = rs.records[i];
26369 errors[i] = r.data;
26372 if(errors.length < 1){
26376 success : rs.success,
26382 ret = Roo.decode(response.responseText);
26386 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26396 Roo.form.Action.Load = function(form, options){
26397 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26398 this.reader = this.form.reader;
26401 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26406 Roo.Ajax.request(Roo.apply(
26407 this.createCallback(), {
26408 method:this.getMethod(),
26409 url:this.getUrl(false),
26410 params:this.getParams()
26414 success : function(response){
26416 var result = this.processResponse(response);
26417 if(result === true || !result.success || !result.data){
26418 this.failureType = Roo.form.Action.LOAD_FAILURE;
26419 this.form.afterAction(this, false);
26422 this.form.clearInvalid();
26423 this.form.setValues(result.data);
26424 this.form.afterAction(this, true);
26427 handleResponse : function(response){
26428 if(this.form.reader){
26429 var rs = this.form.reader.read(response);
26430 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26432 success : rs.success,
26436 return Roo.decode(response.responseText);
26440 Roo.form.Action.ACTION_TYPES = {
26441 'load' : Roo.form.Action.Load,
26442 'submit' : Roo.form.Action.Submit
26445 * Ext JS Library 1.1.1
26446 * Copyright(c) 2006-2007, Ext JS, LLC.
26448 * Originally Released Under LGPL - original licence link has changed is not relivant.
26451 * <script type="text/javascript">
26455 * @class Roo.form.Layout
26456 * @extends Roo.Component
26457 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26459 * @param {Object} config Configuration options
26461 Roo.form.Layout = function(config){
26463 if (config.items) {
26464 xitems = config.items;
26465 delete config.items;
26467 Roo.form.Layout.superclass.constructor.call(this, config);
26469 Roo.each(xitems, this.addxtype, this);
26473 Roo.extend(Roo.form.Layout, Roo.Component, {
26475 * @cfg {String/Object} autoCreate
26476 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26479 * @cfg {String/Object/Function} style
26480 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26481 * a function which returns such a specification.
26484 * @cfg {String} labelAlign
26485 * Valid values are "left," "top" and "right" (defaults to "left")
26488 * @cfg {Number} labelWidth
26489 * Fixed width in pixels of all field labels (defaults to undefined)
26492 * @cfg {Boolean} clear
26493 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26497 * @cfg {String} labelSeparator
26498 * The separator to use after field labels (defaults to ':')
26500 labelSeparator : ':',
26502 * @cfg {Boolean} hideLabels
26503 * True to suppress the display of field labels in this layout (defaults to false)
26505 hideLabels : false,
26508 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26513 onRender : function(ct, position){
26514 if(this.el){ // from markup
26515 this.el = Roo.get(this.el);
26516 }else { // generate
26517 var cfg = this.getAutoCreate();
26518 this.el = ct.createChild(cfg, position);
26521 this.el.applyStyles(this.style);
26523 if(this.labelAlign){
26524 this.el.addClass('x-form-label-'+this.labelAlign);
26526 if(this.hideLabels){
26527 this.labelStyle = "display:none";
26528 this.elementStyle = "padding-left:0;";
26530 if(typeof this.labelWidth == 'number'){
26531 this.labelStyle = "width:"+this.labelWidth+"px;";
26532 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26534 if(this.labelAlign == 'top'){
26535 this.labelStyle = "width:auto;";
26536 this.elementStyle = "padding-left:0;";
26539 var stack = this.stack;
26540 var slen = stack.length;
26542 if(!this.fieldTpl){
26543 var t = new Roo.Template(
26544 '<div class="x-form-item {5}">',
26545 '<label for="{0}" style="{2}">{1}{4}</label>',
26546 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26548 '</div><div class="x-form-clear-left"></div>'
26550 t.disableFormats = true;
26552 Roo.form.Layout.prototype.fieldTpl = t;
26554 for(var i = 0; i < slen; i++) {
26555 if(stack[i].isFormField){
26556 this.renderField(stack[i]);
26558 this.renderComponent(stack[i]);
26563 this.el.createChild({cls:'x-form-clear'});
26568 renderField : function(f){
26569 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26572 f.labelStyle||this.labelStyle||'', //2
26573 this.elementStyle||'', //3
26574 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26575 f.itemCls||this.itemCls||'' //5
26576 ], true).getPrevSibling());
26580 renderComponent : function(c){
26581 c.render(c.isLayout ? this.el : this.el.createChild());
26584 * Adds a object form elements (using the xtype property as the factory method.)
26585 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
26586 * @param {Object} config
26588 addxtype : function(o)
26590 // create the lement.
26591 o.form = this.form;
26592 var fe = Roo.factory(o, Roo.form);
26593 this.form.allItems.push(fe);
26594 this.stack.push(fe);
26596 if (fe.isFormField) {
26597 this.form.items.add(fe);
26605 * @class Roo.form.Column
26606 * @extends Roo.form.Layout
26607 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26609 * @param {Object} config Configuration options
26611 Roo.form.Column = function(config){
26612 Roo.form.Column.superclass.constructor.call(this, config);
26615 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26617 * @cfg {Number/String} width
26618 * The fixed width of the column in pixels or CSS value (defaults to "auto")
26621 * @cfg {String/Object} autoCreate
26622 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26626 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26629 onRender : function(ct, position){
26630 Roo.form.Column.superclass.onRender.call(this, ct, position);
26632 this.el.setWidth(this.width);
26639 * @class Roo.form.Row
26640 * @extends Roo.form.Layout
26641 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26643 * @param {Object} config Configuration options
26647 Roo.form.Row = function(config){
26648 Roo.form.Row.superclass.constructor.call(this, config);
26651 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26653 * @cfg {Number/String} width
26654 * The fixed width of the column in pixels or CSS value (defaults to "auto")
26657 * @cfg {Number/String} height
26658 * The fixed height of the column in pixels or CSS value (defaults to "auto")
26660 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26664 onRender : function(ct, position){
26665 //console.log('row render');
26667 var t = new Roo.Template(
26668 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26669 '<label for="{0}" style="{2}">{1}{4}</label>',
26670 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26674 t.disableFormats = true;
26676 Roo.form.Layout.prototype.rowTpl = t;
26678 this.fieldTpl = this.rowTpl;
26680 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26681 var labelWidth = 100;
26683 if ((this.labelAlign != 'top')) {
26684 if (typeof this.labelWidth == 'number') {
26685 labelWidth = this.labelWidth
26687 this.padWidth = 20 + labelWidth;
26691 Roo.form.Column.superclass.onRender.call(this, ct, position);
26693 this.el.setWidth(this.width);
26696 this.el.setHeight(this.height);
26701 renderField : function(f){
26702 f.fieldEl = this.fieldTpl.append(this.el, [
26703 f.id, f.fieldLabel,
26704 f.labelStyle||this.labelStyle||'',
26705 this.elementStyle||'',
26706 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26707 f.itemCls||this.itemCls||'',
26708 f.width ? f.width + this.padWidth : 160 + this.padWidth
26715 * @class Roo.form.FieldSet
26716 * @extends Roo.form.Layout
26717 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26719 * @param {Object} config Configuration options
26721 Roo.form.FieldSet = function(config){
26722 Roo.form.FieldSet.superclass.constructor.call(this, config);
26725 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26727 * @cfg {String} legend
26728 * The text to display as the legend for the FieldSet (defaults to '')
26731 * @cfg {String/Object} autoCreate
26732 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26736 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26739 onRender : function(ct, position){
26740 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26742 this.setLegend(this.legend);
26747 setLegend : function(text){
26749 this.el.child('legend').update(text);
26754 * Ext JS Library 1.1.1
26755 * Copyright(c) 2006-2007, Ext JS, LLC.
26757 * Originally Released Under LGPL - original licence link has changed is not relivant.
26760 * <script type="text/javascript">
26763 * @class Roo.form.VTypes
26764 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26767 Roo.form.VTypes = function(){
26768 // closure these in so they are only created once.
26769 var alpha = /^[a-zA-Z_]+$/;
26770 var alphanum = /^[a-zA-Z0-9_]+$/;
26771 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26772 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26774 // All these messages and functions are configurable
26777 * The function used to validate email addresses
26778 * @param {String} value The email address
26780 'email' : function(v){
26781 return email.test(v);
26784 * The error text to display when the email validation function returns false
26787 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26789 * The keystroke filter mask to be applied on email input
26792 'emailMask' : /[a-z0-9_\.\-@]/i,
26795 * The function used to validate URLs
26796 * @param {String} value The URL
26798 'url' : function(v){
26799 return url.test(v);
26802 * The error text to display when the url validation function returns false
26805 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26808 * The function used to validate alpha values
26809 * @param {String} value The value
26811 'alpha' : function(v){
26812 return alpha.test(v);
26815 * The error text to display when the alpha validation function returns false
26818 'alphaText' : 'This field should only contain letters and _',
26820 * The keystroke filter mask to be applied on alpha input
26823 'alphaMask' : /[a-z_]/i,
26826 * The function used to validate alphanumeric values
26827 * @param {String} value The value
26829 'alphanum' : function(v){
26830 return alphanum.test(v);
26833 * The error text to display when the alphanumeric validation function returns false
26836 'alphanumText' : 'This field should only contain letters, numbers and _',
26838 * The keystroke filter mask to be applied on alphanumeric input
26841 'alphanumMask' : /[a-z0-9_]/i
26843 }();//<script type="text/javascript">
26846 * @class Roo.form.FCKeditor
26847 * @extends Roo.form.TextArea
26848 * Wrapper around the FCKEditor http://www.fckeditor.net
26850 * Creates a new FCKeditor
26851 * @param {Object} config Configuration options
26853 Roo.form.FCKeditor = function(config){
26854 Roo.form.FCKeditor.superclass.constructor.call(this, config);
26857 * @event editorinit
26858 * Fired when the editor is initialized - you can add extra handlers here..
26859 * @param {FCKeditor} this
26860 * @param {Object} the FCK object.
26867 Roo.form.FCKeditor.editors = { };
26868 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26870 //defaultAutoCreate : {
26871 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
26875 * @cfg {Object} fck options - see fck manual for details.
26880 * @cfg {Object} fck toolbar set (Basic or Default)
26882 toolbarSet : 'Basic',
26884 * @cfg {Object} fck BasePath
26886 basePath : '/fckeditor/',
26894 onRender : function(ct, position)
26897 this.defaultAutoCreate = {
26899 style:"width:300px;height:60px;",
26900 autocomplete: "new-password"
26903 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26906 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26907 if(this.preventScrollbars){
26908 this.el.setStyle("overflow", "hidden");
26910 this.el.setHeight(this.growMin);
26913 //console.log('onrender' + this.getId() );
26914 Roo.form.FCKeditor.editors[this.getId()] = this;
26917 this.replaceTextarea() ;
26921 getEditor : function() {
26922 return this.fckEditor;
26925 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
26926 * @param {Mixed} value The value to set
26930 setValue : function(value)
26932 //console.log('setValue: ' + value);
26934 if(typeof(value) == 'undefined') { // not sure why this is happending...
26937 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26939 //if(!this.el || !this.getEditor()) {
26940 // this.value = value;
26941 //this.setValue.defer(100,this,[value]);
26945 if(!this.getEditor()) {
26949 this.getEditor().SetData(value);
26956 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
26957 * @return {Mixed} value The field value
26959 getValue : function()
26962 if (this.frame && this.frame.dom.style.display == 'none') {
26963 return Roo.form.FCKeditor.superclass.getValue.call(this);
26966 if(!this.el || !this.getEditor()) {
26968 // this.getValue.defer(100,this);
26973 var value=this.getEditor().GetData();
26974 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26975 return Roo.form.FCKeditor.superclass.getValue.call(this);
26981 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
26982 * @return {Mixed} value The field value
26984 getRawValue : function()
26986 if (this.frame && this.frame.dom.style.display == 'none') {
26987 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26990 if(!this.el || !this.getEditor()) {
26991 //this.getRawValue.defer(100,this);
26998 var value=this.getEditor().GetData();
26999 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27000 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27004 setSize : function(w,h) {
27008 //if (this.frame && this.frame.dom.style.display == 'none') {
27009 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27012 //if(!this.el || !this.getEditor()) {
27013 // this.setSize.defer(100,this, [w,h]);
27019 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27021 this.frame.dom.setAttribute('width', w);
27022 this.frame.dom.setAttribute('height', h);
27023 this.frame.setSize(w,h);
27027 toggleSourceEdit : function(value) {
27031 this.el.dom.style.display = value ? '' : 'none';
27032 this.frame.dom.style.display = value ? 'none' : '';
27037 focus: function(tag)
27039 if (this.frame.dom.style.display == 'none') {
27040 return Roo.form.FCKeditor.superclass.focus.call(this);
27042 if(!this.el || !this.getEditor()) {
27043 this.focus.defer(100,this, [tag]);
27050 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27051 this.getEditor().Focus();
27053 if (!this.getEditor().Selection.GetSelection()) {
27054 this.focus.defer(100,this, [tag]);
27059 var r = this.getEditor().EditorDocument.createRange();
27060 r.setStart(tgs[0],0);
27061 r.setEnd(tgs[0],0);
27062 this.getEditor().Selection.GetSelection().removeAllRanges();
27063 this.getEditor().Selection.GetSelection().addRange(r);
27064 this.getEditor().Focus();
27071 replaceTextarea : function()
27073 if ( document.getElementById( this.getId() + '___Frame' ) ) {
27076 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27078 // We must check the elements firstly using the Id and then the name.
27079 var oTextarea = document.getElementById( this.getId() );
27081 var colElementsByName = document.getElementsByName( this.getId() ) ;
27083 oTextarea.style.display = 'none' ;
27085 if ( oTextarea.tabIndex ) {
27086 this.TabIndex = oTextarea.tabIndex ;
27089 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27090 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27091 this.frame = Roo.get(this.getId() + '___Frame')
27094 _getConfigHtml : function()
27098 for ( var o in this.fckconfig ) {
27099 sConfig += sConfig.length > 0 ? '&' : '';
27100 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27103 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27107 _getIFrameHtml : function()
27109 var sFile = 'fckeditor.html' ;
27110 /* no idea what this is about..
27113 if ( (/fcksource=true/i).test( window.top.location.search ) )
27114 sFile = 'fckeditor.original.html' ;
27119 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27120 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
27123 var html = '<iframe id="' + this.getId() +
27124 '___Frame" src="' + sLink +
27125 '" width="' + this.width +
27126 '" height="' + this.height + '"' +
27127 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
27128 ' frameborder="0" scrolling="no"></iframe>' ;
27133 _insertHtmlBefore : function( html, element )
27135 if ( element.insertAdjacentHTML ) {
27137 element.insertAdjacentHTML( 'beforeBegin', html ) ;
27139 var oRange = document.createRange() ;
27140 oRange.setStartBefore( element ) ;
27141 var oFragment = oRange.createContextualFragment( html );
27142 element.parentNode.insertBefore( oFragment, element ) ;
27155 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27157 function FCKeditor_OnComplete(editorInstance){
27158 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27159 f.fckEditor = editorInstance;
27160 //console.log("loaded");
27161 f.fireEvent('editorinit', f, editorInstance);
27181 //<script type="text/javascript">
27183 * @class Roo.form.GridField
27184 * @extends Roo.form.Field
27185 * Embed a grid (or editable grid into a form)
27188 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
27190 * xgrid.store = Roo.data.Store
27191 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
27192 * xgrid.store.reader = Roo.data.JsonReader
27196 * Creates a new GridField
27197 * @param {Object} config Configuration options
27199 Roo.form.GridField = function(config){
27200 Roo.form.GridField.superclass.constructor.call(this, config);
27204 Roo.extend(Roo.form.GridField, Roo.form.Field, {
27206 * @cfg {Number} width - used to restrict width of grid..
27210 * @cfg {Number} height - used to restrict height of grid..
27214 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
27220 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27221 * {tag: "input", type: "checkbox", autocomplete: "off"})
27223 // defaultAutoCreate : { tag: 'div' },
27224 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
27226 * @cfg {String} addTitle Text to include for adding a title.
27230 onResize : function(){
27231 Roo.form.Field.superclass.onResize.apply(this, arguments);
27234 initEvents : function(){
27235 // Roo.form.Checkbox.superclass.initEvents.call(this);
27236 // has no events...
27241 getResizeEl : function(){
27245 getPositionEl : function(){
27250 onRender : function(ct, position){
27252 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
27253 var style = this.style;
27256 Roo.form.GridField.superclass.onRender.call(this, ct, position);
27257 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
27258 this.viewEl = this.wrap.createChild({ tag: 'div' });
27260 this.viewEl.applyStyles(style);
27263 this.viewEl.setWidth(this.width);
27266 this.viewEl.setHeight(this.height);
27268 //if(this.inputValue !== undefined){
27269 //this.setValue(this.value);
27272 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
27275 this.grid.render();
27276 this.grid.getDataSource().on('remove', this.refreshValue, this);
27277 this.grid.getDataSource().on('update', this.refreshValue, this);
27278 this.grid.on('afteredit', this.refreshValue, this);
27284 * Sets the value of the item.
27285 * @param {String} either an object or a string..
27287 setValue : function(v){
27289 v = v || []; // empty set..
27290 // this does not seem smart - it really only affects memoryproxy grids..
27291 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27292 var ds = this.grid.getDataSource();
27293 // assumes a json reader..
27295 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
27296 ds.loadData( data);
27298 // clear selection so it does not get stale.
27299 if (this.grid.sm) {
27300 this.grid.sm.clearSelections();
27303 Roo.form.GridField.superclass.setValue.call(this, v);
27304 this.refreshValue();
27305 // should load data in the grid really....
27309 refreshValue: function() {
27311 this.grid.getDataSource().each(function(r) {
27314 this.el.dom.value = Roo.encode(val);
27322 * Ext JS Library 1.1.1
27323 * Copyright(c) 2006-2007, Ext JS, LLC.
27325 * Originally Released Under LGPL - original licence link has changed is not relivant.
27328 * <script type="text/javascript">
27331 * @class Roo.form.DisplayField
27332 * @extends Roo.form.Field
27333 * A generic Field to display non-editable data.
27334 * @cfg {Boolean} closable (true|false) default false
27336 * Creates a new Display Field item.
27337 * @param {Object} config Configuration options
27339 Roo.form.DisplayField = function(config){
27340 Roo.form.DisplayField.superclass.constructor.call(this, config);
27345 * Fires after the click the close btn
27346 * @param {Roo.form.DisplayField} this
27352 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
27353 inputType: 'hidden',
27359 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27361 focusClass : undefined,
27363 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27365 fieldClass: 'x-form-field',
27368 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27370 valueRenderer: undefined,
27374 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27375 * {tag: "input", type: "checkbox", autocomplete: "off"})
27378 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27382 onResize : function(){
27383 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27387 initEvents : function(){
27388 // Roo.form.Checkbox.superclass.initEvents.call(this);
27389 // has no events...
27392 this.closeEl.on('click', this.onClose, this);
27398 getResizeEl : function(){
27402 getPositionEl : function(){
27407 onRender : function(ct, position){
27409 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27410 //if(this.inputValue !== undefined){
27411 this.wrap = this.el.wrap();
27413 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27416 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27419 if (this.bodyStyle) {
27420 this.viewEl.applyStyles(this.bodyStyle);
27422 //this.viewEl.setStyle('padding', '2px');
27424 this.setValue(this.value);
27429 initValue : Roo.emptyFn,
27434 onClick : function(){
27439 * Sets the checked state of the checkbox.
27440 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27442 setValue : function(v){
27444 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
27445 // this might be called before we have a dom element..
27446 if (!this.viewEl) {
27449 this.viewEl.dom.innerHTML = html;
27450 Roo.form.DisplayField.superclass.setValue.call(this, v);
27454 onClose : function(e)
27456 e.preventDefault();
27458 this.fireEvent('close', this);
27467 * @class Roo.form.DayPicker
27468 * @extends Roo.form.Field
27469 * A Day picker show [M] [T] [W] ....
27471 * Creates a new Day Picker
27472 * @param {Object} config Configuration options
27474 Roo.form.DayPicker= function(config){
27475 Roo.form.DayPicker.superclass.constructor.call(this, config);
27479 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
27481 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27483 focusClass : undefined,
27485 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27487 fieldClass: "x-form-field",
27490 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27491 * {tag: "input", type: "checkbox", autocomplete: "off"})
27493 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27496 actionMode : 'viewEl',
27500 inputType : 'hidden',
27503 inputElement: false, // real input element?
27504 basedOn: false, // ????
27506 isFormField: true, // not sure where this is needed!!!!
27508 onResize : function(){
27509 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27510 if(!this.boxLabel){
27511 this.el.alignTo(this.wrap, 'c-c');
27515 initEvents : function(){
27516 Roo.form.Checkbox.superclass.initEvents.call(this);
27517 this.el.on("click", this.onClick, this);
27518 this.el.on("change", this.onClick, this);
27522 getResizeEl : function(){
27526 getPositionEl : function(){
27532 onRender : function(ct, position){
27533 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27535 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27537 var r1 = '<table><tr>';
27538 var r2 = '<tr class="x-form-daypick-icons">';
27539 for (var i=0; i < 7; i++) {
27540 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27541 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
27544 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27545 viewEl.select('img').on('click', this.onClick, this);
27546 this.viewEl = viewEl;
27549 // this will not work on Chrome!!!
27550 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
27551 this.el.on('propertychange', this.setFromHidden, this); //ie
27559 initValue : Roo.emptyFn,
27562 * Returns the checked state of the checkbox.
27563 * @return {Boolean} True if checked, else false
27565 getValue : function(){
27566 return this.el.dom.value;
27571 onClick : function(e){
27572 //this.setChecked(!this.checked);
27573 Roo.get(e.target).toggleClass('x-menu-item-checked');
27574 this.refreshValue();
27575 //if(this.el.dom.checked != this.checked){
27576 // this.setValue(this.el.dom.checked);
27581 refreshValue : function()
27584 this.viewEl.select('img',true).each(function(e,i,n) {
27585 val += e.is(".x-menu-item-checked") ? String(n) : '';
27587 this.setValue(val, true);
27591 * Sets the checked state of the checkbox.
27592 * On is always based on a string comparison between inputValue and the param.
27593 * @param {Boolean/String} value - the value to set
27594 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27596 setValue : function(v,suppressEvent){
27597 if (!this.el.dom) {
27600 var old = this.el.dom.value ;
27601 this.el.dom.value = v;
27602 if (suppressEvent) {
27606 // update display..
27607 this.viewEl.select('img',true).each(function(e,i,n) {
27609 var on = e.is(".x-menu-item-checked");
27610 var newv = v.indexOf(String(n)) > -1;
27612 e.toggleClass('x-menu-item-checked');
27618 this.fireEvent('change', this, v, old);
27623 // handle setting of hidden value by some other method!!?!?
27624 setFromHidden: function()
27629 //console.log("SET FROM HIDDEN");
27630 //alert('setFrom hidden');
27631 this.setValue(this.el.dom.value);
27634 onDestroy : function()
27637 Roo.get(this.viewEl).remove();
27640 Roo.form.DayPicker.superclass.onDestroy.call(this);
27644 * RooJS Library 1.1.1
27645 * Copyright(c) 2008-2011 Alan Knowles
27652 * @class Roo.form.ComboCheck
27653 * @extends Roo.form.ComboBox
27654 * A combobox for multiple select items.
27656 * FIXME - could do with a reset button..
27659 * Create a new ComboCheck
27660 * @param {Object} config Configuration options
27662 Roo.form.ComboCheck = function(config){
27663 Roo.form.ComboCheck.superclass.constructor.call(this, config);
27664 // should verify some data...
27666 // hiddenName = required..
27667 // displayField = required
27668 // valudField == required
27669 var req= [ 'hiddenName', 'displayField', 'valueField' ];
27671 Roo.each(req, function(e) {
27672 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27673 throw "Roo.form.ComboCheck : missing value for: " + e;
27680 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27685 selectedClass: 'x-menu-item-checked',
27688 onRender : function(ct, position){
27694 var cls = 'x-combo-list';
27697 this.tpl = new Roo.Template({
27698 html : '<div class="'+cls+'-item x-menu-check-item">' +
27699 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
27700 '<span>{' + this.displayField + '}</span>' +
27707 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27708 this.view.singleSelect = false;
27709 this.view.multiSelect = true;
27710 this.view.toggleSelect = true;
27711 this.pageTb.add(new Roo.Toolbar.Fill(), {
27714 handler: function()
27721 onViewOver : function(e, t){
27727 onViewClick : function(doFocus,index){
27731 select: function () {
27732 //Roo.log("SELECT CALLED");
27735 selectByValue : function(xv, scrollIntoView){
27736 var ar = this.getValueArray();
27739 Roo.each(ar, function(v) {
27740 if(v === undefined || v === null){
27743 var r = this.findRecord(this.valueField, v);
27745 sels.push(this.store.indexOf(r))
27749 this.view.select(sels);
27755 onSelect : function(record, index){
27756 // Roo.log("onselect Called");
27757 // this is only called by the clear button now..
27758 this.view.clearSelections();
27759 this.setValue('[]');
27760 if (this.value != this.valueBefore) {
27761 this.fireEvent('change', this, this.value, this.valueBefore);
27762 this.valueBefore = this.value;
27765 getValueArray : function()
27770 //Roo.log(this.value);
27771 if (typeof(this.value) == 'undefined') {
27774 var ar = Roo.decode(this.value);
27775 return ar instanceof Array ? ar : []; //?? valid?
27778 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
27783 expand : function ()
27786 Roo.form.ComboCheck.superclass.expand.call(this);
27787 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27788 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27793 collapse : function(){
27794 Roo.form.ComboCheck.superclass.collapse.call(this);
27795 var sl = this.view.getSelectedIndexes();
27796 var st = this.store;
27800 Roo.each(sl, function(i) {
27802 nv.push(r.get(this.valueField));
27804 this.setValue(Roo.encode(nv));
27805 if (this.value != this.valueBefore) {
27807 this.fireEvent('change', this, this.value, this.valueBefore);
27808 this.valueBefore = this.value;
27813 setValue : function(v){
27817 var vals = this.getValueArray();
27819 Roo.each(vals, function(k) {
27820 var r = this.findRecord(this.valueField, k);
27822 tv.push(r.data[this.displayField]);
27823 }else if(this.valueNotFoundText !== undefined){
27824 tv.push( this.valueNotFoundText );
27829 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27830 this.hiddenField.value = v;
27836 * Ext JS Library 1.1.1
27837 * Copyright(c) 2006-2007, Ext JS, LLC.
27839 * Originally Released Under LGPL - original licence link has changed is not relivant.
27842 * <script type="text/javascript">
27846 * @class Roo.form.Signature
27847 * @extends Roo.form.Field
27851 * @param {Object} config Configuration options
27854 Roo.form.Signature = function(config){
27855 Roo.form.Signature.superclass.constructor.call(this, config);
27857 this.addEvents({// not in used??
27860 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27861 * @param {Roo.form.Signature} combo This combo box
27866 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27867 * @param {Roo.form.ComboBox} combo This combo box
27868 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27874 Roo.extend(Roo.form.Signature, Roo.form.Field, {
27876 * @cfg {Object} labels Label to use when rendering a form.
27880 * confirm : "Confirm"
27885 confirm : "Confirm"
27888 * @cfg {Number} width The signature panel width (defaults to 300)
27892 * @cfg {Number} height The signature panel height (defaults to 100)
27896 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27898 allowBlank : false,
27901 // {Object} signPanel The signature SVG panel element (defaults to {})
27903 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27904 isMouseDown : false,
27905 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27906 isConfirmed : false,
27907 // {String} signatureTmp SVG mapping string (defaults to empty string)
27911 defaultAutoCreate : { // modified by initCompnoent..
27917 onRender : function(ct, position){
27919 Roo.form.Signature.superclass.onRender.call(this, ct, position);
27921 this.wrap = this.el.wrap({
27922 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27925 this.createToolbar(this);
27926 this.signPanel = this.wrap.createChild({
27928 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27932 this.svgID = Roo.id();
27933 this.svgEl = this.signPanel.createChild({
27934 xmlns : 'http://www.w3.org/2000/svg',
27936 id : this.svgID + "-svg",
27938 height: this.height,
27939 viewBox: '0 0 '+this.width+' '+this.height,
27943 id: this.svgID + "-svg-r",
27945 height: this.height,
27950 id: this.svgID + "-svg-l",
27952 y1: (this.height*0.8), // start set the line in 80% of height
27953 x2: this.width, // end
27954 y2: (this.height*0.8), // end set the line in 80% of height
27956 'stroke-width': "1",
27957 'stroke-dasharray': "3",
27958 'shape-rendering': "crispEdges",
27959 'pointer-events': "none"
27963 id: this.svgID + "-svg-p",
27965 'stroke-width': "3",
27967 'pointer-events': 'none'
27972 this.svgBox = this.svgEl.dom.getScreenCTM();
27974 createSVG : function(){
27975 var svg = this.signPanel;
27976 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27979 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27980 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27981 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27982 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27983 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27984 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27985 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27988 isTouchEvent : function(e){
27989 return e.type.match(/^touch/);
27991 getCoords : function (e) {
27992 var pt = this.svgEl.dom.createSVGPoint();
27995 if (this.isTouchEvent(e)) {
27996 pt.x = e.targetTouches[0].clientX;
27997 pt.y = e.targetTouches[0].clientY;
27999 var a = this.svgEl.dom.getScreenCTM();
28000 var b = a.inverse();
28001 var mx = pt.matrixTransform(b);
28002 return mx.x + ',' + mx.y;
28004 //mouse event headler
28005 down : function (e) {
28006 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
28007 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
28009 this.isMouseDown = true;
28011 e.preventDefault();
28013 move : function (e) {
28014 if (this.isMouseDown) {
28015 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
28016 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
28019 e.preventDefault();
28021 up : function (e) {
28022 this.isMouseDown = false;
28023 var sp = this.signatureTmp.split(' ');
28026 if(!sp[sp.length-2].match(/^L/)){
28030 this.signatureTmp = sp.join(" ");
28033 if(this.getValue() != this.signatureTmp){
28034 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28035 this.isConfirmed = false;
28037 e.preventDefault();
28041 * Protected method that will not generally be called directly. It
28042 * is called when the editor creates its toolbar. Override this method if you need to
28043 * add custom toolbar buttons.
28044 * @param {HtmlEditor} editor
28046 createToolbar : function(editor){
28047 function btn(id, toggle, handler){
28048 var xid = fid + '-'+ id ;
28052 cls : 'x-btn-icon x-edit-'+id,
28053 enableToggle:toggle !== false,
28054 scope: editor, // was editor...
28055 handler:handler||editor.relayBtnCmd,
28056 clickEvent:'mousedown',
28057 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28063 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
28067 cls : ' x-signature-btn x-signature-'+id,
28068 scope: editor, // was editor...
28069 handler: this.reset,
28070 clickEvent:'mousedown',
28071 text: this.labels.clear
28078 cls : ' x-signature-btn x-signature-'+id,
28079 scope: editor, // was editor...
28080 handler: this.confirmHandler,
28081 clickEvent:'mousedown',
28082 text: this.labels.confirm
28089 * when user is clicked confirm then show this image.....
28091 * @return {String} Image Data URI
28093 getImageDataURI : function(){
28094 var svg = this.svgEl.dom.parentNode.innerHTML;
28095 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
28100 * @return {Boolean} this.isConfirmed
28102 getConfirmed : function(){
28103 return this.isConfirmed;
28107 * @return {Number} this.width
28109 getWidth : function(){
28114 * @return {Number} this.height
28116 getHeight : function(){
28117 return this.height;
28120 getSignature : function(){
28121 return this.signatureTmp;
28124 reset : function(){
28125 this.signatureTmp = '';
28126 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28127 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
28128 this.isConfirmed = false;
28129 Roo.form.Signature.superclass.reset.call(this);
28131 setSignature : function(s){
28132 this.signatureTmp = s;
28133 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28134 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
28136 this.isConfirmed = false;
28137 Roo.form.Signature.superclass.reset.call(this);
28140 // Roo.log(this.signPanel.dom.contentWindow.up())
28143 setConfirmed : function(){
28147 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
28150 confirmHandler : function(){
28151 if(!this.getSignature()){
28155 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
28156 this.setValue(this.getSignature());
28157 this.isConfirmed = true;
28159 this.fireEvent('confirm', this);
28162 // Subclasses should provide the validation implementation by overriding this
28163 validateValue : function(value){
28164 if(this.allowBlank){
28168 if(this.isConfirmed){
28175 * Ext JS Library 1.1.1
28176 * Copyright(c) 2006-2007, Ext JS, LLC.
28178 * Originally Released Under LGPL - original licence link has changed is not relivant.
28181 * <script type="text/javascript">
28186 * @class Roo.form.ComboBox
28187 * @extends Roo.form.TriggerField
28188 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
28190 * Create a new ComboBox.
28191 * @param {Object} config Configuration options
28193 Roo.form.Select = function(config){
28194 Roo.form.Select.superclass.constructor.call(this, config);
28198 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
28200 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
28203 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
28204 * rendering into an Roo.Editor, defaults to false)
28207 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
28208 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
28211 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
28214 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
28215 * the dropdown list (defaults to undefined, with no header element)
28219 * @cfg {String/Roo.Template} tpl The template to use to render the output
28223 defaultAutoCreate : {tag: "select" },
28225 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
28227 listWidth: undefined,
28229 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
28230 * mode = 'remote' or 'text' if mode = 'local')
28232 displayField: undefined,
28234 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
28235 * mode = 'remote' or 'value' if mode = 'local').
28236 * Note: use of a valueField requires the user make a selection
28237 * in order for a value to be mapped.
28239 valueField: undefined,
28243 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
28244 * field's data value (defaults to the underlying DOM element's name)
28246 hiddenName: undefined,
28248 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
28252 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
28254 selectedClass: 'x-combo-selected',
28256 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
28257 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
28258 * which displays a downward arrow icon).
28260 triggerClass : 'x-form-arrow-trigger',
28262 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28266 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
28267 * anchor positions (defaults to 'tl-bl')
28269 listAlign: 'tl-bl?',
28271 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
28275 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
28276 * query specified by the allQuery config option (defaults to 'query')
28278 triggerAction: 'query',
28280 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
28281 * (defaults to 4, does not apply if editable = false)
28285 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
28286 * delay (typeAheadDelay) if it matches a known value (defaults to false)
28290 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
28291 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
28295 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28296 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
28300 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
28301 * when editable = true (defaults to false)
28303 selectOnFocus:false,
28305 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28307 queryParam: 'query',
28309 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
28310 * when mode = 'remote' (defaults to 'Loading...')
28312 loadingText: 'Loading...',
28314 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28318 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28322 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28323 * traditional select (defaults to true)
28327 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28331 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28335 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28336 * listWidth has a higher value)
28340 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28341 * allow the user to set arbitrary text into the field (defaults to false)
28343 forceSelection:false,
28345 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28346 * if typeAhead = true (defaults to 250)
28348 typeAheadDelay : 250,
28350 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28351 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28353 valueNotFoundText : undefined,
28356 * @cfg {String} defaultValue The value displayed after loading the store.
28361 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28363 blockFocus : false,
28366 * @cfg {Boolean} disableClear Disable showing of clear button.
28368 disableClear : false,
28370 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
28372 alwaysQuery : false,
28378 // element that contains real text value.. (when hidden is used..)
28381 onRender : function(ct, position){
28382 Roo.form.Field.prototype.onRender.call(this, ct, position);
28385 this.store.on('beforeload', this.onBeforeLoad, this);
28386 this.store.on('load', this.onLoad, this);
28387 this.store.on('loadexception', this.onLoadException, this);
28388 this.store.load({});
28396 initEvents : function(){
28397 //Roo.form.ComboBox.superclass.initEvents.call(this);
28401 onDestroy : function(){
28404 this.store.un('beforeload', this.onBeforeLoad, this);
28405 this.store.un('load', this.onLoad, this);
28406 this.store.un('loadexception', this.onLoadException, this);
28408 //Roo.form.ComboBox.superclass.onDestroy.call(this);
28412 fireKey : function(e){
28413 if(e.isNavKeyPress() && !this.list.isVisible()){
28414 this.fireEvent("specialkey", this, e);
28419 onResize: function(w, h){
28427 * Allow or prevent the user from directly editing the field text. If false is passed,
28428 * the user will only be able to select from the items defined in the dropdown list. This method
28429 * is the runtime equivalent of setting the 'editable' config option at config time.
28430 * @param {Boolean} value True to allow the user to directly edit the field text
28432 setEditable : function(value){
28437 onBeforeLoad : function(){
28439 Roo.log("Select before load");
28442 this.innerList.update(this.loadingText ?
28443 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28444 //this.restrictHeight();
28445 this.selectedIndex = -1;
28449 onLoad : function(){
28452 var dom = this.el.dom;
28453 dom.innerHTML = '';
28454 var od = dom.ownerDocument;
28456 if (this.emptyText) {
28457 var op = od.createElement('option');
28458 op.setAttribute('value', '');
28459 op.innerHTML = String.format('{0}', this.emptyText);
28460 dom.appendChild(op);
28462 if(this.store.getCount() > 0){
28464 var vf = this.valueField;
28465 var df = this.displayField;
28466 this.store.data.each(function(r) {
28467 // which colmsn to use... testing - cdoe / title..
28468 var op = od.createElement('option');
28469 op.setAttribute('value', r.data[vf]);
28470 op.innerHTML = String.format('{0}', r.data[df]);
28471 dom.appendChild(op);
28473 if (typeof(this.defaultValue != 'undefined')) {
28474 this.setValue(this.defaultValue);
28479 //this.onEmptyResults();
28484 onLoadException : function()
28486 dom.innerHTML = '';
28488 Roo.log("Select on load exception");
28492 Roo.log(this.store.reader.jsonData);
28493 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28494 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28500 onTypeAhead : function(){
28505 onSelect : function(record, index){
28506 Roo.log('on select?');
28508 if(this.fireEvent('beforeselect', this, record, index) !== false){
28509 this.setFromData(index > -1 ? record.data : false);
28511 this.fireEvent('select', this, record, index);
28516 * Returns the currently selected field value or empty string if no value is set.
28517 * @return {String} value The selected value
28519 getValue : function(){
28520 var dom = this.el.dom;
28521 this.value = dom.options[dom.selectedIndex].value;
28527 * Clears any text/value currently set in the field
28529 clearValue : function(){
28531 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28536 * Sets the specified value into the field. If the value finds a match, the corresponding record text
28537 * will be displayed in the field. If the value does not match the data value of an existing item,
28538 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28539 * Otherwise the field will be blank (although the value will still be set).
28540 * @param {String} value The value to match
28542 setValue : function(v){
28543 var d = this.el.dom;
28544 for (var i =0; i < d.options.length;i++) {
28545 if (v == d.options[i].value) {
28546 d.selectedIndex = i;
28554 * @property {Object} the last set data for the element
28559 * Sets the value of the field based on a object which is related to the record format for the store.
28560 * @param {Object} value the value to set as. or false on reset?
28562 setFromData : function(o){
28563 Roo.log('setfrom data?');
28569 reset : function(){
28573 findRecord : function(prop, value){
28578 if(this.store.getCount() > 0){
28579 this.store.each(function(r){
28580 if(r.data[prop] == value){
28590 getName: function()
28592 // returns hidden if it's set..
28593 if (!this.rendered) {return ''};
28594 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
28602 onEmptyResults : function(){
28603 Roo.log('empty results');
28608 * Returns true if the dropdown list is expanded, else false.
28610 isExpanded : function(){
28615 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28616 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28617 * @param {String} value The data value of the item to select
28618 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28619 * selected item if it is not currently in view (defaults to true)
28620 * @return {Boolean} True if the value matched an item in the list, else false
28622 selectByValue : function(v, scrollIntoView){
28623 Roo.log('select By Value');
28626 if(v !== undefined && v !== null){
28627 var r = this.findRecord(this.valueField || this.displayField, v);
28629 this.select(this.store.indexOf(r), scrollIntoView);
28637 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28638 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28639 * @param {Number} index The zero-based index of the list item to select
28640 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28641 * selected item if it is not currently in view (defaults to true)
28643 select : function(index, scrollIntoView){
28644 Roo.log('select ');
28647 this.selectedIndex = index;
28648 this.view.select(index);
28649 if(scrollIntoView !== false){
28650 var el = this.view.getNode(index);
28652 this.innerList.scrollChildIntoView(el, false);
28660 validateBlur : function(){
28667 initQuery : function(){
28668 this.doQuery(this.getRawValue());
28672 doForce : function(){
28673 if(this.el.dom.value.length > 0){
28674 this.el.dom.value =
28675 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28681 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
28682 * query allowing the query action to be canceled if needed.
28683 * @param {String} query The SQL query to execute
28684 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28685 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
28686 * saved in the current store (defaults to false)
28688 doQuery : function(q, forceAll){
28690 Roo.log('doQuery?');
28691 if(q === undefined || q === null){
28696 forceAll: forceAll,
28700 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28704 forceAll = qe.forceAll;
28705 if(forceAll === true || (q.length >= this.minChars)){
28706 if(this.lastQuery != q || this.alwaysQuery){
28707 this.lastQuery = q;
28708 if(this.mode == 'local'){
28709 this.selectedIndex = -1;
28711 this.store.clearFilter();
28713 this.store.filter(this.displayField, q);
28717 this.store.baseParams[this.queryParam] = q;
28719 params: this.getParams(q)
28724 this.selectedIndex = -1;
28731 getParams : function(q){
28733 //p[this.queryParam] = q;
28736 p.limit = this.pageSize;
28742 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28744 collapse : function(){
28749 collapseIf : function(e){
28754 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28756 expand : function(){
28764 * @cfg {Boolean} grow
28768 * @cfg {Number} growMin
28772 * @cfg {Number} growMax
28780 setWidth : function()
28784 getResizeEl : function(){
28787 });//<script type="text/javasscript">
28791 * @class Roo.DDView
28792 * A DnD enabled version of Roo.View.
28793 * @param {Element/String} container The Element in which to create the View.
28794 * @param {String} tpl The template string used to create the markup for each element of the View
28795 * @param {Object} config The configuration properties. These include all the config options of
28796 * {@link Roo.View} plus some specific to this class.<br>
28798 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28799 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28801 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28802 .x-view-drag-insert-above {
28803 border-top:1px dotted #3366cc;
28805 .x-view-drag-insert-below {
28806 border-bottom:1px dotted #3366cc;
28812 Roo.DDView = function(container, tpl, config) {
28813 Roo.DDView.superclass.constructor.apply(this, arguments);
28814 this.getEl().setStyle("outline", "0px none");
28815 this.getEl().unselectable();
28816 if (this.dragGroup) {
28817 this.setDraggable(this.dragGroup.split(","));
28819 if (this.dropGroup) {
28820 this.setDroppable(this.dropGroup.split(","));
28822 if (this.deletable) {
28823 this.setDeletable();
28825 this.isDirtyFlag = false;
28831 Roo.extend(Roo.DDView, Roo.View, {
28832 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28833 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28834 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28835 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28839 reset: Roo.emptyFn,
28841 clearInvalid: Roo.form.Field.prototype.clearInvalid,
28843 validate: function() {
28847 destroy: function() {
28848 this.purgeListeners();
28849 this.getEl.removeAllListeners();
28850 this.getEl().remove();
28851 if (this.dragZone) {
28852 if (this.dragZone.destroy) {
28853 this.dragZone.destroy();
28856 if (this.dropZone) {
28857 if (this.dropZone.destroy) {
28858 this.dropZone.destroy();
28863 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28864 getName: function() {
28868 /** Loads the View from a JSON string representing the Records to put into the Store. */
28869 setValue: function(v) {
28871 throw "DDView.setValue(). DDView must be constructed with a valid Store";
28874 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28875 this.store.proxy = new Roo.data.MemoryProxy(data);
28879 /** @return {String} a parenthesised list of the ids of the Records in the View. */
28880 getValue: function() {
28882 this.store.each(function(rec) {
28883 result += rec.id + ',';
28885 return result.substr(0, result.length - 1) + ')';
28888 getIds: function() {
28889 var i = 0, result = new Array(this.store.getCount());
28890 this.store.each(function(rec) {
28891 result[i++] = rec.id;
28896 isDirty: function() {
28897 return this.isDirtyFlag;
28901 * Part of the Roo.dd.DropZone interface. If no target node is found, the
28902 * whole Element becomes the target, and this causes the drop gesture to append.
28904 getTargetFromEvent : function(e) {
28905 var target = e.getTarget();
28906 while ((target !== null) && (target.parentNode != this.el.dom)) {
28907 target = target.parentNode;
28910 target = this.el.dom.lastChild || this.el.dom;
28916 * Create the drag data which consists of an object which has the property "ddel" as
28917 * the drag proxy element.
28919 getDragData : function(e) {
28920 var target = this.findItemFromChild(e.getTarget());
28922 this.handleSelection(e);
28923 var selNodes = this.getSelectedNodes();
28926 copy: this.copy || (this.allowCopy && e.ctrlKey),
28930 var selectedIndices = this.getSelectedIndexes();
28931 for (var i = 0; i < selectedIndices.length; i++) {
28932 dragData.records.push(this.store.getAt(selectedIndices[i]));
28934 if (selNodes.length == 1) {
28935 dragData.ddel = target.cloneNode(true); // the div element
28937 var div = document.createElement('div'); // create the multi element drag "ghost"
28938 div.className = 'multi-proxy';
28939 for (var i = 0, len = selNodes.length; i < len; i++) {
28940 div.appendChild(selNodes[i].cloneNode(true));
28942 dragData.ddel = div;
28944 //console.log(dragData)
28945 //console.log(dragData.ddel.innerHTML)
28948 //console.log('nodragData')
28952 /** Specify to which ddGroup items in this DDView may be dragged. */
28953 setDraggable: function(ddGroup) {
28954 if (ddGroup instanceof Array) {
28955 Roo.each(ddGroup, this.setDraggable, this);
28958 if (this.dragZone) {
28959 this.dragZone.addToGroup(ddGroup);
28961 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28962 containerScroll: true,
28966 // Draggability implies selection. DragZone's mousedown selects the element.
28967 if (!this.multiSelect) { this.singleSelect = true; }
28969 // Wire the DragZone's handlers up to methods in *this*
28970 this.dragZone.getDragData = this.getDragData.createDelegate(this);
28974 /** Specify from which ddGroup this DDView accepts drops. */
28975 setDroppable: function(ddGroup) {
28976 if (ddGroup instanceof Array) {
28977 Roo.each(ddGroup, this.setDroppable, this);
28980 if (this.dropZone) {
28981 this.dropZone.addToGroup(ddGroup);
28983 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28984 containerScroll: true,
28988 // Wire the DropZone's handlers up to methods in *this*
28989 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28990 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28991 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28992 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28993 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28997 /** Decide whether to drop above or below a View node. */
28998 getDropPoint : function(e, n, dd){
28999 if (n == this.el.dom) { return "above"; }
29000 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
29001 var c = t + (b - t) / 2;
29002 var y = Roo.lib.Event.getPageY(e);
29010 onNodeEnter : function(n, dd, e, data){
29014 onNodeOver : function(n, dd, e, data){
29015 var pt = this.getDropPoint(e, n, dd);
29016 // set the insert point style on the target node
29017 var dragElClass = this.dropNotAllowed;
29020 if (pt == "above"){
29021 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
29022 targetElClass = "x-view-drag-insert-above";
29024 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
29025 targetElClass = "x-view-drag-insert-below";
29027 if (this.lastInsertClass != targetElClass){
29028 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
29029 this.lastInsertClass = targetElClass;
29032 return dragElClass;
29035 onNodeOut : function(n, dd, e, data){
29036 this.removeDropIndicators(n);
29039 onNodeDrop : function(n, dd, e, data){
29040 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
29043 var pt = this.getDropPoint(e, n, dd);
29044 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
29045 if (pt == "below") { insertAt++; }
29046 for (var i = 0; i < data.records.length; i++) {
29047 var r = data.records[i];
29048 var dup = this.store.getById(r.id);
29049 if (dup && (dd != this.dragZone)) {
29050 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
29053 this.store.insert(insertAt++, r.copy());
29055 data.source.isDirtyFlag = true;
29057 this.store.insert(insertAt++, r);
29059 this.isDirtyFlag = true;
29062 this.dragZone.cachedTarget = null;
29066 removeDropIndicators : function(n){
29068 Roo.fly(n).removeClass([
29069 "x-view-drag-insert-above",
29070 "x-view-drag-insert-below"]);
29071 this.lastInsertClass = "_noclass";
29076 * Utility method. Add a delete option to the DDView's context menu.
29077 * @param {String} imageUrl The URL of the "delete" icon image.
29079 setDeletable: function(imageUrl) {
29080 if (!this.singleSelect && !this.multiSelect) {
29081 this.singleSelect = true;
29083 var c = this.getContextMenu();
29084 this.contextMenu.on("itemclick", function(item) {
29087 this.remove(this.getSelectedIndexes());
29091 this.contextMenu.add({
29098 /** Return the context menu for this DDView. */
29099 getContextMenu: function() {
29100 if (!this.contextMenu) {
29101 // Create the View's context menu
29102 this.contextMenu = new Roo.menu.Menu({
29103 id: this.id + "-contextmenu"
29105 this.el.on("contextmenu", this.showContextMenu, this);
29107 return this.contextMenu;
29110 disableContextMenu: function() {
29111 if (this.contextMenu) {
29112 this.el.un("contextmenu", this.showContextMenu, this);
29116 showContextMenu: function(e, item) {
29117 item = this.findItemFromChild(e.getTarget());
29120 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
29121 this.contextMenu.showAt(e.getXY());
29126 * Remove {@link Roo.data.Record}s at the specified indices.
29127 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
29129 remove: function(selectedIndices) {
29130 selectedIndices = [].concat(selectedIndices);
29131 for (var i = 0; i < selectedIndices.length; i++) {
29132 var rec = this.store.getAt(selectedIndices[i]);
29133 this.store.remove(rec);
29138 * Double click fires the event, but also, if this is draggable, and there is only one other
29139 * related DropZone, it transfers the selected node.
29141 onDblClick : function(e){
29142 var item = this.findItemFromChild(e.getTarget());
29144 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
29147 if (this.dragGroup) {
29148 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
29149 while (targets.indexOf(this.dropZone) > -1) {
29150 targets.remove(this.dropZone);
29152 if (targets.length == 1) {
29153 this.dragZone.cachedTarget = null;
29154 var el = Roo.get(targets[0].getEl());
29155 var box = el.getBox(true);
29156 targets[0].onNodeDrop(el.dom, {
29158 xy: [box.x, box.y + box.height - 1]
29159 }, null, this.getDragData(e));
29165 handleSelection: function(e) {
29166 this.dragZone.cachedTarget = null;
29167 var item = this.findItemFromChild(e.getTarget());
29169 this.clearSelections(true);
29172 if (item && (this.multiSelect || this.singleSelect)){
29173 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
29174 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
29175 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
29176 this.unselect(item);
29178 this.select(item, this.multiSelect && e.ctrlKey);
29179 this.lastSelection = item;
29184 onItemClick : function(item, index, e){
29185 if(this.fireEvent("beforeclick", this, index, item, e) === false){
29191 unselect : function(nodeInfo, suppressEvent){
29192 var node = this.getNode(nodeInfo);
29193 if(node && this.isSelected(node)){
29194 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
29195 Roo.fly(node).removeClass(this.selectedClass);
29196 this.selections.remove(node);
29197 if(!suppressEvent){
29198 this.fireEvent("selectionchange", this, this.selections);
29206 * Ext JS Library 1.1.1
29207 * Copyright(c) 2006-2007, Ext JS, LLC.
29209 * Originally Released Under LGPL - original licence link has changed is not relivant.
29212 * <script type="text/javascript">
29216 * @class Roo.LayoutManager
29217 * @extends Roo.util.Observable
29218 * Base class for layout managers.
29220 Roo.LayoutManager = function(container, config){
29221 Roo.LayoutManager.superclass.constructor.call(this);
29222 this.el = Roo.get(container);
29223 // ie scrollbar fix
29224 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
29225 document.body.scroll = "no";
29226 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
29227 this.el.position('relative');
29229 this.id = this.el.id;
29230 this.el.addClass("x-layout-container");
29231 /** false to disable window resize monitoring @type Boolean */
29232 this.monitorWindowResize = true;
29237 * Fires when a layout is performed.
29238 * @param {Roo.LayoutManager} this
29242 * @event regionresized
29243 * Fires when the user resizes a region.
29244 * @param {Roo.LayoutRegion} region The resized region
29245 * @param {Number} newSize The new size (width for east/west, height for north/south)
29247 "regionresized" : true,
29249 * @event regioncollapsed
29250 * Fires when a region is collapsed.
29251 * @param {Roo.LayoutRegion} region The collapsed region
29253 "regioncollapsed" : true,
29255 * @event regionexpanded
29256 * Fires when a region is expanded.
29257 * @param {Roo.LayoutRegion} region The expanded region
29259 "regionexpanded" : true
29261 this.updating = false;
29262 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
29265 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
29267 * Returns true if this layout is currently being updated
29268 * @return {Boolean}
29270 isUpdating : function(){
29271 return this.updating;
29275 * Suspend the LayoutManager from doing auto-layouts while
29276 * making multiple add or remove calls
29278 beginUpdate : function(){
29279 this.updating = true;
29283 * Restore auto-layouts and optionally disable the manager from performing a layout
29284 * @param {Boolean} noLayout true to disable a layout update
29286 endUpdate : function(noLayout){
29287 this.updating = false;
29293 layout: function(){
29297 onRegionResized : function(region, newSize){
29298 this.fireEvent("regionresized", region, newSize);
29302 onRegionCollapsed : function(region){
29303 this.fireEvent("regioncollapsed", region);
29306 onRegionExpanded : function(region){
29307 this.fireEvent("regionexpanded", region);
29311 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29312 * performs box-model adjustments.
29313 * @return {Object} The size as an object {width: (the width), height: (the height)}
29315 getViewSize : function(){
29317 if(this.el.dom != document.body){
29318 size = this.el.getSize();
29320 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29322 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29323 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29328 * Returns the Element this layout is bound to.
29329 * @return {Roo.Element}
29331 getEl : function(){
29336 * Returns the specified region.
29337 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29338 * @return {Roo.LayoutRegion}
29340 getRegion : function(target){
29341 return this.regions[target.toLowerCase()];
29344 onWindowResize : function(){
29345 if(this.monitorWindowResize){
29351 * Ext JS Library 1.1.1
29352 * Copyright(c) 2006-2007, Ext JS, LLC.
29354 * Originally Released Under LGPL - original licence link has changed is not relivant.
29357 * <script type="text/javascript">
29360 * @class Roo.BorderLayout
29361 * @extends Roo.LayoutManager
29362 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29363 * please see: <br><br>
29364 * <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>
29365 * <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>
29368 var layout = new Roo.BorderLayout(document.body, {
29402 preferredTabWidth: 150
29407 var CP = Roo.ContentPanel;
29409 layout.beginUpdate();
29410 layout.add("north", new CP("north", "North"));
29411 layout.add("south", new CP("south", {title: "South", closable: true}));
29412 layout.add("west", new CP("west", {title: "West"}));
29413 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29414 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29415 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29416 layout.getRegion("center").showPanel("center1");
29417 layout.endUpdate();
29420 <b>The container the layout is rendered into can be either the body element or any other element.
29421 If it is not the body element, the container needs to either be an absolute positioned element,
29422 or you will need to add "position:relative" to the css of the container. You will also need to specify
29423 the container size if it is not the body element.</b>
29426 * Create a new BorderLayout
29427 * @param {String/HTMLElement/Element} container The container this layout is bound to
29428 * @param {Object} config Configuration options
29430 Roo.BorderLayout = function(container, config){
29431 config = config || {};
29432 Roo.BorderLayout.superclass.constructor.call(this, container, config);
29433 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29434 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29435 var target = this.factory.validRegions[i];
29436 if(config[target]){
29437 this.addRegion(target, config[target]);
29442 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29444 * Creates and adds a new region if it doesn't already exist.
29445 * @param {String} target The target region key (north, south, east, west or center).
29446 * @param {Object} config The regions config object
29447 * @return {BorderLayoutRegion} The new region
29449 addRegion : function(target, config){
29450 if(!this.regions[target]){
29451 var r = this.factory.create(target, this, config);
29452 this.bindRegion(target, r);
29454 return this.regions[target];
29458 bindRegion : function(name, r){
29459 this.regions[name] = r;
29460 r.on("visibilitychange", this.layout, this);
29461 r.on("paneladded", this.layout, this);
29462 r.on("panelremoved", this.layout, this);
29463 r.on("invalidated", this.layout, this);
29464 r.on("resized", this.onRegionResized, this);
29465 r.on("collapsed", this.onRegionCollapsed, this);
29466 r.on("expanded", this.onRegionExpanded, this);
29470 * Performs a layout update.
29472 layout : function(){
29473 if(this.updating) {
29476 var size = this.getViewSize();
29477 var w = size.width;
29478 var h = size.height;
29483 //var x = 0, y = 0;
29485 var rs = this.regions;
29486 var north = rs["north"];
29487 var south = rs["south"];
29488 var west = rs["west"];
29489 var east = rs["east"];
29490 var center = rs["center"];
29491 //if(this.hideOnLayout){ // not supported anymore
29492 //c.el.setStyle("display", "none");
29494 if(north && north.isVisible()){
29495 var b = north.getBox();
29496 var m = north.getMargins();
29497 b.width = w - (m.left+m.right);
29500 centerY = b.height + b.y + m.bottom;
29501 centerH -= centerY;
29502 north.updateBox(this.safeBox(b));
29504 if(south && south.isVisible()){
29505 var b = south.getBox();
29506 var m = south.getMargins();
29507 b.width = w - (m.left+m.right);
29509 var totalHeight = (b.height + m.top + m.bottom);
29510 b.y = h - totalHeight + m.top;
29511 centerH -= totalHeight;
29512 south.updateBox(this.safeBox(b));
29514 if(west && west.isVisible()){
29515 var b = west.getBox();
29516 var m = west.getMargins();
29517 b.height = centerH - (m.top+m.bottom);
29519 b.y = centerY + m.top;
29520 var totalWidth = (b.width + m.left + m.right);
29521 centerX += totalWidth;
29522 centerW -= totalWidth;
29523 west.updateBox(this.safeBox(b));
29525 if(east && east.isVisible()){
29526 var b = east.getBox();
29527 var m = east.getMargins();
29528 b.height = centerH - (m.top+m.bottom);
29529 var totalWidth = (b.width + m.left + m.right);
29530 b.x = w - totalWidth + m.left;
29531 b.y = centerY + m.top;
29532 centerW -= totalWidth;
29533 east.updateBox(this.safeBox(b));
29536 var m = center.getMargins();
29538 x: centerX + m.left,
29539 y: centerY + m.top,
29540 width: centerW - (m.left+m.right),
29541 height: centerH - (m.top+m.bottom)
29543 //if(this.hideOnLayout){
29544 //center.el.setStyle("display", "block");
29546 center.updateBox(this.safeBox(centerBox));
29549 this.fireEvent("layout", this);
29553 safeBox : function(box){
29554 box.width = Math.max(0, box.width);
29555 box.height = Math.max(0, box.height);
29560 * Adds a ContentPanel (or subclass) to this layout.
29561 * @param {String} target The target region key (north, south, east, west or center).
29562 * @param {Roo.ContentPanel} panel The panel to add
29563 * @return {Roo.ContentPanel} The added panel
29565 add : function(target, panel){
29567 target = target.toLowerCase();
29568 return this.regions[target].add(panel);
29572 * Remove a ContentPanel (or subclass) to this layout.
29573 * @param {String} target The target region key (north, south, east, west or center).
29574 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29575 * @return {Roo.ContentPanel} The removed panel
29577 remove : function(target, panel){
29578 target = target.toLowerCase();
29579 return this.regions[target].remove(panel);
29583 * Searches all regions for a panel with the specified id
29584 * @param {String} panelId
29585 * @return {Roo.ContentPanel} The panel or null if it wasn't found
29587 findPanel : function(panelId){
29588 var rs = this.regions;
29589 for(var target in rs){
29590 if(typeof rs[target] != "function"){
29591 var p = rs[target].getPanel(panelId);
29601 * Searches all regions for a panel with the specified id and activates (shows) it.
29602 * @param {String/ContentPanel} panelId The panels id or the panel itself
29603 * @return {Roo.ContentPanel} The shown panel or null
29605 showPanel : function(panelId) {
29606 var rs = this.regions;
29607 for(var target in rs){
29608 var r = rs[target];
29609 if(typeof r != "function"){
29610 if(r.hasPanel(panelId)){
29611 return r.showPanel(panelId);
29619 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29620 * @param {Roo.state.Provider} provider (optional) An alternate state provider
29622 restoreState : function(provider){
29624 provider = Roo.state.Manager;
29626 var sm = new Roo.LayoutStateManager();
29627 sm.init(this, provider);
29631 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
29632 * object should contain properties for each region to add ContentPanels to, and each property's value should be
29633 * a valid ContentPanel config object. Example:
29635 // Create the main layout
29636 var layout = new Roo.BorderLayout('main-ct', {
29647 // Create and add multiple ContentPanels at once via configs
29650 id: 'source-files',
29652 title:'Ext Source Files',
29665 * @param {Object} regions An object containing ContentPanel configs by region name
29667 batchAdd : function(regions){
29668 this.beginUpdate();
29669 for(var rname in regions){
29670 var lr = this.regions[rname];
29672 this.addTypedPanels(lr, regions[rname]);
29679 addTypedPanels : function(lr, ps){
29680 if(typeof ps == 'string'){
29681 lr.add(new Roo.ContentPanel(ps));
29683 else if(ps instanceof Array){
29684 for(var i =0, len = ps.length; i < len; i++){
29685 this.addTypedPanels(lr, ps[i]);
29688 else if(!ps.events){ // raw config?
29690 delete ps.el; // prevent conflict
29691 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29693 else { // panel object assumed!
29698 * Adds a xtype elements to the layout.
29702 xtype : 'ContentPanel',
29709 xtype : 'NestedLayoutPanel',
29715 items : [ ... list of content panels or nested layout panels.. ]
29719 * @param {Object} cfg Xtype definition of item to add.
29721 addxtype : function(cfg)
29723 // basically accepts a pannel...
29724 // can accept a layout region..!?!?
29725 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29727 if (!cfg.xtype.match(/Panel$/)) {
29732 if (typeof(cfg.region) == 'undefined') {
29733 Roo.log("Failed to add Panel, region was not set");
29737 var region = cfg.region;
29743 xitems = cfg.items;
29750 case 'ContentPanel': // ContentPanel (el, cfg)
29751 case 'ScrollPanel': // ContentPanel (el, cfg)
29753 if(cfg.autoCreate) {
29754 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29756 var el = this.el.createChild();
29757 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29760 this.add(region, ret);
29764 case 'TreePanel': // our new panel!
29765 cfg.el = this.el.createChild();
29766 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29767 this.add(region, ret);
29770 case 'NestedLayoutPanel':
29771 // create a new Layout (which is a Border Layout...
29772 var el = this.el.createChild();
29773 var clayout = cfg.layout;
29775 clayout.items = clayout.items || [];
29776 // replace this exitems with the clayout ones..
29777 xitems = clayout.items;
29780 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29781 cfg.background = false;
29783 var layout = new Roo.BorderLayout(el, clayout);
29785 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29786 //console.log('adding nested layout panel ' + cfg.toSource());
29787 this.add(region, ret);
29788 nb = {}; /// find first...
29793 // needs grid and region
29795 //var el = this.getRegion(region).el.createChild();
29796 var el = this.el.createChild();
29797 // create the grid first...
29799 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29801 if (region == 'center' && this.active ) {
29802 cfg.background = false;
29804 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29806 this.add(region, ret);
29807 if (cfg.background) {
29808 ret.on('activate', function(gp) {
29809 if (!gp.grid.rendered) {
29824 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29826 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29827 this.add(region, ret);
29830 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29834 // GridPanel (grid, cfg)
29837 this.beginUpdate();
29841 Roo.each(xitems, function(i) {
29842 region = nb && i.region ? i.region : false;
29844 var add = ret.addxtype(i);
29847 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29848 if (!i.background) {
29849 abn[region] = nb[region] ;
29856 // make the last non-background panel active..
29857 //if (nb) { Roo.log(abn); }
29860 for(var r in abn) {
29861 region = this.getRegion(r);
29863 // tried using nb[r], but it does not work..
29865 region.showPanel(abn[r]);
29876 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29877 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
29878 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29879 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
29882 var CP = Roo.ContentPanel;
29884 var layout = Roo.BorderLayout.create({
29888 panels: [new CP("north", "North")]
29897 panels: [new CP("west", {title: "West"})]
29906 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29915 panels: [new CP("south", {title: "South", closable: true})]
29922 preferredTabWidth: 150,
29924 new CP("center1", {title: "Close Me", closable: true}),
29925 new CP("center2", {title: "Center Panel", closable: false})
29930 layout.getRegion("center").showPanel("center1");
29935 Roo.BorderLayout.create = function(config, targetEl){
29936 var layout = new Roo.BorderLayout(targetEl || document.body, config);
29937 layout.beginUpdate();
29938 var regions = Roo.BorderLayout.RegionFactory.validRegions;
29939 for(var j = 0, jlen = regions.length; j < jlen; j++){
29940 var lr = regions[j];
29941 if(layout.regions[lr] && config[lr].panels){
29942 var r = layout.regions[lr];
29943 var ps = config[lr].panels;
29944 layout.addTypedPanels(r, ps);
29947 layout.endUpdate();
29952 Roo.BorderLayout.RegionFactory = {
29954 validRegions : ["north","south","east","west","center"],
29957 create : function(target, mgr, config){
29958 target = target.toLowerCase();
29959 if(config.lightweight || config.basic){
29960 return new Roo.BasicLayoutRegion(mgr, config, target);
29964 return new Roo.NorthLayoutRegion(mgr, config);
29966 return new Roo.SouthLayoutRegion(mgr, config);
29968 return new Roo.EastLayoutRegion(mgr, config);
29970 return new Roo.WestLayoutRegion(mgr, config);
29972 return new Roo.CenterLayoutRegion(mgr, config);
29974 throw 'Layout region "'+target+'" not supported.';
29978 * Ext JS Library 1.1.1
29979 * Copyright(c) 2006-2007, Ext JS, LLC.
29981 * Originally Released Under LGPL - original licence link has changed is not relivant.
29984 * <script type="text/javascript">
29988 * @class Roo.BasicLayoutRegion
29989 * @extends Roo.util.Observable
29990 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29991 * and does not have a titlebar, tabs or any other features. All it does is size and position
29992 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29994 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29996 this.position = pos;
29999 * @scope Roo.BasicLayoutRegion
30003 * @event beforeremove
30004 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
30005 * @param {Roo.LayoutRegion} this
30006 * @param {Roo.ContentPanel} panel The panel
30007 * @param {Object} e The cancel event object
30009 "beforeremove" : true,
30011 * @event invalidated
30012 * Fires when the layout for this region is changed.
30013 * @param {Roo.LayoutRegion} this
30015 "invalidated" : true,
30017 * @event visibilitychange
30018 * Fires when this region is shown or hidden
30019 * @param {Roo.LayoutRegion} this
30020 * @param {Boolean} visibility true or false
30022 "visibilitychange" : true,
30024 * @event paneladded
30025 * Fires when a panel is added.
30026 * @param {Roo.LayoutRegion} this
30027 * @param {Roo.ContentPanel} panel The panel
30029 "paneladded" : true,
30031 * @event panelremoved
30032 * Fires when a panel is removed.
30033 * @param {Roo.LayoutRegion} this
30034 * @param {Roo.ContentPanel} panel The panel
30036 "panelremoved" : true,
30038 * @event beforecollapse
30039 * Fires when this region before collapse.
30040 * @param {Roo.LayoutRegion} this
30042 "beforecollapse" : true,
30045 * Fires when this region is collapsed.
30046 * @param {Roo.LayoutRegion} this
30048 "collapsed" : true,
30051 * Fires when this region is expanded.
30052 * @param {Roo.LayoutRegion} this
30057 * Fires when this region is slid into view.
30058 * @param {Roo.LayoutRegion} this
30060 "slideshow" : true,
30063 * Fires when this region slides out of view.
30064 * @param {Roo.LayoutRegion} this
30066 "slidehide" : true,
30068 * @event panelactivated
30069 * Fires when a panel is activated.
30070 * @param {Roo.LayoutRegion} this
30071 * @param {Roo.ContentPanel} panel The activated panel
30073 "panelactivated" : true,
30076 * Fires when the user resizes this region.
30077 * @param {Roo.LayoutRegion} this
30078 * @param {Number} newSize The new size (width for east/west, height for north/south)
30082 /** A collection of panels in this region. @type Roo.util.MixedCollection */
30083 this.panels = new Roo.util.MixedCollection();
30084 this.panels.getKey = this.getPanelId.createDelegate(this);
30086 this.activePanel = null;
30087 // ensure listeners are added...
30089 if (config.listeners || config.events) {
30090 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
30091 listeners : config.listeners || {},
30092 events : config.events || {}
30096 if(skipConfig !== true){
30097 this.applyConfig(config);
30101 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
30102 getPanelId : function(p){
30106 applyConfig : function(config){
30107 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30108 this.config = config;
30113 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
30114 * the width, for horizontal (north, south) the height.
30115 * @param {Number} newSize The new width or height
30117 resizeTo : function(newSize){
30118 var el = this.el ? this.el :
30119 (this.activePanel ? this.activePanel.getEl() : null);
30121 switch(this.position){
30124 el.setWidth(newSize);
30125 this.fireEvent("resized", this, newSize);
30129 el.setHeight(newSize);
30130 this.fireEvent("resized", this, newSize);
30136 getBox : function(){
30137 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
30140 getMargins : function(){
30141 return this.margins;
30144 updateBox : function(box){
30146 var el = this.activePanel.getEl();
30147 el.dom.style.left = box.x + "px";
30148 el.dom.style.top = box.y + "px";
30149 this.activePanel.setSize(box.width, box.height);
30153 * Returns the container element for this region.
30154 * @return {Roo.Element}
30156 getEl : function(){
30157 return this.activePanel;
30161 * Returns true if this region is currently visible.
30162 * @return {Boolean}
30164 isVisible : function(){
30165 return this.activePanel ? true : false;
30168 setActivePanel : function(panel){
30169 panel = this.getPanel(panel);
30170 if(this.activePanel && this.activePanel != panel){
30171 this.activePanel.setActiveState(false);
30172 this.activePanel.getEl().setLeftTop(-10000,-10000);
30174 this.activePanel = panel;
30175 panel.setActiveState(true);
30177 panel.setSize(this.box.width, this.box.height);
30179 this.fireEvent("panelactivated", this, panel);
30180 this.fireEvent("invalidated");
30184 * Show the specified panel.
30185 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
30186 * @return {Roo.ContentPanel} The shown panel or null
30188 showPanel : function(panel){
30189 if(panel = this.getPanel(panel)){
30190 this.setActivePanel(panel);
30196 * Get the active panel for this region.
30197 * @return {Roo.ContentPanel} The active panel or null
30199 getActivePanel : function(){
30200 return this.activePanel;
30204 * Add the passed ContentPanel(s)
30205 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30206 * @return {Roo.ContentPanel} The panel added (if only one was added)
30208 add : function(panel){
30209 if(arguments.length > 1){
30210 for(var i = 0, len = arguments.length; i < len; i++) {
30211 this.add(arguments[i]);
30215 if(this.hasPanel(panel)){
30216 this.showPanel(panel);
30219 var el = panel.getEl();
30220 if(el.dom.parentNode != this.mgr.el.dom){
30221 this.mgr.el.dom.appendChild(el.dom);
30223 if(panel.setRegion){
30224 panel.setRegion(this);
30226 this.panels.add(panel);
30227 el.setStyle("position", "absolute");
30228 if(!panel.background){
30229 this.setActivePanel(panel);
30230 if(this.config.initialSize && this.panels.getCount()==1){
30231 this.resizeTo(this.config.initialSize);
30234 this.fireEvent("paneladded", this, panel);
30239 * Returns true if the panel is in this region.
30240 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30241 * @return {Boolean}
30243 hasPanel : function(panel){
30244 if(typeof panel == "object"){ // must be panel obj
30245 panel = panel.getId();
30247 return this.getPanel(panel) ? true : false;
30251 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30252 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30253 * @param {Boolean} preservePanel Overrides the config preservePanel option
30254 * @return {Roo.ContentPanel} The panel that was removed
30256 remove : function(panel, preservePanel){
30257 panel = this.getPanel(panel);
30262 this.fireEvent("beforeremove", this, panel, e);
30263 if(e.cancel === true){
30266 var panelId = panel.getId();
30267 this.panels.removeKey(panelId);
30272 * Returns the panel specified or null if it's not in this region.
30273 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30274 * @return {Roo.ContentPanel}
30276 getPanel : function(id){
30277 if(typeof id == "object"){ // must be panel obj
30280 return this.panels.get(id);
30284 * Returns this regions position (north/south/east/west/center).
30287 getPosition: function(){
30288 return this.position;
30292 * Ext JS Library 1.1.1
30293 * Copyright(c) 2006-2007, Ext JS, LLC.
30295 * Originally Released Under LGPL - original licence link has changed is not relivant.
30298 * <script type="text/javascript">
30302 * @class Roo.LayoutRegion
30303 * @extends Roo.BasicLayoutRegion
30304 * This class represents a region in a layout manager.
30305 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
30306 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
30307 * @cfg {Boolean} floatable False to disable floating (defaults to true)
30308 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30309 * @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})
30310 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
30311 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
30312 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
30313 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
30314 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
30315 * @cfg {String} title The title for the region (overrides panel titles)
30316 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
30317 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30318 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
30319 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30320 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
30321 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30322 * the space available, similar to FireFox 1.5 tabs (defaults to false)
30323 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
30324 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
30325 * @cfg {Boolean} showPin True to show a pin button
30326 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
30327 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
30328 * @cfg {Boolean} disableTabTips True to disable tab tooltips
30329 * @cfg {Number} width For East/West panels
30330 * @cfg {Number} height For North/South panels
30331 * @cfg {Boolean} split To show the splitter
30332 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
30334 Roo.LayoutRegion = function(mgr, config, pos){
30335 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30336 var dh = Roo.DomHelper;
30337 /** This region's container element
30338 * @type Roo.Element */
30339 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30340 /** This region's title element
30341 * @type Roo.Element */
30343 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30344 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
30345 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30347 this.titleEl.enableDisplayMode();
30348 /** This region's title text element
30349 * @type HTMLElement */
30350 this.titleTextEl = this.titleEl.dom.firstChild;
30351 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30352 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30353 this.closeBtn.enableDisplayMode();
30354 this.closeBtn.on("click", this.closeClicked, this);
30355 this.closeBtn.hide();
30357 this.createBody(config);
30358 this.visible = true;
30359 this.collapsed = false;
30361 if(config.hideWhenEmpty){
30363 this.on("paneladded", this.validateVisibility, this);
30364 this.on("panelremoved", this.validateVisibility, this);
30366 this.applyConfig(config);
30369 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30371 createBody : function(){
30372 /** This region's body element
30373 * @type Roo.Element */
30374 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30377 applyConfig : function(c){
30378 if(c.collapsible && this.position != "center" && !this.collapsedEl){
30379 var dh = Roo.DomHelper;
30380 if(c.titlebar !== false){
30381 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30382 this.collapseBtn.on("click", this.collapse, this);
30383 this.collapseBtn.enableDisplayMode();
30385 if(c.showPin === true || this.showPin){
30386 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30387 this.stickBtn.enableDisplayMode();
30388 this.stickBtn.on("click", this.expand, this);
30389 this.stickBtn.hide();
30392 /** This region's collapsed element
30393 * @type Roo.Element */
30394 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30395 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30397 if(c.floatable !== false){
30398 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30399 this.collapsedEl.on("click", this.collapseClick, this);
30402 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30403 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30404 id: "message", unselectable: "on", style:{"float":"left"}});
30405 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30407 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30408 this.expandBtn.on("click", this.expand, this);
30410 if(this.collapseBtn){
30411 this.collapseBtn.setVisible(c.collapsible == true);
30413 this.cmargins = c.cmargins || this.cmargins ||
30414 (this.position == "west" || this.position == "east" ?
30415 {top: 0, left: 2, right:2, bottom: 0} :
30416 {top: 2, left: 0, right:0, bottom: 2});
30417 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30418 this.bottomTabs = c.tabPosition != "top";
30419 this.autoScroll = c.autoScroll || false;
30420 if(this.autoScroll){
30421 this.bodyEl.setStyle("overflow", "auto");
30423 this.bodyEl.setStyle("overflow", "hidden");
30425 //if(c.titlebar !== false){
30426 if((!c.titlebar && !c.title) || c.titlebar === false){
30427 this.titleEl.hide();
30429 this.titleEl.show();
30431 this.titleTextEl.innerHTML = c.title;
30435 this.duration = c.duration || .30;
30436 this.slideDuration = c.slideDuration || .45;
30439 this.collapse(true);
30446 * Returns true if this region is currently visible.
30447 * @return {Boolean}
30449 isVisible : function(){
30450 return this.visible;
30454 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30455 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
30457 setCollapsedTitle : function(title){
30458 title = title || " ";
30459 if(this.collapsedTitleTextEl){
30460 this.collapsedTitleTextEl.innerHTML = title;
30464 getBox : function(){
30466 if(!this.collapsed){
30467 b = this.el.getBox(false, true);
30469 b = this.collapsedEl.getBox(false, true);
30474 getMargins : function(){
30475 return this.collapsed ? this.cmargins : this.margins;
30478 highlight : function(){
30479 this.el.addClass("x-layout-panel-dragover");
30482 unhighlight : function(){
30483 this.el.removeClass("x-layout-panel-dragover");
30486 updateBox : function(box){
30488 if(!this.collapsed){
30489 this.el.dom.style.left = box.x + "px";
30490 this.el.dom.style.top = box.y + "px";
30491 this.updateBody(box.width, box.height);
30493 this.collapsedEl.dom.style.left = box.x + "px";
30494 this.collapsedEl.dom.style.top = box.y + "px";
30495 this.collapsedEl.setSize(box.width, box.height);
30498 this.tabs.autoSizeTabs();
30502 updateBody : function(w, h){
30504 this.el.setWidth(w);
30505 w -= this.el.getBorderWidth("rl");
30506 if(this.config.adjustments){
30507 w += this.config.adjustments[0];
30511 this.el.setHeight(h);
30512 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30513 h -= this.el.getBorderWidth("tb");
30514 if(this.config.adjustments){
30515 h += this.config.adjustments[1];
30517 this.bodyEl.setHeight(h);
30519 h = this.tabs.syncHeight(h);
30522 if(this.panelSize){
30523 w = w !== null ? w : this.panelSize.width;
30524 h = h !== null ? h : this.panelSize.height;
30526 if(this.activePanel){
30527 var el = this.activePanel.getEl();
30528 w = w !== null ? w : el.getWidth();
30529 h = h !== null ? h : el.getHeight();
30530 this.panelSize = {width: w, height: h};
30531 this.activePanel.setSize(w, h);
30533 if(Roo.isIE && this.tabs){
30534 this.tabs.el.repaint();
30539 * Returns the container element for this region.
30540 * @return {Roo.Element}
30542 getEl : function(){
30547 * Hides this region.
30550 if(!this.collapsed){
30551 this.el.dom.style.left = "-2000px";
30554 this.collapsedEl.dom.style.left = "-2000px";
30555 this.collapsedEl.hide();
30557 this.visible = false;
30558 this.fireEvent("visibilitychange", this, false);
30562 * Shows this region if it was previously hidden.
30565 if(!this.collapsed){
30568 this.collapsedEl.show();
30570 this.visible = true;
30571 this.fireEvent("visibilitychange", this, true);
30574 closeClicked : function(){
30575 if(this.activePanel){
30576 this.remove(this.activePanel);
30580 collapseClick : function(e){
30582 e.stopPropagation();
30585 e.stopPropagation();
30591 * Collapses this region.
30592 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30594 collapse : function(skipAnim, skipCheck){
30595 if(this.collapsed) {
30599 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30601 this.collapsed = true;
30603 this.split.el.hide();
30605 if(this.config.animate && skipAnim !== true){
30606 this.fireEvent("invalidated", this);
30607 this.animateCollapse();
30609 this.el.setLocation(-20000,-20000);
30611 this.collapsedEl.show();
30612 this.fireEvent("collapsed", this);
30613 this.fireEvent("invalidated", this);
30619 animateCollapse : function(){
30624 * Expands this region if it was previously collapsed.
30625 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30626 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30628 expand : function(e, skipAnim){
30630 e.stopPropagation();
30632 if(!this.collapsed || this.el.hasActiveFx()) {
30636 this.afterSlideIn();
30639 this.collapsed = false;
30640 if(this.config.animate && skipAnim !== true){
30641 this.animateExpand();
30645 this.split.el.show();
30647 this.collapsedEl.setLocation(-2000,-2000);
30648 this.collapsedEl.hide();
30649 this.fireEvent("invalidated", this);
30650 this.fireEvent("expanded", this);
30654 animateExpand : function(){
30658 initTabs : function()
30660 this.bodyEl.setStyle("overflow", "hidden");
30661 var ts = new Roo.TabPanel(
30664 tabPosition: this.bottomTabs ? 'bottom' : 'top',
30665 disableTooltips: this.config.disableTabTips,
30666 toolbar : this.config.toolbar
30669 if(this.config.hideTabs){
30670 ts.stripWrap.setDisplayed(false);
30673 ts.resizeTabs = this.config.resizeTabs === true;
30674 ts.minTabWidth = this.config.minTabWidth || 40;
30675 ts.maxTabWidth = this.config.maxTabWidth || 250;
30676 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30677 ts.monitorResize = false;
30678 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30679 ts.bodyEl.addClass('x-layout-tabs-body');
30680 this.panels.each(this.initPanelAsTab, this);
30683 initPanelAsTab : function(panel){
30684 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30685 this.config.closeOnTab && panel.isClosable());
30686 if(panel.tabTip !== undefined){
30687 ti.setTooltip(panel.tabTip);
30689 ti.on("activate", function(){
30690 this.setActivePanel(panel);
30692 if(this.config.closeOnTab){
30693 ti.on("beforeclose", function(t, e){
30695 this.remove(panel);
30701 updatePanelTitle : function(panel, title){
30702 if(this.activePanel == panel){
30703 this.updateTitle(title);
30706 var ti = this.tabs.getTab(panel.getEl().id);
30708 if(panel.tabTip !== undefined){
30709 ti.setTooltip(panel.tabTip);
30714 updateTitle : function(title){
30715 if(this.titleTextEl && !this.config.title){
30716 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
30720 setActivePanel : function(panel){
30721 panel = this.getPanel(panel);
30722 if(this.activePanel && this.activePanel != panel){
30723 this.activePanel.setActiveState(false);
30725 this.activePanel = panel;
30726 panel.setActiveState(true);
30727 if(this.panelSize){
30728 panel.setSize(this.panelSize.width, this.panelSize.height);
30731 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30733 this.updateTitle(panel.getTitle());
30735 this.fireEvent("invalidated", this);
30737 this.fireEvent("panelactivated", this, panel);
30741 * Shows the specified panel.
30742 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30743 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30745 showPanel : function(panel)
30747 panel = this.getPanel(panel);
30750 var tab = this.tabs.getTab(panel.getEl().id);
30751 if(tab.isHidden()){
30752 this.tabs.unhideTab(tab.id);
30756 this.setActivePanel(panel);
30763 * Get the active panel for this region.
30764 * @return {Roo.ContentPanel} The active panel or null
30766 getActivePanel : function(){
30767 return this.activePanel;
30770 validateVisibility : function(){
30771 if(this.panels.getCount() < 1){
30772 this.updateTitle(" ");
30773 this.closeBtn.hide();
30776 if(!this.isVisible()){
30783 * Adds the passed ContentPanel(s) to this region.
30784 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30785 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30787 add : function(panel){
30788 if(arguments.length > 1){
30789 for(var i = 0, len = arguments.length; i < len; i++) {
30790 this.add(arguments[i]);
30794 if(this.hasPanel(panel)){
30795 this.showPanel(panel);
30798 panel.setRegion(this);
30799 this.panels.add(panel);
30800 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30801 this.bodyEl.dom.appendChild(panel.getEl().dom);
30802 if(panel.background !== true){
30803 this.setActivePanel(panel);
30805 this.fireEvent("paneladded", this, panel);
30811 this.initPanelAsTab(panel);
30813 if(panel.background !== true){
30814 this.tabs.activate(panel.getEl().id);
30816 this.fireEvent("paneladded", this, panel);
30821 * Hides the tab for the specified panel.
30822 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30824 hidePanel : function(panel){
30825 if(this.tabs && (panel = this.getPanel(panel))){
30826 this.tabs.hideTab(panel.getEl().id);
30831 * Unhides the tab for a previously hidden panel.
30832 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30834 unhidePanel : function(panel){
30835 if(this.tabs && (panel = this.getPanel(panel))){
30836 this.tabs.unhideTab(panel.getEl().id);
30840 clearPanels : function(){
30841 while(this.panels.getCount() > 0){
30842 this.remove(this.panels.first());
30847 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30848 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30849 * @param {Boolean} preservePanel Overrides the config preservePanel option
30850 * @return {Roo.ContentPanel} The panel that was removed
30852 remove : function(panel, preservePanel){
30853 panel = this.getPanel(panel);
30858 this.fireEvent("beforeremove", this, panel, e);
30859 if(e.cancel === true){
30862 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30863 var panelId = panel.getId();
30864 this.panels.removeKey(panelId);
30866 document.body.appendChild(panel.getEl().dom);
30869 this.tabs.removeTab(panel.getEl().id);
30870 }else if (!preservePanel){
30871 this.bodyEl.dom.removeChild(panel.getEl().dom);
30873 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30874 var p = this.panels.first();
30875 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30876 tempEl.appendChild(p.getEl().dom);
30877 this.bodyEl.update("");
30878 this.bodyEl.dom.appendChild(p.getEl().dom);
30880 this.updateTitle(p.getTitle());
30882 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30883 this.setActivePanel(p);
30885 panel.setRegion(null);
30886 if(this.activePanel == panel){
30887 this.activePanel = null;
30889 if(this.config.autoDestroy !== false && preservePanel !== true){
30890 try{panel.destroy();}catch(e){}
30892 this.fireEvent("panelremoved", this, panel);
30897 * Returns the TabPanel component used by this region
30898 * @return {Roo.TabPanel}
30900 getTabs : function(){
30904 createTool : function(parentEl, className){
30905 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30906 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
30907 btn.addClassOnOver("x-layout-tools-button-over");
30912 * Ext JS Library 1.1.1
30913 * Copyright(c) 2006-2007, Ext JS, LLC.
30915 * Originally Released Under LGPL - original licence link has changed is not relivant.
30918 * <script type="text/javascript">
30924 * @class Roo.SplitLayoutRegion
30925 * @extends Roo.LayoutRegion
30926 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30928 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30929 this.cursor = cursor;
30930 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30933 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30934 splitTip : "Drag to resize.",
30935 collapsibleSplitTip : "Drag to resize. Double click to hide.",
30936 useSplitTips : false,
30938 applyConfig : function(config){
30939 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30942 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
30943 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
30944 /** The SplitBar for this region
30945 * @type Roo.SplitBar */
30946 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30947 this.split.on("moved", this.onSplitMove, this);
30948 this.split.useShim = config.useShim === true;
30949 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30950 if(this.useSplitTips){
30951 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30953 if(config.collapsible){
30954 this.split.el.on("dblclick", this.collapse, this);
30957 if(typeof config.minSize != "undefined"){
30958 this.split.minSize = config.minSize;
30960 if(typeof config.maxSize != "undefined"){
30961 this.split.maxSize = config.maxSize;
30963 if(config.hideWhenEmpty || config.hidden || config.collapsed){
30964 this.hideSplitter();
30969 getHMaxSize : function(){
30970 var cmax = this.config.maxSize || 10000;
30971 var center = this.mgr.getRegion("center");
30972 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30975 getVMaxSize : function(){
30976 var cmax = this.config.maxSize || 10000;
30977 var center = this.mgr.getRegion("center");
30978 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30981 onSplitMove : function(split, newSize){
30982 this.fireEvent("resized", this, newSize);
30986 * Returns the {@link Roo.SplitBar} for this region.
30987 * @return {Roo.SplitBar}
30989 getSplitBar : function(){
30994 this.hideSplitter();
30995 Roo.SplitLayoutRegion.superclass.hide.call(this);
30998 hideSplitter : function(){
31000 this.split.el.setLocation(-2000,-2000);
31001 this.split.el.hide();
31007 this.split.el.show();
31009 Roo.SplitLayoutRegion.superclass.show.call(this);
31012 beforeSlide: function(){
31013 if(Roo.isGecko){// firefox overflow auto bug workaround
31014 this.bodyEl.clip();
31016 this.tabs.bodyEl.clip();
31018 if(this.activePanel){
31019 this.activePanel.getEl().clip();
31021 if(this.activePanel.beforeSlide){
31022 this.activePanel.beforeSlide();
31028 afterSlide : function(){
31029 if(Roo.isGecko){// firefox overflow auto bug workaround
31030 this.bodyEl.unclip();
31032 this.tabs.bodyEl.unclip();
31034 if(this.activePanel){
31035 this.activePanel.getEl().unclip();
31036 if(this.activePanel.afterSlide){
31037 this.activePanel.afterSlide();
31043 initAutoHide : function(){
31044 if(this.autoHide !== false){
31045 if(!this.autoHideHd){
31046 var st = new Roo.util.DelayedTask(this.slideIn, this);
31047 this.autoHideHd = {
31048 "mouseout": function(e){
31049 if(!e.within(this.el, true)){
31053 "mouseover" : function(e){
31059 this.el.on(this.autoHideHd);
31063 clearAutoHide : function(){
31064 if(this.autoHide !== false){
31065 this.el.un("mouseout", this.autoHideHd.mouseout);
31066 this.el.un("mouseover", this.autoHideHd.mouseover);
31070 clearMonitor : function(){
31071 Roo.get(document).un("click", this.slideInIf, this);
31074 // these names are backwards but not changed for compat
31075 slideOut : function(){
31076 if(this.isSlid || this.el.hasActiveFx()){
31079 this.isSlid = true;
31080 if(this.collapseBtn){
31081 this.collapseBtn.hide();
31083 this.closeBtnState = this.closeBtn.getStyle('display');
31084 this.closeBtn.hide();
31086 this.stickBtn.show();
31089 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
31090 this.beforeSlide();
31091 this.el.setStyle("z-index", 10001);
31092 this.el.slideIn(this.getSlideAnchor(), {
31093 callback: function(){
31095 this.initAutoHide();
31096 Roo.get(document).on("click", this.slideInIf, this);
31097 this.fireEvent("slideshow", this);
31104 afterSlideIn : function(){
31105 this.clearAutoHide();
31106 this.isSlid = false;
31107 this.clearMonitor();
31108 this.el.setStyle("z-index", "");
31109 if(this.collapseBtn){
31110 this.collapseBtn.show();
31112 this.closeBtn.setStyle('display', this.closeBtnState);
31114 this.stickBtn.hide();
31116 this.fireEvent("slidehide", this);
31119 slideIn : function(cb){
31120 if(!this.isSlid || this.el.hasActiveFx()){
31124 this.isSlid = false;
31125 this.beforeSlide();
31126 this.el.slideOut(this.getSlideAnchor(), {
31127 callback: function(){
31128 this.el.setLeftTop(-10000, -10000);
31130 this.afterSlideIn();
31138 slideInIf : function(e){
31139 if(!e.within(this.el)){
31144 animateCollapse : function(){
31145 this.beforeSlide();
31146 this.el.setStyle("z-index", 20000);
31147 var anchor = this.getSlideAnchor();
31148 this.el.slideOut(anchor, {
31149 callback : function(){
31150 this.el.setStyle("z-index", "");
31151 this.collapsedEl.slideIn(anchor, {duration:.3});
31153 this.el.setLocation(-10000,-10000);
31155 this.fireEvent("collapsed", this);
31162 animateExpand : function(){
31163 this.beforeSlide();
31164 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
31165 this.el.setStyle("z-index", 20000);
31166 this.collapsedEl.hide({
31169 this.el.slideIn(this.getSlideAnchor(), {
31170 callback : function(){
31171 this.el.setStyle("z-index", "");
31174 this.split.el.show();
31176 this.fireEvent("invalidated", this);
31177 this.fireEvent("expanded", this);
31205 getAnchor : function(){
31206 return this.anchors[this.position];
31209 getCollapseAnchor : function(){
31210 return this.canchors[this.position];
31213 getSlideAnchor : function(){
31214 return this.sanchors[this.position];
31217 getAlignAdj : function(){
31218 var cm = this.cmargins;
31219 switch(this.position){
31235 getExpandAdj : function(){
31236 var c = this.collapsedEl, cm = this.cmargins;
31237 switch(this.position){
31239 return [-(cm.right+c.getWidth()+cm.left), 0];
31242 return [cm.right+c.getWidth()+cm.left, 0];
31245 return [0, -(cm.top+cm.bottom+c.getHeight())];
31248 return [0, cm.top+cm.bottom+c.getHeight()];
31254 * Ext JS Library 1.1.1
31255 * Copyright(c) 2006-2007, Ext JS, LLC.
31257 * Originally Released Under LGPL - original licence link has changed is not relivant.
31260 * <script type="text/javascript">
31263 * These classes are private internal classes
31265 Roo.CenterLayoutRegion = function(mgr, config){
31266 Roo.LayoutRegion.call(this, mgr, config, "center");
31267 this.visible = true;
31268 this.minWidth = config.minWidth || 20;
31269 this.minHeight = config.minHeight || 20;
31272 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31274 // center panel can't be hidden
31278 // center panel can't be hidden
31281 getMinWidth: function(){
31282 return this.minWidth;
31285 getMinHeight: function(){
31286 return this.minHeight;
31291 Roo.NorthLayoutRegion = function(mgr, config){
31292 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31294 this.split.placement = Roo.SplitBar.TOP;
31295 this.split.orientation = Roo.SplitBar.VERTICAL;
31296 this.split.el.addClass("x-layout-split-v");
31298 var size = config.initialSize || config.height;
31299 if(typeof size != "undefined"){
31300 this.el.setHeight(size);
31303 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31304 orientation: Roo.SplitBar.VERTICAL,
31305 getBox : function(){
31306 if(this.collapsed){
31307 return this.collapsedEl.getBox();
31309 var box = this.el.getBox();
31311 box.height += this.split.el.getHeight();
31316 updateBox : function(box){
31317 if(this.split && !this.collapsed){
31318 box.height -= this.split.el.getHeight();
31319 this.split.el.setLeft(box.x);
31320 this.split.el.setTop(box.y+box.height);
31321 this.split.el.setWidth(box.width);
31323 if(this.collapsed){
31324 this.updateBody(box.width, null);
31326 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31330 Roo.SouthLayoutRegion = function(mgr, config){
31331 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31333 this.split.placement = Roo.SplitBar.BOTTOM;
31334 this.split.orientation = Roo.SplitBar.VERTICAL;
31335 this.split.el.addClass("x-layout-split-v");
31337 var size = config.initialSize || config.height;
31338 if(typeof size != "undefined"){
31339 this.el.setHeight(size);
31342 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31343 orientation: Roo.SplitBar.VERTICAL,
31344 getBox : function(){
31345 if(this.collapsed){
31346 return this.collapsedEl.getBox();
31348 var box = this.el.getBox();
31350 var sh = this.split.el.getHeight();
31357 updateBox : function(box){
31358 if(this.split && !this.collapsed){
31359 var sh = this.split.el.getHeight();
31362 this.split.el.setLeft(box.x);
31363 this.split.el.setTop(box.y-sh);
31364 this.split.el.setWidth(box.width);
31366 if(this.collapsed){
31367 this.updateBody(box.width, null);
31369 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31373 Roo.EastLayoutRegion = function(mgr, config){
31374 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31376 this.split.placement = Roo.SplitBar.RIGHT;
31377 this.split.orientation = Roo.SplitBar.HORIZONTAL;
31378 this.split.el.addClass("x-layout-split-h");
31380 var size = config.initialSize || config.width;
31381 if(typeof size != "undefined"){
31382 this.el.setWidth(size);
31385 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31386 orientation: Roo.SplitBar.HORIZONTAL,
31387 getBox : function(){
31388 if(this.collapsed){
31389 return this.collapsedEl.getBox();
31391 var box = this.el.getBox();
31393 var sw = this.split.el.getWidth();
31400 updateBox : function(box){
31401 if(this.split && !this.collapsed){
31402 var sw = this.split.el.getWidth();
31404 this.split.el.setLeft(box.x);
31405 this.split.el.setTop(box.y);
31406 this.split.el.setHeight(box.height);
31409 if(this.collapsed){
31410 this.updateBody(null, box.height);
31412 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31416 Roo.WestLayoutRegion = function(mgr, config){
31417 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31419 this.split.placement = Roo.SplitBar.LEFT;
31420 this.split.orientation = Roo.SplitBar.HORIZONTAL;
31421 this.split.el.addClass("x-layout-split-h");
31423 var size = config.initialSize || config.width;
31424 if(typeof size != "undefined"){
31425 this.el.setWidth(size);
31428 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31429 orientation: Roo.SplitBar.HORIZONTAL,
31430 getBox : function(){
31431 if(this.collapsed){
31432 return this.collapsedEl.getBox();
31434 var box = this.el.getBox();
31436 box.width += this.split.el.getWidth();
31441 updateBox : function(box){
31442 if(this.split && !this.collapsed){
31443 var sw = this.split.el.getWidth();
31445 this.split.el.setLeft(box.x+box.width);
31446 this.split.el.setTop(box.y);
31447 this.split.el.setHeight(box.height);
31449 if(this.collapsed){
31450 this.updateBody(null, box.height);
31452 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31457 * Ext JS Library 1.1.1
31458 * Copyright(c) 2006-2007, Ext JS, LLC.
31460 * Originally Released Under LGPL - original licence link has changed is not relivant.
31463 * <script type="text/javascript">
31468 * Private internal class for reading and applying state
31470 Roo.LayoutStateManager = function(layout){
31471 // default empty state
31480 Roo.LayoutStateManager.prototype = {
31481 init : function(layout, provider){
31482 this.provider = provider;
31483 var state = provider.get(layout.id+"-layout-state");
31485 var wasUpdating = layout.isUpdating();
31487 layout.beginUpdate();
31489 for(var key in state){
31490 if(typeof state[key] != "function"){
31491 var rstate = state[key];
31492 var r = layout.getRegion(key);
31495 r.resizeTo(rstate.size);
31497 if(rstate.collapsed == true){
31500 r.expand(null, true);
31506 layout.endUpdate();
31508 this.state = state;
31510 this.layout = layout;
31511 layout.on("regionresized", this.onRegionResized, this);
31512 layout.on("regioncollapsed", this.onRegionCollapsed, this);
31513 layout.on("regionexpanded", this.onRegionExpanded, this);
31516 storeState : function(){
31517 this.provider.set(this.layout.id+"-layout-state", this.state);
31520 onRegionResized : function(region, newSize){
31521 this.state[region.getPosition()].size = newSize;
31525 onRegionCollapsed : function(region){
31526 this.state[region.getPosition()].collapsed = true;
31530 onRegionExpanded : function(region){
31531 this.state[region.getPosition()].collapsed = false;
31536 * Ext JS Library 1.1.1
31537 * Copyright(c) 2006-2007, Ext JS, LLC.
31539 * Originally Released Under LGPL - original licence link has changed is not relivant.
31542 * <script type="text/javascript">
31545 * @class Roo.ContentPanel
31546 * @extends Roo.util.Observable
31547 * A basic ContentPanel element.
31548 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
31549 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
31550 * @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
31551 * @cfg {Boolean} closable True if the panel can be closed/removed
31552 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
31553 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31554 * @cfg {Toolbar} toolbar A toolbar for this panel
31555 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
31556 * @cfg {String} title The title for this panel
31557 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31558 * @cfg {String} url Calls {@link #setUrl} with this value
31559 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31560 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
31561 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
31562 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
31565 * Create a new ContentPanel.
31566 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31567 * @param {String/Object} config A string to set only the title or a config object
31568 * @param {String} content (optional) Set the HTML content for this panel
31569 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31571 Roo.ContentPanel = function(el, config, content){
31575 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31579 if (config && config.parentLayout) {
31580 el = config.parentLayout.el.createChild();
31583 if(el.autoCreate){ // xtype is available if this is called from factory
31587 this.el = Roo.get(el);
31588 if(!this.el && config && config.autoCreate){
31589 if(typeof config.autoCreate == "object"){
31590 if(!config.autoCreate.id){
31591 config.autoCreate.id = config.id||el;
31593 this.el = Roo.DomHelper.append(document.body,
31594 config.autoCreate, true);
31596 this.el = Roo.DomHelper.append(document.body,
31597 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31600 this.closable = false;
31601 this.loaded = false;
31602 this.active = false;
31603 if(typeof config == "string"){
31604 this.title = config;
31606 Roo.apply(this, config);
31609 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31610 this.wrapEl = this.el.wrap();
31611 this.toolbar.container = this.el.insertSibling(false, 'before');
31612 this.toolbar = new Roo.Toolbar(this.toolbar);
31615 // xtype created footer. - not sure if will work as we normally have to render first..
31616 if (this.footer && !this.footer.el && this.footer.xtype) {
31617 if (!this.wrapEl) {
31618 this.wrapEl = this.el.wrap();
31621 this.footer.container = this.wrapEl.createChild();
31623 this.footer = Roo.factory(this.footer, Roo);
31628 this.resizeEl = Roo.get(this.resizeEl, true);
31630 this.resizeEl = this.el;
31632 // handle view.xtype
31640 * Fires when this panel is activated.
31641 * @param {Roo.ContentPanel} this
31645 * @event deactivate
31646 * Fires when this panel is activated.
31647 * @param {Roo.ContentPanel} this
31649 "deactivate" : true,
31653 * Fires when this panel is resized if fitToFrame is true.
31654 * @param {Roo.ContentPanel} this
31655 * @param {Number} width The width after any component adjustments
31656 * @param {Number} height The height after any component adjustments
31662 * Fires when this tab is created
31663 * @param {Roo.ContentPanel} this
31673 if(this.autoScroll){
31674 this.resizeEl.setStyle("overflow", "auto");
31676 // fix randome scrolling
31677 this.el.on('scroll', function() {
31678 Roo.log('fix random scolling');
31679 this.scrollTo('top',0);
31682 content = content || this.content;
31684 this.setContent(content);
31686 if(config && config.url){
31687 this.setUrl(this.url, this.params, this.loadOnce);
31692 Roo.ContentPanel.superclass.constructor.call(this);
31694 if (this.view && typeof(this.view.xtype) != 'undefined') {
31695 this.view.el = this.el.appendChild(document.createElement("div"));
31696 this.view = Roo.factory(this.view);
31697 this.view.render && this.view.render(false, '');
31701 this.fireEvent('render', this);
31704 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31706 setRegion : function(region){
31707 this.region = region;
31709 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31711 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31716 * Returns the toolbar for this Panel if one was configured.
31717 * @return {Roo.Toolbar}
31719 getToolbar : function(){
31720 return this.toolbar;
31723 setActiveState : function(active){
31724 this.active = active;
31726 this.fireEvent("deactivate", this);
31728 this.fireEvent("activate", this);
31732 * Updates this panel's element
31733 * @param {String} content The new content
31734 * @param {Boolean} loadScripts (optional) true to look for and process scripts
31736 setContent : function(content, loadScripts){
31737 this.el.update(content, loadScripts);
31740 ignoreResize : function(w, h){
31741 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31744 this.lastSize = {width: w, height: h};
31749 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31750 * @return {Roo.UpdateManager} The UpdateManager
31752 getUpdateManager : function(){
31753 return this.el.getUpdateManager();
31756 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31757 * @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:
31760 url: "your-url.php",
31761 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31762 callback: yourFunction,
31763 scope: yourObject, //(optional scope)
31766 text: "Loading...",
31771 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31772 * 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.
31773 * @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}
31774 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31775 * @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.
31776 * @return {Roo.ContentPanel} this
31779 var um = this.el.getUpdateManager();
31780 um.update.apply(um, arguments);
31786 * 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.
31787 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31788 * @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)
31789 * @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)
31790 * @return {Roo.UpdateManager} The UpdateManager
31792 setUrl : function(url, params, loadOnce){
31793 if(this.refreshDelegate){
31794 this.removeListener("activate", this.refreshDelegate);
31796 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31797 this.on("activate", this.refreshDelegate);
31798 return this.el.getUpdateManager();
31801 _handleRefresh : function(url, params, loadOnce){
31802 if(!loadOnce || !this.loaded){
31803 var updater = this.el.getUpdateManager();
31804 updater.update(url, params, this._setLoaded.createDelegate(this));
31808 _setLoaded : function(){
31809 this.loaded = true;
31813 * Returns this panel's id
31816 getId : function(){
31821 * Returns this panel's element - used by regiosn to add.
31822 * @return {Roo.Element}
31824 getEl : function(){
31825 return this.wrapEl || this.el;
31828 adjustForComponents : function(width, height)
31830 //Roo.log('adjustForComponents ');
31831 if(this.resizeEl != this.el){
31832 width -= this.el.getFrameWidth('lr');
31833 height -= this.el.getFrameWidth('tb');
31836 var te = this.toolbar.getEl();
31837 height -= te.getHeight();
31838 te.setWidth(width);
31841 var te = this.footer.getEl();
31842 //Roo.log("footer:" + te.getHeight());
31844 height -= te.getHeight();
31845 te.setWidth(width);
31849 if(this.adjustments){
31850 width += this.adjustments[0];
31851 height += this.adjustments[1];
31853 return {"width": width, "height": height};
31856 setSize : function(width, height){
31857 if(this.fitToFrame && !this.ignoreResize(width, height)){
31858 if(this.fitContainer && this.resizeEl != this.el){
31859 this.el.setSize(width, height);
31861 var size = this.adjustForComponents(width, height);
31862 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31863 this.fireEvent('resize', this, size.width, size.height);
31868 * Returns this panel's title
31871 getTitle : function(){
31876 * Set this panel's title
31877 * @param {String} title
31879 setTitle : function(title){
31880 this.title = title;
31882 this.region.updatePanelTitle(this, title);
31887 * Returns true is this panel was configured to be closable
31888 * @return {Boolean}
31890 isClosable : function(){
31891 return this.closable;
31894 beforeSlide : function(){
31896 this.resizeEl.clip();
31899 afterSlide : function(){
31901 this.resizeEl.unclip();
31905 * Force a content refresh from the URL specified in the {@link #setUrl} method.
31906 * Will fail silently if the {@link #setUrl} method has not been called.
31907 * This does not activate the panel, just updates its content.
31909 refresh : function(){
31910 if(this.refreshDelegate){
31911 this.loaded = false;
31912 this.refreshDelegate();
31917 * Destroys this panel
31919 destroy : function(){
31920 this.el.removeAllListeners();
31921 var tempEl = document.createElement("span");
31922 tempEl.appendChild(this.el.dom);
31923 tempEl.innerHTML = "";
31929 * form - if the content panel contains a form - this is a reference to it.
31930 * @type {Roo.form.Form}
31934 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31935 * This contains a reference to it.
31941 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31951 * @param {Object} cfg Xtype definition of item to add.
31954 addxtype : function(cfg) {
31956 if (cfg.xtype.match(/^Form$/)) {
31959 //if (this.footer) {
31960 // el = this.footer.container.insertSibling(false, 'before');
31962 el = this.el.createChild();
31965 this.form = new Roo.form.Form(cfg);
31968 if ( this.form.allItems.length) {
31969 this.form.render(el.dom);
31973 // should only have one of theses..
31974 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31975 // views.. should not be just added - used named prop 'view''
31977 cfg.el = this.el.appendChild(document.createElement("div"));
31980 var ret = new Roo.factory(cfg);
31982 ret.render && ret.render(false, ''); // render blank..
31991 * @class Roo.GridPanel
31992 * @extends Roo.ContentPanel
31994 * Create a new GridPanel.
31995 * @param {Roo.grid.Grid} grid The grid for this panel
31996 * @param {String/Object} config A string to set only the panel's title, or a config object
31998 Roo.GridPanel = function(grid, config){
32001 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
32002 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
32004 this.wrapper.dom.appendChild(grid.getGridEl().dom);
32006 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
32009 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
32011 // xtype created footer. - not sure if will work as we normally have to render first..
32012 if (this.footer && !this.footer.el && this.footer.xtype) {
32014 this.footer.container = this.grid.getView().getFooterPanel(true);
32015 this.footer.dataSource = this.grid.dataSource;
32016 this.footer = Roo.factory(this.footer, Roo);
32020 grid.monitorWindowResize = false; // turn off autosizing
32021 grid.autoHeight = false;
32022 grid.autoWidth = false;
32024 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
32027 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
32028 getId : function(){
32029 return this.grid.id;
32033 * Returns the grid for this panel
32034 * @return {Roo.grid.Grid}
32036 getGrid : function(){
32040 setSize : function(width, height){
32041 if(!this.ignoreResize(width, height)){
32042 var grid = this.grid;
32043 var size = this.adjustForComponents(width, height);
32044 grid.getGridEl().setSize(size.width, size.height);
32049 beforeSlide : function(){
32050 this.grid.getView().scroller.clip();
32053 afterSlide : function(){
32054 this.grid.getView().scroller.unclip();
32057 destroy : function(){
32058 this.grid.destroy();
32060 Roo.GridPanel.superclass.destroy.call(this);
32066 * @class Roo.NestedLayoutPanel
32067 * @extends Roo.ContentPanel
32069 * Create a new NestedLayoutPanel.
32072 * @param {Roo.BorderLayout} layout The layout for this panel
32073 * @param {String/Object} config A string to set only the title or a config object
32075 Roo.NestedLayoutPanel = function(layout, config)
32077 // construct with only one argument..
32078 /* FIXME - implement nicer consturctors
32079 if (layout.layout) {
32081 layout = config.layout;
32082 delete config.layout;
32084 if (layout.xtype && !layout.getEl) {
32085 // then layout needs constructing..
32086 layout = Roo.factory(layout, Roo);
32091 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
32093 layout.monitorWindowResize = false; // turn off autosizing
32094 this.layout = layout;
32095 this.layout.getEl().addClass("x-layout-nested-layout");
32102 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
32104 setSize : function(width, height){
32105 if(!this.ignoreResize(width, height)){
32106 var size = this.adjustForComponents(width, height);
32107 var el = this.layout.getEl();
32108 el.setSize(size.width, size.height);
32109 var touch = el.dom.offsetWidth;
32110 this.layout.layout();
32111 // ie requires a double layout on the first pass
32112 if(Roo.isIE && !this.initialized){
32113 this.initialized = true;
32114 this.layout.layout();
32119 // activate all subpanels if not currently active..
32121 setActiveState : function(active){
32122 this.active = active;
32124 this.fireEvent("deactivate", this);
32128 this.fireEvent("activate", this);
32129 // not sure if this should happen before or after..
32130 if (!this.layout) {
32131 return; // should not happen..
32134 for (var r in this.layout.regions) {
32135 reg = this.layout.getRegion(r);
32136 if (reg.getActivePanel()) {
32137 //reg.showPanel(reg.getActivePanel()); // force it to activate..
32138 reg.setActivePanel(reg.getActivePanel());
32141 if (!reg.panels.length) {
32144 reg.showPanel(reg.getPanel(0));
32153 * Returns the nested BorderLayout for this panel
32154 * @return {Roo.BorderLayout}
32156 getLayout : function(){
32157 return this.layout;
32161 * Adds a xtype elements to the layout of the nested panel
32165 xtype : 'ContentPanel',
32172 xtype : 'NestedLayoutPanel',
32178 items : [ ... list of content panels or nested layout panels.. ]
32182 * @param {Object} cfg Xtype definition of item to add.
32184 addxtype : function(cfg) {
32185 return this.layout.addxtype(cfg);
32190 Roo.ScrollPanel = function(el, config, content){
32191 config = config || {};
32192 config.fitToFrame = true;
32193 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
32195 this.el.dom.style.overflow = "hidden";
32196 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
32197 this.el.removeClass("x-layout-inactive-content");
32198 this.el.on("mousewheel", this.onWheel, this);
32200 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
32201 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
32202 up.unselectable(); down.unselectable();
32203 up.on("click", this.scrollUp, this);
32204 down.on("click", this.scrollDown, this);
32205 up.addClassOnOver("x-scroller-btn-over");
32206 down.addClassOnOver("x-scroller-btn-over");
32207 up.addClassOnClick("x-scroller-btn-click");
32208 down.addClassOnClick("x-scroller-btn-click");
32209 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
32211 this.resizeEl = this.el;
32212 this.el = wrap; this.up = up; this.down = down;
32215 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
32217 wheelIncrement : 5,
32218 scrollUp : function(){
32219 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
32222 scrollDown : function(){
32223 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
32226 afterScroll : function(){
32227 var el = this.resizeEl;
32228 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
32229 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32230 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32233 setSize : function(){
32234 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
32235 this.afterScroll();
32238 onWheel : function(e){
32239 var d = e.getWheelDelta();
32240 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
32241 this.afterScroll();
32245 setContent : function(content, loadScripts){
32246 this.resizeEl.update(content, loadScripts);
32260 * @class Roo.TreePanel
32261 * @extends Roo.ContentPanel
32263 * Create a new TreePanel. - defaults to fit/scoll contents.
32264 * @param {String/Object} config A string to set only the panel's title, or a config object
32265 * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32267 Roo.TreePanel = function(config){
32268 var el = config.el;
32269 var tree = config.tree;
32270 delete config.tree;
32271 delete config.el; // hopefull!
32273 // wrapper for IE7 strict & safari scroll issue
32275 var treeEl = el.createChild();
32276 config.resizeEl = treeEl;
32280 Roo.TreePanel.superclass.constructor.call(this, el, config);
32283 this.tree = new Roo.tree.TreePanel(treeEl , tree);
32284 //console.log(tree);
32285 this.on('activate', function()
32287 if (this.tree.rendered) {
32290 //console.log('render tree');
32291 this.tree.render();
32293 // this should not be needed.. - it's actually the 'el' that resizes?
32294 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32296 //this.on('resize', function (cp, w, h) {
32297 // this.tree.innerCt.setWidth(w);
32298 // this.tree.innerCt.setHeight(h);
32299 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
32306 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
32323 * Ext JS Library 1.1.1
32324 * Copyright(c) 2006-2007, Ext JS, LLC.
32326 * Originally Released Under LGPL - original licence link has changed is not relivant.
32329 * <script type="text/javascript">
32334 * @class Roo.ReaderLayout
32335 * @extends Roo.BorderLayout
32336 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
32337 * center region containing two nested regions (a top one for a list view and one for item preview below),
32338 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32339 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32340 * expedites the setup of the overall layout and regions for this common application style.
32343 var reader = new Roo.ReaderLayout();
32344 var CP = Roo.ContentPanel; // shortcut for adding
32346 reader.beginUpdate();
32347 reader.add("north", new CP("north", "North"));
32348 reader.add("west", new CP("west", {title: "West"}));
32349 reader.add("east", new CP("east", {title: "East"}));
32351 reader.regions.listView.add(new CP("listView", "List"));
32352 reader.regions.preview.add(new CP("preview", "Preview"));
32353 reader.endUpdate();
32356 * Create a new ReaderLayout
32357 * @param {Object} config Configuration options
32358 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32359 * document.body if omitted)
32361 Roo.ReaderLayout = function(config, renderTo){
32362 var c = config || {size:{}};
32363 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32364 north: c.north !== false ? Roo.apply({
32368 }, c.north) : false,
32369 west: c.west !== false ? Roo.apply({
32377 margins:{left:5,right:0,bottom:5,top:5},
32378 cmargins:{left:5,right:5,bottom:5,top:5}
32379 }, c.west) : false,
32380 east: c.east !== false ? Roo.apply({
32388 margins:{left:0,right:5,bottom:5,top:5},
32389 cmargins:{left:5,right:5,bottom:5,top:5}
32390 }, c.east) : false,
32391 center: Roo.apply({
32392 tabPosition: 'top',
32396 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32400 this.el.addClass('x-reader');
32402 this.beginUpdate();
32404 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32405 south: c.preview !== false ? Roo.apply({
32412 cmargins:{top:5,left:0, right:0, bottom:0}
32413 }, c.preview) : false,
32414 center: Roo.apply({
32420 this.add('center', new Roo.NestedLayoutPanel(inner,
32421 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32425 this.regions.preview = inner.getRegion('south');
32426 this.regions.listView = inner.getRegion('center');
32429 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32431 * Ext JS Library 1.1.1
32432 * Copyright(c) 2006-2007, Ext JS, LLC.
32434 * Originally Released Under LGPL - original licence link has changed is not relivant.
32437 * <script type="text/javascript">
32441 * @class Roo.grid.Grid
32442 * @extends Roo.util.Observable
32443 * This class represents the primary interface of a component based grid control.
32444 * <br><br>Usage:<pre><code>
32445 var grid = new Roo.grid.Grid("my-container-id", {
32448 selModel: mySelectionModel,
32449 autoSizeColumns: true,
32450 monitorWindowResize: false,
32451 trackMouseOver: true
32456 * <b>Common Problems:</b><br/>
32457 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32458 * element will correct this<br/>
32459 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32460 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32461 * are unpredictable.<br/>
32462 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32463 * grid to calculate dimensions/offsets.<br/>
32465 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32466 * The container MUST have some type of size defined for the grid to fill. The container will be
32467 * automatically set to position relative if it isn't already.
32468 * @param {Object} config A config object that sets properties on this grid.
32470 Roo.grid.Grid = function(container, config){
32471 // initialize the container
32472 this.container = Roo.get(container);
32473 this.container.update("");
32474 this.container.setStyle("overflow", "hidden");
32475 this.container.addClass('x-grid-container');
32477 this.id = this.container.id;
32479 Roo.apply(this, config);
32480 // check and correct shorthanded configs
32482 this.dataSource = this.ds;
32486 this.colModel = this.cm;
32490 this.selModel = this.sm;
32494 if (this.selModel) {
32495 this.selModel = Roo.factory(this.selModel, Roo.grid);
32496 this.sm = this.selModel;
32497 this.sm.xmodule = this.xmodule || false;
32499 if (typeof(this.colModel.config) == 'undefined') {
32500 this.colModel = new Roo.grid.ColumnModel(this.colModel);
32501 this.cm = this.colModel;
32502 this.cm.xmodule = this.xmodule || false;
32504 if (this.dataSource) {
32505 this.dataSource= Roo.factory(this.dataSource, Roo.data);
32506 this.ds = this.dataSource;
32507 this.ds.xmodule = this.xmodule || false;
32514 this.container.setWidth(this.width);
32518 this.container.setHeight(this.height);
32525 * The raw click event for the entire grid.
32526 * @param {Roo.EventObject} e
32531 * The raw dblclick event for the entire grid.
32532 * @param {Roo.EventObject} e
32536 * @event contextmenu
32537 * The raw contextmenu event for the entire grid.
32538 * @param {Roo.EventObject} e
32540 "contextmenu" : true,
32543 * The raw mousedown event for the entire grid.
32544 * @param {Roo.EventObject} e
32546 "mousedown" : true,
32549 * The raw mouseup event for the entire grid.
32550 * @param {Roo.EventObject} e
32555 * The raw mouseover event for the entire grid.
32556 * @param {Roo.EventObject} e
32558 "mouseover" : true,
32561 * The raw mouseout event for the entire grid.
32562 * @param {Roo.EventObject} e
32567 * The raw keypress event for the entire grid.
32568 * @param {Roo.EventObject} e
32573 * The raw keydown event for the entire grid.
32574 * @param {Roo.EventObject} e
32582 * Fires when a cell is clicked
32583 * @param {Grid} this
32584 * @param {Number} rowIndex
32585 * @param {Number} columnIndex
32586 * @param {Roo.EventObject} e
32588 "cellclick" : true,
32590 * @event celldblclick
32591 * Fires when a cell is double clicked
32592 * @param {Grid} this
32593 * @param {Number} rowIndex
32594 * @param {Number} columnIndex
32595 * @param {Roo.EventObject} e
32597 "celldblclick" : true,
32600 * Fires when a row is clicked
32601 * @param {Grid} this
32602 * @param {Number} rowIndex
32603 * @param {Roo.EventObject} e
32607 * @event rowdblclick
32608 * Fires when a row is double clicked
32609 * @param {Grid} this
32610 * @param {Number} rowIndex
32611 * @param {Roo.EventObject} e
32613 "rowdblclick" : true,
32615 * @event headerclick
32616 * Fires when a header is clicked
32617 * @param {Grid} this
32618 * @param {Number} columnIndex
32619 * @param {Roo.EventObject} e
32621 "headerclick" : true,
32623 * @event headerdblclick
32624 * Fires when a header cell is double clicked
32625 * @param {Grid} this
32626 * @param {Number} columnIndex
32627 * @param {Roo.EventObject} e
32629 "headerdblclick" : true,
32631 * @event rowcontextmenu
32632 * Fires when a row is right clicked
32633 * @param {Grid} this
32634 * @param {Number} rowIndex
32635 * @param {Roo.EventObject} e
32637 "rowcontextmenu" : true,
32639 * @event cellcontextmenu
32640 * Fires when a cell is right clicked
32641 * @param {Grid} this
32642 * @param {Number} rowIndex
32643 * @param {Number} cellIndex
32644 * @param {Roo.EventObject} e
32646 "cellcontextmenu" : true,
32648 * @event headercontextmenu
32649 * Fires when a header is right clicked
32650 * @param {Grid} this
32651 * @param {Number} columnIndex
32652 * @param {Roo.EventObject} e
32654 "headercontextmenu" : true,
32656 * @event bodyscroll
32657 * Fires when the body element is scrolled
32658 * @param {Number} scrollLeft
32659 * @param {Number} scrollTop
32661 "bodyscroll" : true,
32663 * @event columnresize
32664 * Fires when the user resizes a column
32665 * @param {Number} columnIndex
32666 * @param {Number} newSize
32668 "columnresize" : true,
32670 * @event columnmove
32671 * Fires when the user moves a column
32672 * @param {Number} oldIndex
32673 * @param {Number} newIndex
32675 "columnmove" : true,
32678 * Fires when row(s) start being dragged
32679 * @param {Grid} this
32680 * @param {Roo.GridDD} dd The drag drop object
32681 * @param {event} e The raw browser event
32683 "startdrag" : true,
32686 * Fires when a drag operation is complete
32687 * @param {Grid} this
32688 * @param {Roo.GridDD} dd The drag drop object
32689 * @param {event} e The raw browser event
32694 * Fires when dragged row(s) are dropped on a valid DD target
32695 * @param {Grid} this
32696 * @param {Roo.GridDD} dd The drag drop object
32697 * @param {String} targetId The target drag drop object
32698 * @param {event} e The raw browser event
32703 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32704 * @param {Grid} this
32705 * @param {Roo.GridDD} dd The drag drop object
32706 * @param {String} targetId The target drag drop object
32707 * @param {event} e The raw browser event
32712 * Fires when the dragged row(s) first cross another DD target while being dragged
32713 * @param {Grid} this
32714 * @param {Roo.GridDD} dd The drag drop object
32715 * @param {String} targetId The target drag drop object
32716 * @param {event} e The raw browser event
32718 "dragenter" : true,
32721 * Fires when the dragged row(s) leave another DD target while being dragged
32722 * @param {Grid} this
32723 * @param {Roo.GridDD} dd The drag drop object
32724 * @param {String} targetId The target drag drop object
32725 * @param {event} e The raw browser event
32730 * Fires when a row is rendered, so you can change add a style to it.
32731 * @param {GridView} gridview The grid view
32732 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
32738 * Fires when the grid is rendered
32739 * @param {Grid} grid
32744 Roo.grid.Grid.superclass.constructor.call(this);
32746 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32749 * @cfg {String} ddGroup - drag drop group.
32753 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32755 minColumnWidth : 25,
32758 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32759 * <b>on initial render.</b> It is more efficient to explicitly size the columns
32760 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
32762 autoSizeColumns : false,
32765 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32767 autoSizeHeaders : true,
32770 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32772 monitorWindowResize : true,
32775 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32776 * rows measured to get a columns size. Default is 0 (all rows).
32778 maxRowsToMeasure : 0,
32781 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32783 trackMouseOver : true,
32786 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
32790 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32792 enableDragDrop : false,
32795 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32797 enableColumnMove : true,
32800 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32802 enableColumnHide : true,
32805 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32807 enableRowHeightSync : false,
32810 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
32815 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32817 autoHeight : false,
32820 * @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.
32822 autoExpandColumn : false,
32825 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32828 autoExpandMin : 50,
32831 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32833 autoExpandMax : 1000,
32836 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32841 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32845 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32855 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32856 * of a fixed width. Default is false.
32859 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32862 * Called once after all setup has been completed and the grid is ready to be rendered.
32863 * @return {Roo.grid.Grid} this
32865 render : function()
32867 var c = this.container;
32868 // try to detect autoHeight/width mode
32869 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32870 this.autoHeight = true;
32872 var view = this.getView();
32875 c.on("click", this.onClick, this);
32876 c.on("dblclick", this.onDblClick, this);
32877 c.on("contextmenu", this.onContextMenu, this);
32878 c.on("keydown", this.onKeyDown, this);
32880 c.on("touchstart", this.onTouchStart, this);
32883 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32885 this.getSelectionModel().init(this);
32890 this.loadMask = new Roo.LoadMask(this.container,
32891 Roo.apply({store:this.dataSource}, this.loadMask));
32895 if (this.toolbar && this.toolbar.xtype) {
32896 this.toolbar.container = this.getView().getHeaderPanel(true);
32897 this.toolbar = new Roo.Toolbar(this.toolbar);
32899 if (this.footer && this.footer.xtype) {
32900 this.footer.dataSource = this.getDataSource();
32901 this.footer.container = this.getView().getFooterPanel(true);
32902 this.footer = Roo.factory(this.footer, Roo);
32904 if (this.dropTarget && this.dropTarget.xtype) {
32905 delete this.dropTarget.xtype;
32906 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32910 this.rendered = true;
32911 this.fireEvent('render', this);
32916 * Reconfigures the grid to use a different Store and Column Model.
32917 * The View will be bound to the new objects and refreshed.
32918 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32919 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32921 reconfigure : function(dataSource, colModel){
32923 this.loadMask.destroy();
32924 this.loadMask = new Roo.LoadMask(this.container,
32925 Roo.apply({store:dataSource}, this.loadMask));
32927 this.view.bind(dataSource, colModel);
32928 this.dataSource = dataSource;
32929 this.colModel = colModel;
32930 this.view.refresh(true);
32934 onKeyDown : function(e){
32935 this.fireEvent("keydown", e);
32939 * Destroy this grid.
32940 * @param {Boolean} removeEl True to remove the element
32942 destroy : function(removeEl, keepListeners){
32944 this.loadMask.destroy();
32946 var c = this.container;
32947 c.removeAllListeners();
32948 this.view.destroy();
32949 this.colModel.purgeListeners();
32950 if(!keepListeners){
32951 this.purgeListeners();
32954 if(removeEl === true){
32960 processEvent : function(name, e){
32961 // does this fire select???
32962 //Roo.log('grid:processEvent ' + name);
32964 if (name != 'touchstart' ) {
32965 this.fireEvent(name, e);
32968 var t = e.getTarget();
32970 var header = v.findHeaderIndex(t);
32971 if(header !== false){
32972 var ename = name == 'touchstart' ? 'click' : name;
32974 this.fireEvent("header" + ename, this, header, e);
32976 var row = v.findRowIndex(t);
32977 var cell = v.findCellIndex(t);
32978 if (name == 'touchstart') {
32979 // first touch is always a click.
32980 // hopefull this happens after selection is updated.?
32983 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32984 var cs = this.selModel.getSelectedCell();
32985 if (row == cs[0] && cell == cs[1]){
32989 if (typeof(this.selModel.getSelections) != 'undefined') {
32990 var cs = this.selModel.getSelections();
32991 var ds = this.dataSource;
32992 if (cs.length == 1 && ds.getAt(row) == cs[0]){
33003 this.fireEvent("row" + name, this, row, e);
33004 if(cell !== false){
33005 this.fireEvent("cell" + name, this, row, cell, e);
33012 onClick : function(e){
33013 this.processEvent("click", e);
33016 onTouchStart : function(e){
33017 this.processEvent("touchstart", e);
33021 onContextMenu : function(e, t){
33022 this.processEvent("contextmenu", e);
33026 onDblClick : function(e){
33027 this.processEvent("dblclick", e);
33031 walkCells : function(row, col, step, fn, scope){
33032 var cm = this.colModel, clen = cm.getColumnCount();
33033 var ds = this.dataSource, rlen = ds.getCount(), first = true;
33045 if(fn.call(scope || this, row, col, cm) === true){
33063 if(fn.call(scope || this, row, col, cm) === true){
33075 getSelections : function(){
33076 return this.selModel.getSelections();
33080 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
33081 * but if manual update is required this method will initiate it.
33083 autoSize : function(){
33085 this.view.layout();
33086 if(this.view.adjustForScroll){
33087 this.view.adjustForScroll();
33093 * Returns the grid's underlying element.
33094 * @return {Element} The element
33096 getGridEl : function(){
33097 return this.container;
33100 // private for compatibility, overridden by editor grid
33101 stopEditing : function(){},
33104 * Returns the grid's SelectionModel.
33105 * @return {SelectionModel}
33107 getSelectionModel : function(){
33108 if(!this.selModel){
33109 this.selModel = new Roo.grid.RowSelectionModel();
33111 return this.selModel;
33115 * Returns the grid's DataSource.
33116 * @return {DataSource}
33118 getDataSource : function(){
33119 return this.dataSource;
33123 * Returns the grid's ColumnModel.
33124 * @return {ColumnModel}
33126 getColumnModel : function(){
33127 return this.colModel;
33131 * Returns the grid's GridView object.
33132 * @return {GridView}
33134 getView : function(){
33136 this.view = new Roo.grid.GridView(this.viewConfig);
33141 * Called to get grid's drag proxy text, by default returns this.ddText.
33144 getDragDropText : function(){
33145 var count = this.selModel.getCount();
33146 return String.format(this.ddText, count, count == 1 ? '' : 's');
33150 * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
33151 * %0 is replaced with the number of selected rows.
33154 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
33156 * Ext JS Library 1.1.1
33157 * Copyright(c) 2006-2007, Ext JS, LLC.
33159 * Originally Released Under LGPL - original licence link has changed is not relivant.
33162 * <script type="text/javascript">
33165 Roo.grid.AbstractGridView = function(){
33169 "beforerowremoved" : true,
33170 "beforerowsinserted" : true,
33171 "beforerefresh" : true,
33172 "rowremoved" : true,
33173 "rowsinserted" : true,
33174 "rowupdated" : true,
33177 Roo.grid.AbstractGridView.superclass.constructor.call(this);
33180 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
33181 rowClass : "x-grid-row",
33182 cellClass : "x-grid-cell",
33183 tdClass : "x-grid-td",
33184 hdClass : "x-grid-hd",
33185 splitClass : "x-grid-hd-split",
33187 init: function(grid){
33189 var cid = this.grid.getGridEl().id;
33190 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33191 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33192 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33193 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33196 getColumnRenderers : function(){
33197 var renderers = [];
33198 var cm = this.grid.colModel;
33199 var colCount = cm.getColumnCount();
33200 for(var i = 0; i < colCount; i++){
33201 renderers[i] = cm.getRenderer(i);
33206 getColumnIds : function(){
33208 var cm = this.grid.colModel;
33209 var colCount = cm.getColumnCount();
33210 for(var i = 0; i < colCount; i++){
33211 ids[i] = cm.getColumnId(i);
33216 getDataIndexes : function(){
33217 if(!this.indexMap){
33218 this.indexMap = this.buildIndexMap();
33220 return this.indexMap.colToData;
33223 getColumnIndexByDataIndex : function(dataIndex){
33224 if(!this.indexMap){
33225 this.indexMap = this.buildIndexMap();
33227 return this.indexMap.dataToCol[dataIndex];
33231 * Set a css style for a column dynamically.
33232 * @param {Number} colIndex The index of the column
33233 * @param {String} name The css property name
33234 * @param {String} value The css value
33236 setCSSStyle : function(colIndex, name, value){
33237 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33238 Roo.util.CSS.updateRule(selector, name, value);
33241 generateRules : function(cm){
33242 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33243 Roo.util.CSS.removeStyleSheet(rulesId);
33244 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33245 var cid = cm.getColumnId(i);
33246 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33247 this.tdSelector, cid, " {\n}\n",
33248 this.hdSelector, cid, " {\n}\n",
33249 this.splitSelector, cid, " {\n}\n");
33251 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33255 * Ext JS Library 1.1.1
33256 * Copyright(c) 2006-2007, Ext JS, LLC.
33258 * Originally Released Under LGPL - original licence link has changed is not relivant.
33261 * <script type="text/javascript">
33265 // This is a support class used internally by the Grid components
33266 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33268 this.view = grid.getView();
33269 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33270 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33272 this.setHandleElId(Roo.id(hd));
33273 this.setOuterHandleElId(Roo.id(hd2));
33275 this.scroll = false;
33277 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33279 getDragData : function(e){
33280 var t = Roo.lib.Event.getTarget(e);
33281 var h = this.view.findHeaderCell(t);
33283 return {ddel: h.firstChild, header:h};
33288 onInitDrag : function(e){
33289 this.view.headersDisabled = true;
33290 var clone = this.dragData.ddel.cloneNode(true);
33291 clone.id = Roo.id();
33292 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33293 this.proxy.update(clone);
33297 afterValidDrop : function(){
33299 setTimeout(function(){
33300 v.headersDisabled = false;
33304 afterInvalidDrop : function(){
33306 setTimeout(function(){
33307 v.headersDisabled = false;
33313 * Ext JS Library 1.1.1
33314 * Copyright(c) 2006-2007, Ext JS, LLC.
33316 * Originally Released Under LGPL - original licence link has changed is not relivant.
33319 * <script type="text/javascript">
33322 // This is a support class used internally by the Grid components
33323 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33325 this.view = grid.getView();
33326 // split the proxies so they don't interfere with mouse events
33327 this.proxyTop = Roo.DomHelper.append(document.body, {
33328 cls:"col-move-top", html:" "
33330 this.proxyBottom = Roo.DomHelper.append(document.body, {
33331 cls:"col-move-bottom", html:" "
33333 this.proxyTop.hide = this.proxyBottom.hide = function(){
33334 this.setLeftTop(-100,-100);
33335 this.setStyle("visibility", "hidden");
33337 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33338 // temporarily disabled
33339 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33340 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33342 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33343 proxyOffsets : [-4, -9],
33344 fly: Roo.Element.fly,
33346 getTargetFromEvent : function(e){
33347 var t = Roo.lib.Event.getTarget(e);
33348 var cindex = this.view.findCellIndex(t);
33349 if(cindex !== false){
33350 return this.view.getHeaderCell(cindex);
33355 nextVisible : function(h){
33356 var v = this.view, cm = this.grid.colModel;
33359 if(!cm.isHidden(v.getCellIndex(h))){
33367 prevVisible : function(h){
33368 var v = this.view, cm = this.grid.colModel;
33371 if(!cm.isHidden(v.getCellIndex(h))){
33379 positionIndicator : function(h, n, e){
33380 var x = Roo.lib.Event.getPageX(e);
33381 var r = Roo.lib.Dom.getRegion(n.firstChild);
33382 var px, pt, py = r.top + this.proxyOffsets[1];
33383 if((r.right - x) <= (r.right-r.left)/2){
33384 px = r.right+this.view.borderWidth;
33390 var oldIndex = this.view.getCellIndex(h);
33391 var newIndex = this.view.getCellIndex(n);
33393 if(this.grid.colModel.isFixed(newIndex)){
33397 var locked = this.grid.colModel.isLocked(newIndex);
33402 if(oldIndex < newIndex){
33405 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33408 px += this.proxyOffsets[0];
33409 this.proxyTop.setLeftTop(px, py);
33410 this.proxyTop.show();
33411 if(!this.bottomOffset){
33412 this.bottomOffset = this.view.mainHd.getHeight();
33414 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33415 this.proxyBottom.show();
33419 onNodeEnter : function(n, dd, e, data){
33420 if(data.header != n){
33421 this.positionIndicator(data.header, n, e);
33425 onNodeOver : function(n, dd, e, data){
33426 var result = false;
33427 if(data.header != n){
33428 result = this.positionIndicator(data.header, n, e);
33431 this.proxyTop.hide();
33432 this.proxyBottom.hide();
33434 return result ? this.dropAllowed : this.dropNotAllowed;
33437 onNodeOut : function(n, dd, e, data){
33438 this.proxyTop.hide();
33439 this.proxyBottom.hide();
33442 onNodeDrop : function(n, dd, e, data){
33443 var h = data.header;
33445 var cm = this.grid.colModel;
33446 var x = Roo.lib.Event.getPageX(e);
33447 var r = Roo.lib.Dom.getRegion(n.firstChild);
33448 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33449 var oldIndex = this.view.getCellIndex(h);
33450 var newIndex = this.view.getCellIndex(n);
33451 var locked = cm.isLocked(newIndex);
33455 if(oldIndex < newIndex){
33458 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33461 cm.setLocked(oldIndex, locked, true);
33462 cm.moveColumn(oldIndex, newIndex);
33463 this.grid.fireEvent("columnmove", oldIndex, newIndex);
33471 * Ext JS Library 1.1.1
33472 * Copyright(c) 2006-2007, Ext JS, LLC.
33474 * Originally Released Under LGPL - original licence link has changed is not relivant.
33477 * <script type="text/javascript">
33481 * @class Roo.grid.GridView
33482 * @extends Roo.util.Observable
33485 * @param {Object} config
33487 Roo.grid.GridView = function(config){
33488 Roo.grid.GridView.superclass.constructor.call(this);
33491 Roo.apply(this, config);
33494 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33496 unselectable : 'unselectable="on"',
33497 unselectableCls : 'x-unselectable',
33500 rowClass : "x-grid-row",
33502 cellClass : "x-grid-col",
33504 tdClass : "x-grid-td",
33506 hdClass : "x-grid-hd",
33508 splitClass : "x-grid-split",
33510 sortClasses : ["sort-asc", "sort-desc"],
33512 enableMoveAnim : false,
33516 dh : Roo.DomHelper,
33518 fly : Roo.Element.fly,
33520 css : Roo.util.CSS,
33526 scrollIncrement : 22,
33528 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33530 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33532 bind : function(ds, cm){
33534 this.ds.un("load", this.onLoad, this);
33535 this.ds.un("datachanged", this.onDataChange, this);
33536 this.ds.un("add", this.onAdd, this);
33537 this.ds.un("remove", this.onRemove, this);
33538 this.ds.un("update", this.onUpdate, this);
33539 this.ds.un("clear", this.onClear, this);
33542 ds.on("load", this.onLoad, this);
33543 ds.on("datachanged", this.onDataChange, this);
33544 ds.on("add", this.onAdd, this);
33545 ds.on("remove", this.onRemove, this);
33546 ds.on("update", this.onUpdate, this);
33547 ds.on("clear", this.onClear, this);
33552 this.cm.un("widthchange", this.onColWidthChange, this);
33553 this.cm.un("headerchange", this.onHeaderChange, this);
33554 this.cm.un("hiddenchange", this.onHiddenChange, this);
33555 this.cm.un("columnmoved", this.onColumnMove, this);
33556 this.cm.un("columnlockchange", this.onColumnLock, this);
33559 this.generateRules(cm);
33560 cm.on("widthchange", this.onColWidthChange, this);
33561 cm.on("headerchange", this.onHeaderChange, this);
33562 cm.on("hiddenchange", this.onHiddenChange, this);
33563 cm.on("columnmoved", this.onColumnMove, this);
33564 cm.on("columnlockchange", this.onColumnLock, this);
33569 init: function(grid){
33570 Roo.grid.GridView.superclass.init.call(this, grid);
33572 this.bind(grid.dataSource, grid.colModel);
33574 grid.on("headerclick", this.handleHeaderClick, this);
33576 if(grid.trackMouseOver){
33577 grid.on("mouseover", this.onRowOver, this);
33578 grid.on("mouseout", this.onRowOut, this);
33580 grid.cancelTextSelection = function(){};
33581 this.gridId = grid.id;
33583 var tpls = this.templates || {};
33586 tpls.master = new Roo.Template(
33587 '<div class="x-grid" hidefocus="true">',
33588 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33589 '<div class="x-grid-topbar"></div>',
33590 '<div class="x-grid-scroller"><div></div></div>',
33591 '<div class="x-grid-locked">',
33592 '<div class="x-grid-header">{lockedHeader}</div>',
33593 '<div class="x-grid-body">{lockedBody}</div>',
33595 '<div class="x-grid-viewport">',
33596 '<div class="x-grid-header">{header}</div>',
33597 '<div class="x-grid-body">{body}</div>',
33599 '<div class="x-grid-bottombar"></div>',
33601 '<div class="x-grid-resize-proxy"> </div>',
33604 tpls.master.disableformats = true;
33608 tpls.header = new Roo.Template(
33609 '<table border="0" cellspacing="0" cellpadding="0">',
33610 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33613 tpls.header.disableformats = true;
33615 tpls.header.compile();
33618 tpls.hcell = new Roo.Template(
33619 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33620 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33623 tpls.hcell.disableFormats = true;
33625 tpls.hcell.compile();
33628 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33629 this.unselectableCls + '" ' + this.unselectable +'> </div>');
33630 tpls.hsplit.disableFormats = true;
33632 tpls.hsplit.compile();
33635 tpls.body = new Roo.Template(
33636 '<table border="0" cellspacing="0" cellpadding="0">',
33637 "<tbody>{rows}</tbody>",
33640 tpls.body.disableFormats = true;
33642 tpls.body.compile();
33645 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33646 tpls.row.disableFormats = true;
33648 tpls.row.compile();
33651 tpls.cell = new Roo.Template(
33652 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33653 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33654 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33657 tpls.cell.disableFormats = true;
33659 tpls.cell.compile();
33661 this.templates = tpls;
33664 // remap these for backwards compat
33665 onColWidthChange : function(){
33666 this.updateColumns.apply(this, arguments);
33668 onHeaderChange : function(){
33669 this.updateHeaders.apply(this, arguments);
33671 onHiddenChange : function(){
33672 this.handleHiddenChange.apply(this, arguments);
33674 onColumnMove : function(){
33675 this.handleColumnMove.apply(this, arguments);
33677 onColumnLock : function(){
33678 this.handleLockChange.apply(this, arguments);
33681 onDataChange : function(){
33683 this.updateHeaderSortState();
33686 onClear : function(){
33690 onUpdate : function(ds, record){
33691 this.refreshRow(record);
33694 refreshRow : function(record){
33695 var ds = this.ds, index;
33696 if(typeof record == 'number'){
33698 record = ds.getAt(index);
33700 index = ds.indexOf(record);
33702 this.insertRows(ds, index, index, true);
33703 this.onRemove(ds, record, index+1, true);
33704 this.syncRowHeights(index, index);
33706 this.fireEvent("rowupdated", this, index, record);
33709 onAdd : function(ds, records, index){
33710 this.insertRows(ds, index, index + (records.length-1));
33713 onRemove : function(ds, record, index, isUpdate){
33714 if(isUpdate !== true){
33715 this.fireEvent("beforerowremoved", this, index, record);
33717 var bt = this.getBodyTable(), lt = this.getLockedTable();
33718 if(bt.rows[index]){
33719 bt.firstChild.removeChild(bt.rows[index]);
33721 if(lt.rows[index]){
33722 lt.firstChild.removeChild(lt.rows[index]);
33724 if(isUpdate !== true){
33725 this.stripeRows(index);
33726 this.syncRowHeights(index, index);
33728 this.fireEvent("rowremoved", this, index, record);
33732 onLoad : function(){
33733 this.scrollToTop();
33737 * Scrolls the grid to the top
33739 scrollToTop : function(){
33741 this.scroller.dom.scrollTop = 0;
33747 * Gets a panel in the header of the grid that can be used for toolbars etc.
33748 * After modifying the contents of this panel a call to grid.autoSize() may be
33749 * required to register any changes in size.
33750 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33751 * @return Roo.Element
33753 getHeaderPanel : function(doShow){
33755 this.headerPanel.show();
33757 return this.headerPanel;
33761 * Gets a panel in the footer of the grid that can be used for toolbars etc.
33762 * After modifying the contents of this panel a call to grid.autoSize() may be
33763 * required to register any changes in size.
33764 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33765 * @return Roo.Element
33767 getFooterPanel : function(doShow){
33769 this.footerPanel.show();
33771 return this.footerPanel;
33774 initElements : function(){
33775 var E = Roo.Element;
33776 var el = this.grid.getGridEl().dom.firstChild;
33777 var cs = el.childNodes;
33779 this.el = new E(el);
33781 this.focusEl = new E(el.firstChild);
33782 this.focusEl.swallowEvent("click", true);
33784 this.headerPanel = new E(cs[1]);
33785 this.headerPanel.enableDisplayMode("block");
33787 this.scroller = new E(cs[2]);
33788 this.scrollSizer = new E(this.scroller.dom.firstChild);
33790 this.lockedWrap = new E(cs[3]);
33791 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33792 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33794 this.mainWrap = new E(cs[4]);
33795 this.mainHd = new E(this.mainWrap.dom.firstChild);
33796 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33798 this.footerPanel = new E(cs[5]);
33799 this.footerPanel.enableDisplayMode("block");
33801 this.resizeProxy = new E(cs[6]);
33803 this.headerSelector = String.format(
33804 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33805 this.lockedHd.id, this.mainHd.id
33808 this.splitterSelector = String.format(
33809 '#{0} div.x-grid-split, #{1} div.x-grid-split',
33810 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33813 idToCssName : function(s)
33815 return s.replace(/[^a-z0-9]+/ig, '-');
33818 getHeaderCell : function(index){
33819 return Roo.DomQuery.select(this.headerSelector)[index];
33822 getHeaderCellMeasure : function(index){
33823 return this.getHeaderCell(index).firstChild;
33826 getHeaderCellText : function(index){
33827 return this.getHeaderCell(index).firstChild.firstChild;
33830 getLockedTable : function(){
33831 return this.lockedBody.dom.firstChild;
33834 getBodyTable : function(){
33835 return this.mainBody.dom.firstChild;
33838 getLockedRow : function(index){
33839 return this.getLockedTable().rows[index];
33842 getRow : function(index){
33843 return this.getBodyTable().rows[index];
33846 getRowComposite : function(index){
33848 this.rowEl = new Roo.CompositeElementLite();
33850 var els = [], lrow, mrow;
33851 if(lrow = this.getLockedRow(index)){
33854 if(mrow = this.getRow(index)){
33857 this.rowEl.elements = els;
33861 * Gets the 'td' of the cell
33863 * @param {Integer} rowIndex row to select
33864 * @param {Integer} colIndex column to select
33868 getCell : function(rowIndex, colIndex){
33869 var locked = this.cm.getLockedCount();
33871 if(colIndex < locked){
33872 source = this.lockedBody.dom.firstChild;
33874 source = this.mainBody.dom.firstChild;
33875 colIndex -= locked;
33877 return source.rows[rowIndex].childNodes[colIndex];
33880 getCellText : function(rowIndex, colIndex){
33881 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33884 getCellBox : function(cell){
33885 var b = this.fly(cell).getBox();
33886 if(Roo.isOpera){ // opera fails to report the Y
33887 b.y = cell.offsetTop + this.mainBody.getY();
33892 getCellIndex : function(cell){
33893 var id = String(cell.className).match(this.cellRE);
33895 return parseInt(id[1], 10);
33900 findHeaderIndex : function(n){
33901 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33902 return r ? this.getCellIndex(r) : false;
33905 findHeaderCell : function(n){
33906 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33907 return r ? r : false;
33910 findRowIndex : function(n){
33914 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33915 return r ? r.rowIndex : false;
33918 findCellIndex : function(node){
33919 var stop = this.el.dom;
33920 while(node && node != stop){
33921 if(this.findRE.test(node.className)){
33922 return this.getCellIndex(node);
33924 node = node.parentNode;
33929 getColumnId : function(index){
33930 return this.cm.getColumnId(index);
33933 getSplitters : function()
33935 if(this.splitterSelector){
33936 return Roo.DomQuery.select(this.splitterSelector);
33942 getSplitter : function(index){
33943 return this.getSplitters()[index];
33946 onRowOver : function(e, t){
33948 if((row = this.findRowIndex(t)) !== false){
33949 this.getRowComposite(row).addClass("x-grid-row-over");
33953 onRowOut : function(e, t){
33955 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33956 this.getRowComposite(row).removeClass("x-grid-row-over");
33960 renderHeaders : function(){
33962 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33963 var cb = [], lb = [], sb = [], lsb = [], p = {};
33964 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33965 p.cellId = "x-grid-hd-0-" + i;
33966 p.splitId = "x-grid-csplit-0-" + i;
33967 p.id = cm.getColumnId(i);
33968 p.value = cm.getColumnHeader(i) || "";
33969 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
33970 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33971 if(!cm.isLocked(i)){
33972 cb[cb.length] = ct.apply(p);
33973 sb[sb.length] = st.apply(p);
33975 lb[lb.length] = ct.apply(p);
33976 lsb[lsb.length] = st.apply(p);
33979 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33980 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33983 updateHeaders : function(){
33984 var html = this.renderHeaders();
33985 this.lockedHd.update(html[0]);
33986 this.mainHd.update(html[1]);
33990 * Focuses the specified row.
33991 * @param {Number} row The row index
33993 focusRow : function(row)
33995 //Roo.log('GridView.focusRow');
33996 var x = this.scroller.dom.scrollLeft;
33997 this.focusCell(row, 0, false);
33998 this.scroller.dom.scrollLeft = x;
34002 * Focuses the specified cell.
34003 * @param {Number} row The row index
34004 * @param {Number} col The column index
34005 * @param {Boolean} hscroll false to disable horizontal scrolling
34007 focusCell : function(row, col, hscroll)
34009 //Roo.log('GridView.focusCell');
34010 var el = this.ensureVisible(row, col, hscroll);
34011 this.focusEl.alignTo(el, "tl-tl");
34013 this.focusEl.focus();
34015 this.focusEl.focus.defer(1, this.focusEl);
34020 * Scrolls the specified cell into view
34021 * @param {Number} row The row index
34022 * @param {Number} col The column index
34023 * @param {Boolean} hscroll false to disable horizontal scrolling
34025 ensureVisible : function(row, col, hscroll)
34027 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
34028 //return null; //disable for testing.
34029 if(typeof row != "number"){
34030 row = row.rowIndex;
34032 if(row < 0 && row >= this.ds.getCount()){
34035 col = (col !== undefined ? col : 0);
34036 var cm = this.grid.colModel;
34037 while(cm.isHidden(col)){
34041 var el = this.getCell(row, col);
34045 var c = this.scroller.dom;
34047 var ctop = parseInt(el.offsetTop, 10);
34048 var cleft = parseInt(el.offsetLeft, 10);
34049 var cbot = ctop + el.offsetHeight;
34050 var cright = cleft + el.offsetWidth;
34052 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
34053 var stop = parseInt(c.scrollTop, 10);
34054 var sleft = parseInt(c.scrollLeft, 10);
34055 var sbot = stop + ch;
34056 var sright = sleft + c.clientWidth;
34058 Roo.log('GridView.ensureVisible:' +
34060 ' c.clientHeight:' + c.clientHeight +
34061 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
34069 c.scrollTop = ctop;
34070 //Roo.log("set scrolltop to ctop DISABLE?");
34071 }else if(cbot > sbot){
34072 //Roo.log("set scrolltop to cbot-ch");
34073 c.scrollTop = cbot-ch;
34076 if(hscroll !== false){
34078 c.scrollLeft = cleft;
34079 }else if(cright > sright){
34080 c.scrollLeft = cright-c.clientWidth;
34087 updateColumns : function(){
34088 this.grid.stopEditing();
34089 var cm = this.grid.colModel, colIds = this.getColumnIds();
34090 //var totalWidth = cm.getTotalWidth();
34092 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34093 //if(cm.isHidden(i)) continue;
34094 var w = cm.getColumnWidth(i);
34095 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34096 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34098 this.updateSplitters();
34101 generateRules : function(cm){
34102 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
34103 Roo.util.CSS.removeStyleSheet(rulesId);
34104 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34105 var cid = cm.getColumnId(i);
34107 if(cm.config[i].align){
34108 align = 'text-align:'+cm.config[i].align+';';
34111 if(cm.isHidden(i)){
34112 hidden = 'display:none;';
34114 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
34116 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
34117 this.hdSelector, cid, " {\n", align, width, "}\n",
34118 this.tdSelector, cid, " {\n",hidden,"\n}\n",
34119 this.splitSelector, cid, " {\n", hidden , "\n}\n");
34121 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34124 updateSplitters : function(){
34125 var cm = this.cm, s = this.getSplitters();
34126 if(s){ // splitters not created yet
34127 var pos = 0, locked = true;
34128 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34129 if(cm.isHidden(i)) {
34132 var w = cm.getColumnWidth(i); // make sure it's a number
34133 if(!cm.isLocked(i) && locked){
34138 s[i].style.left = (pos-this.splitOffset) + "px";
34143 handleHiddenChange : function(colModel, colIndex, hidden){
34145 this.hideColumn(colIndex);
34147 this.unhideColumn(colIndex);
34151 hideColumn : function(colIndex){
34152 var cid = this.getColumnId(colIndex);
34153 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
34154 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
34156 this.updateHeaders();
34158 this.updateSplitters();
34162 unhideColumn : function(colIndex){
34163 var cid = this.getColumnId(colIndex);
34164 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
34165 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
34168 this.updateHeaders();
34170 this.updateSplitters();
34174 insertRows : function(dm, firstRow, lastRow, isUpdate){
34175 if(firstRow == 0 && lastRow == dm.getCount()-1){
34179 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
34181 var s = this.getScrollState();
34182 var markup = this.renderRows(firstRow, lastRow);
34183 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
34184 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
34185 this.restoreScroll(s);
34187 this.fireEvent("rowsinserted", this, firstRow, lastRow);
34188 this.syncRowHeights(firstRow, lastRow);
34189 this.stripeRows(firstRow);
34195 bufferRows : function(markup, target, index){
34196 var before = null, trows = target.rows, tbody = target.tBodies[0];
34197 if(index < trows.length){
34198 before = trows[index];
34200 var b = document.createElement("div");
34201 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34202 var rows = b.firstChild.rows;
34203 for(var i = 0, len = rows.length; i < len; i++){
34205 tbody.insertBefore(rows[0], before);
34207 tbody.appendChild(rows[0]);
34214 deleteRows : function(dm, firstRow, lastRow){
34215 if(dm.getRowCount()<1){
34216 this.fireEvent("beforerefresh", this);
34217 this.mainBody.update("");
34218 this.lockedBody.update("");
34219 this.fireEvent("refresh", this);
34221 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34222 var bt = this.getBodyTable();
34223 var tbody = bt.firstChild;
34224 var rows = bt.rows;
34225 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34226 tbody.removeChild(rows[firstRow]);
34228 this.stripeRows(firstRow);
34229 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34233 updateRows : function(dataSource, firstRow, lastRow){
34234 var s = this.getScrollState();
34236 this.restoreScroll(s);
34239 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34243 this.updateHeaderSortState();
34246 getScrollState : function(){
34248 var sb = this.scroller.dom;
34249 return {left: sb.scrollLeft, top: sb.scrollTop};
34252 stripeRows : function(startRow){
34253 if(!this.grid.stripeRows || this.ds.getCount() < 1){
34256 startRow = startRow || 0;
34257 var rows = this.getBodyTable().rows;
34258 var lrows = this.getLockedTable().rows;
34259 var cls = ' x-grid-row-alt ';
34260 for(var i = startRow, len = rows.length; i < len; i++){
34261 var row = rows[i], lrow = lrows[i];
34262 var isAlt = ((i+1) % 2 == 0);
34263 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34264 if(isAlt == hasAlt){
34268 row.className += " x-grid-row-alt";
34270 row.className = row.className.replace("x-grid-row-alt", "");
34273 lrow.className = row.className;
34278 restoreScroll : function(state){
34279 //Roo.log('GridView.restoreScroll');
34280 var sb = this.scroller.dom;
34281 sb.scrollLeft = state.left;
34282 sb.scrollTop = state.top;
34286 syncScroll : function(){
34287 //Roo.log('GridView.syncScroll');
34288 var sb = this.scroller.dom;
34289 var sh = this.mainHd.dom;
34290 var bs = this.mainBody.dom;
34291 var lv = this.lockedBody.dom;
34292 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34293 lv.scrollTop = bs.scrollTop = sb.scrollTop;
34296 handleScroll : function(e){
34298 var sb = this.scroller.dom;
34299 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34303 handleWheel : function(e){
34304 var d = e.getWheelDelta();
34305 this.scroller.dom.scrollTop -= d*22;
34306 // set this here to prevent jumpy scrolling on large tables
34307 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34311 renderRows : function(startRow, endRow){
34312 // pull in all the crap needed to render rows
34313 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34314 var colCount = cm.getColumnCount();
34316 if(ds.getCount() < 1){
34320 // build a map for all the columns
34322 for(var i = 0; i < colCount; i++){
34323 var name = cm.getDataIndex(i);
34325 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34326 renderer : cm.getRenderer(i),
34327 id : cm.getColumnId(i),
34328 locked : cm.isLocked(i),
34329 has_editor : cm.isCellEditable(i)
34333 startRow = startRow || 0;
34334 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34336 // records to render
34337 var rs = ds.getRange(startRow, endRow);
34339 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34342 // As much as I hate to duplicate code, this was branched because FireFox really hates
34343 // [].join("") on strings. The performance difference was substantial enough to
34344 // branch this function
34345 doRender : Roo.isGecko ?
34346 function(cs, rs, ds, startRow, colCount, stripe){
34347 var ts = this.templates, ct = ts.cell, rt = ts.row;
34349 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34351 var hasListener = this.grid.hasListener('rowclass');
34353 for(var j = 0, len = rs.length; j < len; j++){
34354 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34355 for(var i = 0; i < colCount; i++){
34357 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34359 p.css = p.attr = "";
34360 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34361 if(p.value == undefined || p.value === "") {
34362 p.value = " ";
34365 p.css += ' x-grid-editable-cell';
34367 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34368 p.css += ' x-grid-dirty-cell';
34370 var markup = ct.apply(p);
34378 if(stripe && ((rowIndex+1) % 2 == 0)){
34379 alt.push("x-grid-row-alt")
34382 alt.push( " x-grid-dirty-row");
34385 if(this.getRowClass){
34386 alt.push(this.getRowClass(r, rowIndex));
34392 rowIndex : rowIndex,
34395 this.grid.fireEvent('rowclass', this, rowcfg);
34396 alt.push(rowcfg.rowClass);
34398 rp.alt = alt.join(" ");
34399 lbuf+= rt.apply(rp);
34401 buf+= rt.apply(rp);
34403 return [lbuf, buf];
34405 function(cs, rs, ds, startRow, colCount, stripe){
34406 var ts = this.templates, ct = ts.cell, rt = ts.row;
34408 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34409 var hasListener = this.grid.hasListener('rowclass');
34412 for(var j = 0, len = rs.length; j < len; j++){
34413 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34414 for(var i = 0; i < colCount; i++){
34416 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34418 p.css = p.attr = "";
34419 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34420 if(p.value == undefined || p.value === "") {
34421 p.value = " ";
34425 p.css += ' x-grid-editable-cell';
34427 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34428 p.css += ' x-grid-dirty-cell'
34431 var markup = ct.apply(p);
34433 cb[cb.length] = markup;
34435 lcb[lcb.length] = markup;
34439 if(stripe && ((rowIndex+1) % 2 == 0)){
34440 alt.push( "x-grid-row-alt");
34443 alt.push(" x-grid-dirty-row");
34446 if(this.getRowClass){
34447 alt.push( this.getRowClass(r, rowIndex));
34453 rowIndex : rowIndex,
34456 this.grid.fireEvent('rowclass', this, rowcfg);
34457 alt.push(rowcfg.rowClass);
34460 rp.alt = alt.join(" ");
34461 rp.cells = lcb.join("");
34462 lbuf[lbuf.length] = rt.apply(rp);
34463 rp.cells = cb.join("");
34464 buf[buf.length] = rt.apply(rp);
34466 return [lbuf.join(""), buf.join("")];
34469 renderBody : function(){
34470 var markup = this.renderRows();
34471 var bt = this.templates.body;
34472 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34476 * Refreshes the grid
34477 * @param {Boolean} headersToo
34479 refresh : function(headersToo){
34480 this.fireEvent("beforerefresh", this);
34481 this.grid.stopEditing();
34482 var result = this.renderBody();
34483 this.lockedBody.update(result[0]);
34484 this.mainBody.update(result[1]);
34485 if(headersToo === true){
34486 this.updateHeaders();
34487 this.updateColumns();
34488 this.updateSplitters();
34489 this.updateHeaderSortState();
34491 this.syncRowHeights();
34493 this.fireEvent("refresh", this);
34496 handleColumnMove : function(cm, oldIndex, newIndex){
34497 this.indexMap = null;
34498 var s = this.getScrollState();
34499 this.refresh(true);
34500 this.restoreScroll(s);
34501 this.afterMove(newIndex);
34504 afterMove : function(colIndex){
34505 if(this.enableMoveAnim && Roo.enableFx){
34506 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34508 // if multisort - fix sortOrder, and reload..
34509 if (this.grid.dataSource.multiSort) {
34510 // the we can call sort again..
34511 var dm = this.grid.dataSource;
34512 var cm = this.grid.colModel;
34514 for(var i = 0; i < cm.config.length; i++ ) {
34516 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34517 continue; // dont' bother, it's not in sort list or being set.
34520 so.push(cm.config[i].dataIndex);
34523 dm.load(dm.lastOptions);
34530 updateCell : function(dm, rowIndex, dataIndex){
34531 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34532 if(typeof colIndex == "undefined"){ // not present in grid
34535 var cm = this.grid.colModel;
34536 var cell = this.getCell(rowIndex, colIndex);
34537 var cellText = this.getCellText(rowIndex, colIndex);
34540 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34541 id : cm.getColumnId(colIndex),
34542 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34544 var renderer = cm.getRenderer(colIndex);
34545 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34546 if(typeof val == "undefined" || val === "") {
34549 cellText.innerHTML = val;
34550 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34551 this.syncRowHeights(rowIndex, rowIndex);
34554 calcColumnWidth : function(colIndex, maxRowsToMeasure){
34556 if(this.grid.autoSizeHeaders){
34557 var h = this.getHeaderCellMeasure(colIndex);
34558 maxWidth = Math.max(maxWidth, h.scrollWidth);
34561 if(this.cm.isLocked(colIndex)){
34562 tb = this.getLockedTable();
34565 tb = this.getBodyTable();
34566 index = colIndex - this.cm.getLockedCount();
34569 var rows = tb.rows;
34570 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34571 for(var i = 0; i < stopIndex; i++){
34572 var cell = rows[i].childNodes[index].firstChild;
34573 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34576 return maxWidth + /*margin for error in IE*/ 5;
34579 * Autofit a column to its content.
34580 * @param {Number} colIndex
34581 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34583 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34584 if(this.cm.isHidden(colIndex)){
34585 return; // can't calc a hidden column
34588 var cid = this.cm.getColumnId(colIndex);
34589 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34590 if(this.grid.autoSizeHeaders){
34591 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34594 var newWidth = this.calcColumnWidth(colIndex);
34595 this.cm.setColumnWidth(colIndex,
34596 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34597 if(!suppressEvent){
34598 this.grid.fireEvent("columnresize", colIndex, newWidth);
34603 * Autofits all columns to their content and then expands to fit any extra space in the grid
34605 autoSizeColumns : function(){
34606 var cm = this.grid.colModel;
34607 var colCount = cm.getColumnCount();
34608 for(var i = 0; i < colCount; i++){
34609 this.autoSizeColumn(i, true, true);
34611 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34614 this.updateColumns();
34620 * Autofits all columns to the grid's width proportionate with their current size
34621 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34623 fitColumns : function(reserveScrollSpace){
34624 var cm = this.grid.colModel;
34625 var colCount = cm.getColumnCount();
34629 for (i = 0; i < colCount; i++){
34630 if(!cm.isHidden(i) && !cm.isFixed(i)){
34631 w = cm.getColumnWidth(i);
34637 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34638 if(reserveScrollSpace){
34641 var frac = (avail - cm.getTotalWidth())/width;
34642 while (cols.length){
34645 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34647 this.updateColumns();
34651 onRowSelect : function(rowIndex){
34652 var row = this.getRowComposite(rowIndex);
34653 row.addClass("x-grid-row-selected");
34656 onRowDeselect : function(rowIndex){
34657 var row = this.getRowComposite(rowIndex);
34658 row.removeClass("x-grid-row-selected");
34661 onCellSelect : function(row, col){
34662 var cell = this.getCell(row, col);
34664 Roo.fly(cell).addClass("x-grid-cell-selected");
34668 onCellDeselect : function(row, col){
34669 var cell = this.getCell(row, col);
34671 Roo.fly(cell).removeClass("x-grid-cell-selected");
34675 updateHeaderSortState : function(){
34677 // sort state can be single { field: xxx, direction : yyy}
34678 // or { xxx=>ASC , yyy : DESC ..... }
34681 if (!this.ds.multiSort) {
34682 var state = this.ds.getSortState();
34686 mstate[state.field] = state.direction;
34687 // FIXME... - this is not used here.. but might be elsewhere..
34688 this.sortState = state;
34691 mstate = this.ds.sortToggle;
34693 //remove existing sort classes..
34695 var sc = this.sortClasses;
34696 var hds = this.el.select(this.headerSelector).removeClass(sc);
34698 for(var f in mstate) {
34700 var sortColumn = this.cm.findColumnIndex(f);
34702 if(sortColumn != -1){
34703 var sortDir = mstate[f];
34704 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34713 handleHeaderClick : function(g, index,e){
34715 Roo.log("header click");
34718 // touch events on header are handled by context
34719 this.handleHdCtx(g,index,e);
34724 if(this.headersDisabled){
34727 var dm = g.dataSource, cm = g.colModel;
34728 if(!cm.isSortable(index)){
34733 if (dm.multiSort) {
34734 // update the sortOrder
34736 for(var i = 0; i < cm.config.length; i++ ) {
34738 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34739 continue; // dont' bother, it's not in sort list or being set.
34742 so.push(cm.config[i].dataIndex);
34748 dm.sort(cm.getDataIndex(index));
34752 destroy : function(){
34754 this.colMenu.removeAll();
34755 Roo.menu.MenuMgr.unregister(this.colMenu);
34756 this.colMenu.getEl().remove();
34757 delete this.colMenu;
34760 this.hmenu.removeAll();
34761 Roo.menu.MenuMgr.unregister(this.hmenu);
34762 this.hmenu.getEl().remove();
34765 if(this.grid.enableColumnMove){
34766 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34768 for(var dd in dds){
34769 if(!dds[dd].config.isTarget && dds[dd].dragElId){
34770 var elid = dds[dd].dragElId;
34772 Roo.get(elid).remove();
34773 } else if(dds[dd].config.isTarget){
34774 dds[dd].proxyTop.remove();
34775 dds[dd].proxyBottom.remove();
34778 if(Roo.dd.DDM.locationCache[dd]){
34779 delete Roo.dd.DDM.locationCache[dd];
34782 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34785 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34786 this.bind(null, null);
34787 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34790 handleLockChange : function(){
34791 this.refresh(true);
34794 onDenyColumnLock : function(){
34798 onDenyColumnHide : function(){
34802 handleHdMenuClick : function(item){
34803 var index = this.hdCtxIndex;
34804 var cm = this.cm, ds = this.ds;
34807 ds.sort(cm.getDataIndex(index), "ASC");
34810 ds.sort(cm.getDataIndex(index), "DESC");
34813 var lc = cm.getLockedCount();
34814 if(cm.getColumnCount(true) <= lc+1){
34815 this.onDenyColumnLock();
34819 cm.setLocked(index, true, true);
34820 cm.moveColumn(index, lc);
34821 this.grid.fireEvent("columnmove", index, lc);
34823 cm.setLocked(index, true);
34827 var lc = cm.getLockedCount();
34828 if((lc-1) != index){
34829 cm.setLocked(index, false, true);
34830 cm.moveColumn(index, lc-1);
34831 this.grid.fireEvent("columnmove", index, lc-1);
34833 cm.setLocked(index, false);
34836 case 'wider': // used to expand cols on touch..
34838 var cw = cm.getColumnWidth(index);
34839 cw += (item.id == 'wider' ? 1 : -1) * 50;
34840 cw = Math.max(0, cw);
34841 cw = Math.min(cw,4000);
34842 cm.setColumnWidth(index, cw);
34846 index = cm.getIndexById(item.id.substr(4));
34848 if(item.checked && cm.getColumnCount(true) <= 1){
34849 this.onDenyColumnHide();
34852 cm.setHidden(index, item.checked);
34858 beforeColMenuShow : function(){
34859 var cm = this.cm, colCount = cm.getColumnCount();
34860 this.colMenu.removeAll();
34861 for(var i = 0; i < colCount; i++){
34862 this.colMenu.add(new Roo.menu.CheckItem({
34863 id: "col-"+cm.getColumnId(i),
34864 text: cm.getColumnHeader(i),
34865 checked: !cm.isHidden(i),
34871 handleHdCtx : function(g, index, e){
34873 var hd = this.getHeaderCell(index);
34874 this.hdCtxIndex = index;
34875 var ms = this.hmenu.items, cm = this.cm;
34876 ms.get("asc").setDisabled(!cm.isSortable(index));
34877 ms.get("desc").setDisabled(!cm.isSortable(index));
34878 if(this.grid.enableColLock !== false){
34879 ms.get("lock").setDisabled(cm.isLocked(index));
34880 ms.get("unlock").setDisabled(!cm.isLocked(index));
34882 this.hmenu.show(hd, "tl-bl");
34885 handleHdOver : function(e){
34886 var hd = this.findHeaderCell(e.getTarget());
34887 if(hd && !this.headersDisabled){
34888 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34889 this.fly(hd).addClass("x-grid-hd-over");
34894 handleHdOut : function(e){
34895 var hd = this.findHeaderCell(e.getTarget());
34897 this.fly(hd).removeClass("x-grid-hd-over");
34901 handleSplitDblClick : function(e, t){
34902 var i = this.getCellIndex(t);
34903 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34904 this.autoSizeColumn(i, true);
34909 render : function(){
34912 var colCount = cm.getColumnCount();
34914 if(this.grid.monitorWindowResize === true){
34915 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34917 var header = this.renderHeaders();
34918 var body = this.templates.body.apply({rows:""});
34919 var html = this.templates.master.apply({
34922 lockedHeader: header[0],
34926 //this.updateColumns();
34928 this.grid.getGridEl().dom.innerHTML = html;
34930 this.initElements();
34932 // a kludge to fix the random scolling effect in webkit
34933 this.el.on("scroll", function() {
34934 this.el.dom.scrollTop=0; // hopefully not recursive..
34937 this.scroller.on("scroll", this.handleScroll, this);
34938 this.lockedBody.on("mousewheel", this.handleWheel, this);
34939 this.mainBody.on("mousewheel", this.handleWheel, this);
34941 this.mainHd.on("mouseover", this.handleHdOver, this);
34942 this.mainHd.on("mouseout", this.handleHdOut, this);
34943 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34944 {delegate: "."+this.splitClass});
34946 this.lockedHd.on("mouseover", this.handleHdOver, this);
34947 this.lockedHd.on("mouseout", this.handleHdOut, this);
34948 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34949 {delegate: "."+this.splitClass});
34951 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34952 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34955 this.updateSplitters();
34957 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34958 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34959 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34962 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34963 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34965 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34966 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34968 if(this.grid.enableColLock !== false){
34969 this.hmenu.add('-',
34970 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34971 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34975 this.hmenu.add('-',
34976 {id:"wider", text: this.columnsWiderText},
34977 {id:"narrow", text: this.columnsNarrowText }
34983 if(this.grid.enableColumnHide !== false){
34985 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34986 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34987 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34989 this.hmenu.add('-',
34990 {id:"columns", text: this.columnsText, menu: this.colMenu}
34993 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34995 this.grid.on("headercontextmenu", this.handleHdCtx, this);
34998 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34999 this.dd = new Roo.grid.GridDragZone(this.grid, {
35000 ddGroup : this.grid.ddGroup || 'GridDD'
35006 for(var i = 0; i < colCount; i++){
35007 if(cm.isHidden(i)){
35008 this.hideColumn(i);
35010 if(cm.config[i].align){
35011 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
35012 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
35016 this.updateHeaderSortState();
35018 this.beforeInitialResize();
35021 // two part rendering gives faster view to the user
35022 this.renderPhase2.defer(1, this);
35025 renderPhase2 : function(){
35026 // render the rows now
35028 if(this.grid.autoSizeColumns){
35029 this.autoSizeColumns();
35033 beforeInitialResize : function(){
35037 onColumnSplitterMoved : function(i, w){
35038 this.userResized = true;
35039 var cm = this.grid.colModel;
35040 cm.setColumnWidth(i, w, true);
35041 var cid = cm.getColumnId(i);
35042 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35043 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35044 this.updateSplitters();
35046 this.grid.fireEvent("columnresize", i, w);
35049 syncRowHeights : function(startIndex, endIndex){
35050 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
35051 startIndex = startIndex || 0;
35052 var mrows = this.getBodyTable().rows;
35053 var lrows = this.getLockedTable().rows;
35054 var len = mrows.length-1;
35055 endIndex = Math.min(endIndex || len, len);
35056 for(var i = startIndex; i <= endIndex; i++){
35057 var m = mrows[i], l = lrows[i];
35058 var h = Math.max(m.offsetHeight, l.offsetHeight);
35059 m.style.height = l.style.height = h + "px";
35064 layout : function(initialRender, is2ndPass){
35066 var auto = g.autoHeight;
35067 var scrollOffset = 16;
35068 var c = g.getGridEl(), cm = this.cm,
35069 expandCol = g.autoExpandColumn,
35071 //c.beginMeasure();
35073 if(!c.dom.offsetWidth){ // display:none?
35075 this.lockedWrap.show();
35076 this.mainWrap.show();
35081 var hasLock = this.cm.isLocked(0);
35083 var tbh = this.headerPanel.getHeight();
35084 var bbh = this.footerPanel.getHeight();
35087 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
35088 var newHeight = ch + c.getBorderWidth("tb");
35090 newHeight = Math.min(g.maxHeight, newHeight);
35092 c.setHeight(newHeight);
35096 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
35099 var s = this.scroller;
35101 var csize = c.getSize(true);
35103 this.el.setSize(csize.width, csize.height);
35105 this.headerPanel.setWidth(csize.width);
35106 this.footerPanel.setWidth(csize.width);
35108 var hdHeight = this.mainHd.getHeight();
35109 var vw = csize.width;
35110 var vh = csize.height - (tbh + bbh);
35114 var bt = this.getBodyTable();
35116 if(cm.getLockedCount() == cm.config.length){
35117 bt = this.getLockedTable();
35120 var ltWidth = hasLock ?
35121 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
35123 var scrollHeight = bt.offsetHeight;
35124 var scrollWidth = ltWidth + bt.offsetWidth;
35125 var vscroll = false, hscroll = false;
35127 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
35129 var lw = this.lockedWrap, mw = this.mainWrap;
35130 var lb = this.lockedBody, mb = this.mainBody;
35132 setTimeout(function(){
35133 var t = s.dom.offsetTop;
35134 var w = s.dom.clientWidth,
35135 h = s.dom.clientHeight;
35138 lw.setSize(ltWidth, h);
35140 mw.setLeftTop(ltWidth, t);
35141 mw.setSize(w-ltWidth, h);
35143 lb.setHeight(h-hdHeight);
35144 mb.setHeight(h-hdHeight);
35146 if(is2ndPass !== true && !gv.userResized && expandCol){
35147 // high speed resize without full column calculation
35149 var ci = cm.getIndexById(expandCol);
35151 ci = cm.findColumnIndex(expandCol);
35153 ci = Math.max(0, ci); // make sure it's got at least the first col.
35154 var expandId = cm.getColumnId(ci);
35155 var tw = cm.getTotalWidth(false);
35156 var currentWidth = cm.getColumnWidth(ci);
35157 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
35158 if(currentWidth != cw){
35159 cm.setColumnWidth(ci, cw, true);
35160 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35161 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35162 gv.updateSplitters();
35163 gv.layout(false, true);
35175 onWindowResize : function(){
35176 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
35182 appendFooter : function(parentEl){
35186 sortAscText : "Sort Ascending",
35187 sortDescText : "Sort Descending",
35188 lockText : "Lock Column",
35189 unlockText : "Unlock Column",
35190 columnsText : "Columns",
35192 columnsWiderText : "Wider",
35193 columnsNarrowText : "Thinner"
35197 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35198 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35199 this.proxy.el.addClass('x-grid3-col-dd');
35202 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35203 handleMouseDown : function(e){
35207 callHandleMouseDown : function(e){
35208 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35213 * Ext JS Library 1.1.1
35214 * Copyright(c) 2006-2007, Ext JS, LLC.
35216 * Originally Released Under LGPL - original licence link has changed is not relivant.
35219 * <script type="text/javascript">
35223 // This is a support class used internally by the Grid components
35224 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35226 this.view = grid.getView();
35227 this.proxy = this.view.resizeProxy;
35228 Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
35229 "gridSplitters" + this.grid.getGridEl().id, {
35230 dragElId : Roo.id(this.proxy.dom), resizeFrame:false
35232 this.setHandleElId(Roo.id(hd));
35233 this.setOuterHandleElId(Roo.id(hd2));
35234 this.scroll = false;
35236 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35237 fly: Roo.Element.fly,
35239 b4StartDrag : function(x, y){
35240 this.view.headersDisabled = true;
35241 this.proxy.setHeight(this.view.mainWrap.getHeight());
35242 var w = this.cm.getColumnWidth(this.cellIndex);
35243 var minw = Math.max(w-this.grid.minColumnWidth, 0);
35244 this.resetConstraints();
35245 this.setXConstraint(minw, 1000);
35246 this.setYConstraint(0, 0);
35247 this.minX = x - minw;
35248 this.maxX = x + 1000;
35250 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35254 handleMouseDown : function(e){
35255 ev = Roo.EventObject.setEvent(e);
35256 var t = this.fly(ev.getTarget());
35257 if(t.hasClass("x-grid-split")){
35258 this.cellIndex = this.view.getCellIndex(t.dom);
35259 this.split = t.dom;
35260 this.cm = this.grid.colModel;
35261 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35262 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35267 endDrag : function(e){
35268 this.view.headersDisabled = false;
35269 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35270 var diff = endX - this.startPos;
35271 this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
35274 autoOffset : function(){
35275 this.setDelta(0,0);
35279 * Ext JS Library 1.1.1
35280 * Copyright(c) 2006-2007, Ext JS, LLC.
35282 * Originally Released Under LGPL - original licence link has changed is not relivant.
35285 * <script type="text/javascript">
35289 // This is a support class used internally by the Grid components
35290 Roo.grid.GridDragZone = function(grid, config){
35291 this.view = grid.getView();
35292 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35293 if(this.view.lockedBody){
35294 this.setHandleElId(Roo.id(this.view.mainBody.dom));
35295 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35297 this.scroll = false;
35299 this.ddel = document.createElement('div');
35300 this.ddel.className = 'x-grid-dd-wrap';
35303 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35304 ddGroup : "GridDD",
35306 getDragData : function(e){
35307 var t = Roo.lib.Event.getTarget(e);
35308 var rowIndex = this.view.findRowIndex(t);
35309 var sm = this.grid.selModel;
35311 //Roo.log(rowIndex);
35313 if (sm.getSelectedCell) {
35314 // cell selection..
35315 if (!sm.getSelectedCell()) {
35318 if (rowIndex != sm.getSelectedCell()[0]) {
35324 if(rowIndex !== false){
35329 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35331 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35334 if (e.hasModifier()){
35335 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35338 Roo.log("getDragData");
35343 rowIndex: rowIndex,
35344 selections:sm.getSelections ? sm.getSelections() : (
35345 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
35352 onInitDrag : function(e){
35353 var data = this.dragData;
35354 this.ddel.innerHTML = this.grid.getDragDropText();
35355 this.proxy.update(this.ddel);
35356 // fire start drag?
35359 afterRepair : function(){
35360 this.dragging = false;
35363 getRepairXY : function(e, data){
35367 onEndDrag : function(data, e){
35371 onValidDrop : function(dd, e, id){
35376 beforeInvalidDrop : function(e, id){
35381 * Ext JS Library 1.1.1
35382 * Copyright(c) 2006-2007, Ext JS, LLC.
35384 * Originally Released Under LGPL - original licence link has changed is not relivant.
35387 * <script type="text/javascript">
35392 * @class Roo.grid.ColumnModel
35393 * @extends Roo.util.Observable
35394 * This is the default implementation of a ColumnModel used by the Grid. It defines
35395 * the columns in the grid.
35398 var colModel = new Roo.grid.ColumnModel([
35399 {header: "Ticker", width: 60, sortable: true, locked: true},
35400 {header: "Company Name", width: 150, sortable: true},
35401 {header: "Market Cap.", width: 100, sortable: true},
35402 {header: "$ Sales", width: 100, sortable: true, renderer: money},
35403 {header: "Employees", width: 100, sortable: true, resizable: false}
35408 * The config options listed for this class are options which may appear in each
35409 * individual column definition.
35410 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35412 * @param {Object} config An Array of column config objects. See this class's
35413 * config objects for details.
35415 Roo.grid.ColumnModel = function(config){
35417 * The config passed into the constructor
35419 this.config = config;
35422 // if no id, create one
35423 // if the column does not have a dataIndex mapping,
35424 // map it to the order it is in the config
35425 for(var i = 0, len = config.length; i < len; i++){
35427 if(typeof c.dataIndex == "undefined"){
35430 if(typeof c.renderer == "string"){
35431 c.renderer = Roo.util.Format[c.renderer];
35433 if(typeof c.id == "undefined"){
35436 if(c.editor && c.editor.xtype){
35437 c.editor = Roo.factory(c.editor, Roo.grid);
35439 if(c.editor && c.editor.isFormField){
35440 c.editor = new Roo.grid.GridEditor(c.editor);
35442 this.lookup[c.id] = c;
35446 * The width of columns which have no width specified (defaults to 100)
35449 this.defaultWidth = 100;
35452 * Default sortable of columns which have no sortable specified (defaults to false)
35455 this.defaultSortable = false;
35459 * @event widthchange
35460 * Fires when the width of a column changes.
35461 * @param {ColumnModel} this
35462 * @param {Number} columnIndex The column index
35463 * @param {Number} newWidth The new width
35465 "widthchange": true,
35467 * @event headerchange
35468 * Fires when the text of a header changes.
35469 * @param {ColumnModel} this
35470 * @param {Number} columnIndex The column index
35471 * @param {Number} newText The new header text
35473 "headerchange": true,
35475 * @event hiddenchange
35476 * Fires when a column is hidden or "unhidden".
35477 * @param {ColumnModel} this
35478 * @param {Number} columnIndex The column index
35479 * @param {Boolean} hidden true if hidden, false otherwise
35481 "hiddenchange": true,
35483 * @event columnmoved
35484 * Fires when a column is moved.
35485 * @param {ColumnModel} this
35486 * @param {Number} oldIndex
35487 * @param {Number} newIndex
35489 "columnmoved" : true,
35491 * @event columlockchange
35492 * Fires when a column's locked state is changed
35493 * @param {ColumnModel} this
35494 * @param {Number} colIndex
35495 * @param {Boolean} locked true if locked
35497 "columnlockchange" : true
35499 Roo.grid.ColumnModel.superclass.constructor.call(this);
35501 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35503 * @cfg {String} header The header text to display in the Grid view.
35506 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35507 * {@link Roo.data.Record} definition from which to draw the column's value. If not
35508 * specified, the column's index is used as an index into the Record's data Array.
35511 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35512 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35515 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35516 * Defaults to the value of the {@link #defaultSortable} property.
35517 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35520 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
35523 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
35526 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35529 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35532 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35533 * given the cell's data value. See {@link #setRenderer}. If not specified, the
35534 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35535 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35538 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
35541 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
35544 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
35547 * @cfg {String} cursor (Optional)
35550 * @cfg {String} tooltip (Optional)
35553 * @cfg {Number} xs (Optional)
35556 * @cfg {Number} sm (Optional)
35559 * @cfg {Number} md (Optional)
35562 * @cfg {Number} lg (Optional)
35565 * Returns the id of the column at the specified index.
35566 * @param {Number} index The column index
35567 * @return {String} the id
35569 getColumnId : function(index){
35570 return this.config[index].id;
35574 * Returns the column for a specified id.
35575 * @param {String} id The column id
35576 * @return {Object} the column
35578 getColumnById : function(id){
35579 return this.lookup[id];
35584 * Returns the column for a specified dataIndex.
35585 * @param {String} dataIndex The column dataIndex
35586 * @return {Object|Boolean} the column or false if not found
35588 getColumnByDataIndex: function(dataIndex){
35589 var index = this.findColumnIndex(dataIndex);
35590 return index > -1 ? this.config[index] : false;
35594 * Returns the index for a specified column id.
35595 * @param {String} id The column id
35596 * @return {Number} the index, or -1 if not found
35598 getIndexById : function(id){
35599 for(var i = 0, len = this.config.length; i < len; i++){
35600 if(this.config[i].id == id){
35608 * Returns the index for a specified column dataIndex.
35609 * @param {String} dataIndex The column dataIndex
35610 * @return {Number} the index, or -1 if not found
35613 findColumnIndex : function(dataIndex){
35614 for(var i = 0, len = this.config.length; i < len; i++){
35615 if(this.config[i].dataIndex == dataIndex){
35623 moveColumn : function(oldIndex, newIndex){
35624 var c = this.config[oldIndex];
35625 this.config.splice(oldIndex, 1);
35626 this.config.splice(newIndex, 0, c);
35627 this.dataMap = null;
35628 this.fireEvent("columnmoved", this, oldIndex, newIndex);
35631 isLocked : function(colIndex){
35632 return this.config[colIndex].locked === true;
35635 setLocked : function(colIndex, value, suppressEvent){
35636 if(this.isLocked(colIndex) == value){
35639 this.config[colIndex].locked = value;
35640 if(!suppressEvent){
35641 this.fireEvent("columnlockchange", this, colIndex, value);
35645 getTotalLockedWidth : function(){
35646 var totalWidth = 0;
35647 for(var i = 0; i < this.config.length; i++){
35648 if(this.isLocked(i) && !this.isHidden(i)){
35649 this.totalWidth += this.getColumnWidth(i);
35655 getLockedCount : function(){
35656 for(var i = 0, len = this.config.length; i < len; i++){
35657 if(!this.isLocked(i)){
35662 return this.config.length;
35666 * Returns the number of columns.
35669 getColumnCount : function(visibleOnly){
35670 if(visibleOnly === true){
35672 for(var i = 0, len = this.config.length; i < len; i++){
35673 if(!this.isHidden(i)){
35679 return this.config.length;
35683 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35684 * @param {Function} fn
35685 * @param {Object} scope (optional)
35686 * @return {Array} result
35688 getColumnsBy : function(fn, scope){
35690 for(var i = 0, len = this.config.length; i < len; i++){
35691 var c = this.config[i];
35692 if(fn.call(scope||this, c, i) === true){
35700 * Returns true if the specified column is sortable.
35701 * @param {Number} col The column index
35702 * @return {Boolean}
35704 isSortable : function(col){
35705 if(typeof this.config[col].sortable == "undefined"){
35706 return this.defaultSortable;
35708 return this.config[col].sortable;
35712 * Returns the rendering (formatting) function defined for the column.
35713 * @param {Number} col The column index.
35714 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35716 getRenderer : function(col){
35717 if(!this.config[col].renderer){
35718 return Roo.grid.ColumnModel.defaultRenderer;
35720 return this.config[col].renderer;
35724 * Sets the rendering (formatting) function for a column.
35725 * @param {Number} col The column index
35726 * @param {Function} fn The function to use to process the cell's raw data
35727 * to return HTML markup for the grid view. The render function is called with
35728 * the following parameters:<ul>
35729 * <li>Data value.</li>
35730 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35731 * <li>css A CSS style string to apply to the table cell.</li>
35732 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35733 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35734 * <li>Row index</li>
35735 * <li>Column index</li>
35736 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35738 setRenderer : function(col, fn){
35739 this.config[col].renderer = fn;
35743 * Returns the width for the specified column.
35744 * @param {Number} col The column index
35747 getColumnWidth : function(col){
35748 return this.config[col].width * 1 || this.defaultWidth;
35752 * Sets the width for a column.
35753 * @param {Number} col The column index
35754 * @param {Number} width The new width
35756 setColumnWidth : function(col, width, suppressEvent){
35757 this.config[col].width = width;
35758 this.totalWidth = null;
35759 if(!suppressEvent){
35760 this.fireEvent("widthchange", this, col, width);
35765 * Returns the total width of all columns.
35766 * @param {Boolean} includeHidden True to include hidden column widths
35769 getTotalWidth : function(includeHidden){
35770 if(!this.totalWidth){
35771 this.totalWidth = 0;
35772 for(var i = 0, len = this.config.length; i < len; i++){
35773 if(includeHidden || !this.isHidden(i)){
35774 this.totalWidth += this.getColumnWidth(i);
35778 return this.totalWidth;
35782 * Returns the header for the specified column.
35783 * @param {Number} col The column index
35786 getColumnHeader : function(col){
35787 return this.config[col].header;
35791 * Sets the header for a column.
35792 * @param {Number} col The column index
35793 * @param {String} header The new header
35795 setColumnHeader : function(col, header){
35796 this.config[col].header = header;
35797 this.fireEvent("headerchange", this, col, header);
35801 * Returns the tooltip for the specified column.
35802 * @param {Number} col The column index
35805 getColumnTooltip : function(col){
35806 return this.config[col].tooltip;
35809 * Sets the tooltip for a column.
35810 * @param {Number} col The column index
35811 * @param {String} tooltip The new tooltip
35813 setColumnTooltip : function(col, tooltip){
35814 this.config[col].tooltip = tooltip;
35818 * Returns the dataIndex for the specified column.
35819 * @param {Number} col The column index
35822 getDataIndex : function(col){
35823 return this.config[col].dataIndex;
35827 * Sets the dataIndex for a column.
35828 * @param {Number} col The column index
35829 * @param {Number} dataIndex The new dataIndex
35831 setDataIndex : function(col, dataIndex){
35832 this.config[col].dataIndex = dataIndex;
35838 * Returns true if the cell is editable.
35839 * @param {Number} colIndex The column index
35840 * @param {Number} rowIndex The row index - this is nto actually used..?
35841 * @return {Boolean}
35843 isCellEditable : function(colIndex, rowIndex){
35844 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35848 * Returns the editor defined for the cell/column.
35849 * return false or null to disable editing.
35850 * @param {Number} colIndex The column index
35851 * @param {Number} rowIndex The row index
35854 getCellEditor : function(colIndex, rowIndex){
35855 return this.config[colIndex].editor;
35859 * Sets if a column is editable.
35860 * @param {Number} col The column index
35861 * @param {Boolean} editable True if the column is editable
35863 setEditable : function(col, editable){
35864 this.config[col].editable = editable;
35869 * Returns true if the column is hidden.
35870 * @param {Number} colIndex The column index
35871 * @return {Boolean}
35873 isHidden : function(colIndex){
35874 return this.config[colIndex].hidden;
35879 * Returns true if the column width cannot be changed
35881 isFixed : function(colIndex){
35882 return this.config[colIndex].fixed;
35886 * Returns true if the column can be resized
35887 * @return {Boolean}
35889 isResizable : function(colIndex){
35890 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35893 * Sets if a column is hidden.
35894 * @param {Number} colIndex The column index
35895 * @param {Boolean} hidden True if the column is hidden
35897 setHidden : function(colIndex, hidden){
35898 this.config[colIndex].hidden = hidden;
35899 this.totalWidth = null;
35900 this.fireEvent("hiddenchange", this, colIndex, hidden);
35904 * Sets the editor for a column.
35905 * @param {Number} col The column index
35906 * @param {Object} editor The editor object
35908 setEditor : function(col, editor){
35909 this.config[col].editor = editor;
35913 Roo.grid.ColumnModel.defaultRenderer = function(value)
35915 if(typeof value == "object") {
35918 if(typeof value == "string" && value.length < 1){
35922 return String.format("{0}", value);
35925 // Alias for backwards compatibility
35926 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35929 * Ext JS Library 1.1.1
35930 * Copyright(c) 2006-2007, Ext JS, LLC.
35932 * Originally Released Under LGPL - original licence link has changed is not relivant.
35935 * <script type="text/javascript">
35939 * @class Roo.grid.AbstractSelectionModel
35940 * @extends Roo.util.Observable
35941 * Abstract base class for grid SelectionModels. It provides the interface that should be
35942 * implemented by descendant classes. This class should not be directly instantiated.
35945 Roo.grid.AbstractSelectionModel = function(){
35946 this.locked = false;
35947 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35950 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
35951 /** @ignore Called by the grid automatically. Do not call directly. */
35952 init : function(grid){
35958 * Locks the selections.
35961 this.locked = true;
35965 * Unlocks the selections.
35967 unlock : function(){
35968 this.locked = false;
35972 * Returns true if the selections are locked.
35973 * @return {Boolean}
35975 isLocked : function(){
35976 return this.locked;
35980 * Ext JS Library 1.1.1
35981 * Copyright(c) 2006-2007, Ext JS, LLC.
35983 * Originally Released Under LGPL - original licence link has changed is not relivant.
35986 * <script type="text/javascript">
35989 * @extends Roo.grid.AbstractSelectionModel
35990 * @class Roo.grid.RowSelectionModel
35991 * The default SelectionModel used by {@link Roo.grid.Grid}.
35992 * It supports multiple selections and keyboard selection/navigation.
35994 * @param {Object} config
35996 Roo.grid.RowSelectionModel = function(config){
35997 Roo.apply(this, config);
35998 this.selections = new Roo.util.MixedCollection(false, function(o){
36003 this.lastActive = false;
36007 * @event selectionchange
36008 * Fires when the selection changes
36009 * @param {SelectionModel} this
36011 "selectionchange" : true,
36013 * @event afterselectionchange
36014 * Fires after the selection changes (eg. by key press or clicking)
36015 * @param {SelectionModel} this
36017 "afterselectionchange" : true,
36019 * @event beforerowselect
36020 * Fires when a row is selected being selected, return false to cancel.
36021 * @param {SelectionModel} this
36022 * @param {Number} rowIndex The selected index
36023 * @param {Boolean} keepExisting False if other selections will be cleared
36025 "beforerowselect" : true,
36028 * Fires when a row is selected.
36029 * @param {SelectionModel} this
36030 * @param {Number} rowIndex The selected index
36031 * @param {Roo.data.Record} r The record
36033 "rowselect" : true,
36035 * @event rowdeselect
36036 * Fires when a row is deselected.
36037 * @param {SelectionModel} this
36038 * @param {Number} rowIndex The selected index
36040 "rowdeselect" : true
36042 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36043 this.locked = false;
36046 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
36048 * @cfg {Boolean} singleSelect
36049 * True to allow selection of only one row at a time (defaults to false)
36051 singleSelect : false,
36054 initEvents : function(){
36056 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36057 this.grid.on("mousedown", this.handleMouseDown, this);
36058 }else{ // allow click to work like normal
36059 this.grid.on("rowclick", this.handleDragableRowClick, this);
36062 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36063 "up" : function(e){
36065 this.selectPrevious(e.shiftKey);
36066 }else if(this.last !== false && this.lastActive !== false){
36067 var last = this.last;
36068 this.selectRange(this.last, this.lastActive-1);
36069 this.grid.getView().focusRow(this.lastActive);
36070 if(last !== false){
36074 this.selectFirstRow();
36076 this.fireEvent("afterselectionchange", this);
36078 "down" : function(e){
36080 this.selectNext(e.shiftKey);
36081 }else if(this.last !== false && this.lastActive !== false){
36082 var last = this.last;
36083 this.selectRange(this.last, this.lastActive+1);
36084 this.grid.getView().focusRow(this.lastActive);
36085 if(last !== false){
36089 this.selectFirstRow();
36091 this.fireEvent("afterselectionchange", this);
36096 var view = this.grid.view;
36097 view.on("refresh", this.onRefresh, this);
36098 view.on("rowupdated", this.onRowUpdated, this);
36099 view.on("rowremoved", this.onRemove, this);
36103 onRefresh : function(){
36104 var ds = this.grid.dataSource, i, v = this.grid.view;
36105 var s = this.selections;
36106 s.each(function(r){
36107 if((i = ds.indexOfId(r.id)) != -1){
36109 s.add(ds.getAt(i)); // updating the selection relate data
36117 onRemove : function(v, index, r){
36118 this.selections.remove(r);
36122 onRowUpdated : function(v, index, r){
36123 if(this.isSelected(r)){
36124 v.onRowSelect(index);
36130 * @param {Array} records The records to select
36131 * @param {Boolean} keepExisting (optional) True to keep existing selections
36133 selectRecords : function(records, keepExisting){
36135 this.clearSelections();
36137 var ds = this.grid.dataSource;
36138 for(var i = 0, len = records.length; i < len; i++){
36139 this.selectRow(ds.indexOf(records[i]), true);
36144 * Gets the number of selected rows.
36147 getCount : function(){
36148 return this.selections.length;
36152 * Selects the first row in the grid.
36154 selectFirstRow : function(){
36159 * Select the last row.
36160 * @param {Boolean} keepExisting (optional) True to keep existing selections
36162 selectLastRow : function(keepExisting){
36163 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
36167 * Selects the row immediately following the last selected row.
36168 * @param {Boolean} keepExisting (optional) True to keep existing selections
36170 selectNext : function(keepExisting){
36171 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
36172 this.selectRow(this.last+1, keepExisting);
36173 this.grid.getView().focusRow(this.last);
36178 * Selects the row that precedes the last selected row.
36179 * @param {Boolean} keepExisting (optional) True to keep existing selections
36181 selectPrevious : function(keepExisting){
36183 this.selectRow(this.last-1, keepExisting);
36184 this.grid.getView().focusRow(this.last);
36189 * Returns the selected records
36190 * @return {Array} Array of selected records
36192 getSelections : function(){
36193 return [].concat(this.selections.items);
36197 * Returns the first selected record.
36200 getSelected : function(){
36201 return this.selections.itemAt(0);
36206 * Clears all selections.
36208 clearSelections : function(fast){
36213 var ds = this.grid.dataSource;
36214 var s = this.selections;
36215 s.each(function(r){
36216 this.deselectRow(ds.indexOfId(r.id));
36220 this.selections.clear();
36227 * Selects all rows.
36229 selectAll : function(){
36233 this.selections.clear();
36234 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
36235 this.selectRow(i, true);
36240 * Returns True if there is a selection.
36241 * @return {Boolean}
36243 hasSelection : function(){
36244 return this.selections.length > 0;
36248 * Returns True if the specified row is selected.
36249 * @param {Number/Record} record The record or index of the record to check
36250 * @return {Boolean}
36252 isSelected : function(index){
36253 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
36254 return (r && this.selections.key(r.id) ? true : false);
36258 * Returns True if the specified record id is selected.
36259 * @param {String} id The id of record to check
36260 * @return {Boolean}
36262 isIdSelected : function(id){
36263 return (this.selections.key(id) ? true : false);
36267 handleMouseDown : function(e, t){
36268 var view = this.grid.getView(), rowIndex;
36269 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36272 if(e.shiftKey && this.last !== false){
36273 var last = this.last;
36274 this.selectRange(last, rowIndex, e.ctrlKey);
36275 this.last = last; // reset the last
36276 view.focusRow(rowIndex);
36278 var isSelected = this.isSelected(rowIndex);
36279 if(e.button !== 0 && isSelected){
36280 view.focusRow(rowIndex);
36281 }else if(e.ctrlKey && isSelected){
36282 this.deselectRow(rowIndex);
36283 }else if(!isSelected){
36284 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36285 view.focusRow(rowIndex);
36288 this.fireEvent("afterselectionchange", this);
36291 handleDragableRowClick : function(grid, rowIndex, e)
36293 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36294 this.selectRow(rowIndex, false);
36295 grid.view.focusRow(rowIndex);
36296 this.fireEvent("afterselectionchange", this);
36301 * Selects multiple rows.
36302 * @param {Array} rows Array of the indexes of the row to select
36303 * @param {Boolean} keepExisting (optional) True to keep existing selections
36305 selectRows : function(rows, keepExisting){
36307 this.clearSelections();
36309 for(var i = 0, len = rows.length; i < len; i++){
36310 this.selectRow(rows[i], true);
36315 * Selects a range of rows. All rows in between startRow and endRow are also selected.
36316 * @param {Number} startRow The index of the first row in the range
36317 * @param {Number} endRow The index of the last row in the range
36318 * @param {Boolean} keepExisting (optional) True to retain existing selections
36320 selectRange : function(startRow, endRow, keepExisting){
36325 this.clearSelections();
36327 if(startRow <= endRow){
36328 for(var i = startRow; i <= endRow; i++){
36329 this.selectRow(i, true);
36332 for(var i = startRow; i >= endRow; i--){
36333 this.selectRow(i, true);
36339 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36340 * @param {Number} startRow The index of the first row in the range
36341 * @param {Number} endRow The index of the last row in the range
36343 deselectRange : function(startRow, endRow, preventViewNotify){
36347 for(var i = startRow; i <= endRow; i++){
36348 this.deselectRow(i, preventViewNotify);
36354 * @param {Number} row The index of the row to select
36355 * @param {Boolean} keepExisting (optional) True to keep existing selections
36357 selectRow : function(index, keepExisting, preventViewNotify){
36358 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
36361 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36362 if(!keepExisting || this.singleSelect){
36363 this.clearSelections();
36365 var r = this.grid.dataSource.getAt(index);
36366 this.selections.add(r);
36367 this.last = this.lastActive = index;
36368 if(!preventViewNotify){
36369 this.grid.getView().onRowSelect(index);
36371 this.fireEvent("rowselect", this, index, r);
36372 this.fireEvent("selectionchange", this);
36378 * @param {Number} row The index of the row to deselect
36380 deselectRow : function(index, preventViewNotify){
36384 if(this.last == index){
36387 if(this.lastActive == index){
36388 this.lastActive = false;
36390 var r = this.grid.dataSource.getAt(index);
36391 this.selections.remove(r);
36392 if(!preventViewNotify){
36393 this.grid.getView().onRowDeselect(index);
36395 this.fireEvent("rowdeselect", this, index);
36396 this.fireEvent("selectionchange", this);
36400 restoreLast : function(){
36402 this.last = this._last;
36407 acceptsNav : function(row, col, cm){
36408 return !cm.isHidden(col) && cm.isCellEditable(col, row);
36412 onEditorKey : function(field, e){
36413 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36418 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36420 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36422 }else if(k == e.ENTER && !e.ctrlKey){
36426 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36428 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36430 }else if(k == e.ESC){
36434 g.startEditing(newCell[0], newCell[1]);
36439 * Ext JS Library 1.1.1
36440 * Copyright(c) 2006-2007, Ext JS, LLC.
36442 * Originally Released Under LGPL - original licence link has changed is not relivant.
36445 * <script type="text/javascript">
36448 * @class Roo.grid.CellSelectionModel
36449 * @extends Roo.grid.AbstractSelectionModel
36450 * This class provides the basic implementation for cell selection in a grid.
36452 * @param {Object} config The object containing the configuration of this model.
36453 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36455 Roo.grid.CellSelectionModel = function(config){
36456 Roo.apply(this, config);
36458 this.selection = null;
36462 * @event beforerowselect
36463 * Fires before a cell is selected.
36464 * @param {SelectionModel} this
36465 * @param {Number} rowIndex The selected row index
36466 * @param {Number} colIndex The selected cell index
36468 "beforecellselect" : true,
36470 * @event cellselect
36471 * Fires when a cell is selected.
36472 * @param {SelectionModel} this
36473 * @param {Number} rowIndex The selected row index
36474 * @param {Number} colIndex The selected cell index
36476 "cellselect" : true,
36478 * @event selectionchange
36479 * Fires when the active selection changes.
36480 * @param {SelectionModel} this
36481 * @param {Object} selection null for no selection or an object (o) with two properties
36483 <li>o.record: the record object for the row the selection is in</li>
36484 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36487 "selectionchange" : true,
36490 * Fires when the tab (or enter) was pressed on the last editable cell
36491 * You can use this to trigger add new row.
36492 * @param {SelectionModel} this
36496 * @event beforeeditnext
36497 * Fires before the next editable sell is made active
36498 * You can use this to skip to another cell or fire the tabend
36499 * if you set cell to false
36500 * @param {Object} eventdata object : { cell : [ row, col ] }
36502 "beforeeditnext" : true
36504 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36507 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
36509 enter_is_tab: false,
36512 initEvents : function(){
36513 this.grid.on("mousedown", this.handleMouseDown, this);
36514 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36515 var view = this.grid.view;
36516 view.on("refresh", this.onViewChange, this);
36517 view.on("rowupdated", this.onRowUpdated, this);
36518 view.on("beforerowremoved", this.clearSelections, this);
36519 view.on("beforerowsinserted", this.clearSelections, this);
36520 if(this.grid.isEditor){
36521 this.grid.on("beforeedit", this.beforeEdit, this);
36526 beforeEdit : function(e){
36527 this.select(e.row, e.column, false, true, e.record);
36531 onRowUpdated : function(v, index, r){
36532 if(this.selection && this.selection.record == r){
36533 v.onCellSelect(index, this.selection.cell[1]);
36538 onViewChange : function(){
36539 this.clearSelections(true);
36543 * Returns the currently selected cell,.
36544 * @return {Array} The selected cell (row, column) or null if none selected.
36546 getSelectedCell : function(){
36547 return this.selection ? this.selection.cell : null;
36551 * Clears all selections.
36552 * @param {Boolean} true to prevent the gridview from being notified about the change.
36554 clearSelections : function(preventNotify){
36555 var s = this.selection;
36557 if(preventNotify !== true){
36558 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36560 this.selection = null;
36561 this.fireEvent("selectionchange", this, null);
36566 * Returns true if there is a selection.
36567 * @return {Boolean}
36569 hasSelection : function(){
36570 return this.selection ? true : false;
36574 handleMouseDown : function(e, t){
36575 var v = this.grid.getView();
36576 if(this.isLocked()){
36579 var row = v.findRowIndex(t);
36580 var cell = v.findCellIndex(t);
36581 if(row !== false && cell !== false){
36582 this.select(row, cell);
36588 * @param {Number} rowIndex
36589 * @param {Number} collIndex
36591 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36592 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36593 this.clearSelections();
36594 r = r || this.grid.dataSource.getAt(rowIndex);
36597 cell : [rowIndex, colIndex]
36599 if(!preventViewNotify){
36600 var v = this.grid.getView();
36601 v.onCellSelect(rowIndex, colIndex);
36602 if(preventFocus !== true){
36603 v.focusCell(rowIndex, colIndex);
36606 this.fireEvent("cellselect", this, rowIndex, colIndex);
36607 this.fireEvent("selectionchange", this, this.selection);
36612 isSelectable : function(rowIndex, colIndex, cm){
36613 return !cm.isHidden(colIndex);
36617 handleKeyDown : function(e){
36618 //Roo.log('Cell Sel Model handleKeyDown');
36619 if(!e.isNavKeyPress()){
36622 var g = this.grid, s = this.selection;
36625 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
36627 this.select(cell[0], cell[1]);
36632 var walk = function(row, col, step){
36633 return g.walkCells(row, col, step, sm.isSelectable, sm);
36635 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36642 // handled by onEditorKey
36643 if (g.isEditor && g.editing) {
36647 newCell = walk(r, c-1, -1);
36649 newCell = walk(r, c+1, 1);
36654 newCell = walk(r+1, c, 1);
36658 newCell = walk(r-1, c, -1);
36662 newCell = walk(r, c+1, 1);
36666 newCell = walk(r, c-1, -1);
36671 if(g.isEditor && !g.editing){
36672 g.startEditing(r, c);
36681 this.select(newCell[0], newCell[1]);
36687 acceptsNav : function(row, col, cm){
36688 return !cm.isHidden(col) && cm.isCellEditable(col, row);
36692 * @param {Number} field (not used) - as it's normally used as a listener
36693 * @param {Number} e - event - fake it by using
36695 * var e = Roo.EventObjectImpl.prototype;
36696 * e.keyCode = e.TAB
36700 onEditorKey : function(field, e){
36702 var k = e.getKey(),
36705 ed = g.activeEditor,
36707 ///Roo.log('onEditorKey' + k);
36710 if (this.enter_is_tab && k == e.ENTER) {
36716 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36718 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36724 } else if(k == e.ENTER && !e.ctrlKey){
36727 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36729 } else if(k == e.ESC){
36734 var ecall = { cell : newCell, forward : forward };
36735 this.fireEvent('beforeeditnext', ecall );
36736 newCell = ecall.cell;
36737 forward = ecall.forward;
36741 //Roo.log('next cell after edit');
36742 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36743 } else if (forward) {
36744 // tabbed past last
36745 this.fireEvent.defer(100, this, ['tabend',this]);
36750 * Ext JS Library 1.1.1
36751 * Copyright(c) 2006-2007, Ext JS, LLC.
36753 * Originally Released Under LGPL - original licence link has changed is not relivant.
36756 * <script type="text/javascript">
36760 * @class Roo.grid.EditorGrid
36761 * @extends Roo.grid.Grid
36762 * Class for creating and editable grid.
36763 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36764 * The container MUST have some type of size defined for the grid to fill. The container will be
36765 * automatically set to position relative if it isn't already.
36766 * @param {Object} dataSource The data model to bind to
36767 * @param {Object} colModel The column model with info about this grid's columns
36769 Roo.grid.EditorGrid = function(container, config){
36770 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36771 this.getGridEl().addClass("xedit-grid");
36773 if(!this.selModel){
36774 this.selModel = new Roo.grid.CellSelectionModel();
36777 this.activeEditor = null;
36781 * @event beforeedit
36782 * Fires before cell editing is triggered. The edit event object has the following properties <br />
36783 * <ul style="padding:5px;padding-left:16px;">
36784 * <li>grid - This grid</li>
36785 * <li>record - The record being edited</li>
36786 * <li>field - The field name being edited</li>
36787 * <li>value - The value for the field being edited.</li>
36788 * <li>row - The grid row index</li>
36789 * <li>column - The grid column index</li>
36790 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36792 * @param {Object} e An edit event (see above for description)
36794 "beforeedit" : true,
36797 * Fires after a cell is edited. <br />
36798 * <ul style="padding:5px;padding-left:16px;">
36799 * <li>grid - This grid</li>
36800 * <li>record - The record being edited</li>
36801 * <li>field - The field name being edited</li>
36802 * <li>value - The value being set</li>
36803 * <li>originalValue - The original value for the field, before the edit.</li>
36804 * <li>row - The grid row index</li>
36805 * <li>column - The grid column index</li>
36807 * @param {Object} e An edit event (see above for description)
36809 "afteredit" : true,
36811 * @event validateedit
36812 * Fires after a cell is edited, but before the value is set in the record.
36813 * You can use this to modify the value being set in the field, Return false
36814 * to cancel the change. The edit event object has the following properties <br />
36815 * <ul style="padding:5px;padding-left:16px;">
36816 * <li>editor - This editor</li>
36817 * <li>grid - This grid</li>
36818 * <li>record - The record being edited</li>
36819 * <li>field - The field name being edited</li>
36820 * <li>value - The value being set</li>
36821 * <li>originalValue - The original value for the field, before the edit.</li>
36822 * <li>row - The grid row index</li>
36823 * <li>column - The grid column index</li>
36824 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36826 * @param {Object} e An edit event (see above for description)
36828 "validateedit" : true
36830 this.on("bodyscroll", this.stopEditing, this);
36831 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
36834 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36836 * @cfg {Number} clicksToEdit
36837 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36844 trackMouseOver: false, // causes very odd FF errors
36846 onCellDblClick : function(g, row, col){
36847 this.startEditing(row, col);
36850 onEditComplete : function(ed, value, startValue){
36851 this.editing = false;
36852 this.activeEditor = null;
36853 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36855 var field = this.colModel.getDataIndex(ed.col);
36860 originalValue: startValue,
36867 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36870 if(String(value) !== String(startValue)){
36872 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36873 r.set(field, e.value);
36874 // if we are dealing with a combo box..
36875 // then we also set the 'name' colum to be the displayField
36876 if (ed.field.displayField && ed.field.name) {
36877 r.set(ed.field.name, ed.field.el.dom.value);
36880 delete e.cancel; //?? why!!!
36881 this.fireEvent("afteredit", e);
36884 this.fireEvent("afteredit", e); // always fire it!
36886 this.view.focusCell(ed.row, ed.col);
36890 * Starts editing the specified for the specified row/column
36891 * @param {Number} rowIndex
36892 * @param {Number} colIndex
36894 startEditing : function(row, col){
36895 this.stopEditing();
36896 if(this.colModel.isCellEditable(col, row)){
36897 this.view.ensureVisible(row, col, true);
36899 var r = this.dataSource.getAt(row);
36900 var field = this.colModel.getDataIndex(col);
36901 var cell = Roo.get(this.view.getCell(row,col));
36906 value: r.data[field],
36911 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36912 this.editing = true;
36913 var ed = this.colModel.getCellEditor(col, row);
36919 ed.render(ed.parentEl || document.body);
36925 (function(){ // complex but required for focus issues in safari, ie and opera
36929 ed.on("complete", this.onEditComplete, this, {single: true});
36930 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
36931 this.activeEditor = ed;
36932 var v = r.data[field];
36933 ed.startEdit(this.view.getCell(row, col), v);
36934 // combo's with 'displayField and name set
36935 if (ed.field.displayField && ed.field.name) {
36936 ed.field.el.dom.value = r.data[ed.field.name];
36940 }).defer(50, this);
36946 * Stops any active editing
36948 stopEditing : function(){
36949 if(this.activeEditor){
36950 this.activeEditor.completeEdit();
36952 this.activeEditor = null;
36956 * Called to get grid's drag proxy text, by default returns this.ddText.
36959 getDragDropText : function(){
36960 var count = this.selModel.getSelectedCell() ? 1 : 0;
36961 return String.format(this.ddText, count, count == 1 ? '' : 's');
36966 * Ext JS Library 1.1.1
36967 * Copyright(c) 2006-2007, Ext JS, LLC.
36969 * Originally Released Under LGPL - original licence link has changed is not relivant.
36972 * <script type="text/javascript">
36975 // private - not really -- you end up using it !
36976 // This is a support class used internally by the Grid components
36979 * @class Roo.grid.GridEditor
36980 * @extends Roo.Editor
36981 * Class for creating and editable grid elements.
36982 * @param {Object} config any settings (must include field)
36984 Roo.grid.GridEditor = function(field, config){
36985 if (!config && field.field) {
36987 field = Roo.factory(config.field, Roo.form);
36989 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36990 field.monitorTab = false;
36993 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36996 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36999 alignment: "tl-tl",
37002 cls: "x-small-editor x-grid-editor",
37007 * Ext JS Library 1.1.1
37008 * Copyright(c) 2006-2007, Ext JS, LLC.
37010 * Originally Released Under LGPL - original licence link has changed is not relivant.
37013 * <script type="text/javascript">
37018 Roo.grid.PropertyRecord = Roo.data.Record.create([
37019 {name:'name',type:'string'}, 'value'
37023 Roo.grid.PropertyStore = function(grid, source){
37025 this.store = new Roo.data.Store({
37026 recordType : Roo.grid.PropertyRecord
37028 this.store.on('update', this.onUpdate, this);
37030 this.setSource(source);
37032 Roo.grid.PropertyStore.superclass.constructor.call(this);
37037 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37038 setSource : function(o){
37040 this.store.removeAll();
37043 if(this.isEditableValue(o[k])){
37044 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37047 this.store.loadRecords({records: data}, {}, true);
37050 onUpdate : function(ds, record, type){
37051 if(type == Roo.data.Record.EDIT){
37052 var v = record.data['value'];
37053 var oldValue = record.modified['value'];
37054 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37055 this.source[record.id] = v;
37057 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37064 getProperty : function(row){
37065 return this.store.getAt(row);
37068 isEditableValue: function(val){
37069 if(val && val instanceof Date){
37071 }else if(typeof val == 'object' || typeof val == 'function'){
37077 setValue : function(prop, value){
37078 this.source[prop] = value;
37079 this.store.getById(prop).set('value', value);
37082 getSource : function(){
37083 return this.source;
37087 Roo.grid.PropertyColumnModel = function(grid, store){
37090 g.PropertyColumnModel.superclass.constructor.call(this, [
37091 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37092 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37094 this.store = store;
37095 this.bselect = Roo.DomHelper.append(document.body, {
37096 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37097 {tag: 'option', value: 'true', html: 'true'},
37098 {tag: 'option', value: 'false', html: 'false'}
37101 Roo.id(this.bselect);
37104 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37105 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37106 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37107 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37108 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37110 this.renderCellDelegate = this.renderCell.createDelegate(this);
37111 this.renderPropDelegate = this.renderProp.createDelegate(this);
37114 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37118 valueText : 'Value',
37120 dateFormat : 'm/j/Y',
37123 renderDate : function(dateVal){
37124 return dateVal.dateFormat(this.dateFormat);
37127 renderBool : function(bVal){
37128 return bVal ? 'true' : 'false';
37131 isCellEditable : function(colIndex, rowIndex){
37132 return colIndex == 1;
37135 getRenderer : function(col){
37137 this.renderCellDelegate : this.renderPropDelegate;
37140 renderProp : function(v){
37141 return this.getPropertyName(v);
37144 renderCell : function(val){
37146 if(val instanceof Date){
37147 rv = this.renderDate(val);
37148 }else if(typeof val == 'boolean'){
37149 rv = this.renderBool(val);
37151 return Roo.util.Format.htmlEncode(rv);
37154 getPropertyName : function(name){
37155 var pn = this.grid.propertyNames;
37156 return pn && pn[name] ? pn[name] : name;
37159 getCellEditor : function(colIndex, rowIndex){
37160 var p = this.store.getProperty(rowIndex);
37161 var n = p.data['name'], val = p.data['value'];
37163 if(typeof(this.grid.customEditors[n]) == 'string'){
37164 return this.editors[this.grid.customEditors[n]];
37166 if(typeof(this.grid.customEditors[n]) != 'undefined'){
37167 return this.grid.customEditors[n];
37169 if(val instanceof Date){
37170 return this.editors['date'];
37171 }else if(typeof val == 'number'){
37172 return this.editors['number'];
37173 }else if(typeof val == 'boolean'){
37174 return this.editors['boolean'];
37176 return this.editors['string'];
37182 * @class Roo.grid.PropertyGrid
37183 * @extends Roo.grid.EditorGrid
37184 * This class represents the interface of a component based property grid control.
37185 * <br><br>Usage:<pre><code>
37186 var grid = new Roo.grid.PropertyGrid("my-container-id", {
37194 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37195 * The container MUST have some type of size defined for the grid to fill. The container will be
37196 * automatically set to position relative if it isn't already.
37197 * @param {Object} config A config object that sets properties on this grid.
37199 Roo.grid.PropertyGrid = function(container, config){
37200 config = config || {};
37201 var store = new Roo.grid.PropertyStore(this);
37202 this.store = store;
37203 var cm = new Roo.grid.PropertyColumnModel(this, store);
37204 store.store.sort('name', 'ASC');
37205 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37208 enableColLock:false,
37209 enableColumnMove:false,
37211 trackMouseOver: false,
37214 this.getGridEl().addClass('x-props-grid');
37215 this.lastEditRow = null;
37216 this.on('columnresize', this.onColumnResize, this);
37219 * @event beforepropertychange
37220 * Fires before a property changes (return false to stop?)
37221 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37222 * @param {String} id Record Id
37223 * @param {String} newval New Value
37224 * @param {String} oldval Old Value
37226 "beforepropertychange": true,
37228 * @event propertychange
37229 * Fires after a property changes
37230 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37231 * @param {String} id Record Id
37232 * @param {String} newval New Value
37233 * @param {String} oldval Old Value
37235 "propertychange": true
37237 this.customEditors = this.customEditors || {};
37239 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37242 * @cfg {Object} customEditors map of colnames=> custom editors.
37243 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37244 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37245 * false disables editing of the field.
37249 * @cfg {Object} propertyNames map of property Names to their displayed value
37252 render : function(){
37253 Roo.grid.PropertyGrid.superclass.render.call(this);
37254 this.autoSize.defer(100, this);
37257 autoSize : function(){
37258 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37260 this.view.fitColumns();
37264 onColumnResize : function(){
37265 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37269 * Sets the data for the Grid
37270 * accepts a Key => Value object of all the elements avaiable.
37271 * @param {Object} data to appear in grid.
37273 setSource : function(source){
37274 this.store.setSource(source);
37278 * Gets all the data from the grid.
37279 * @return {Object} data data stored in grid
37281 getSource : function(){
37282 return this.store.getSource();
37291 * @class Roo.grid.Calendar
37292 * @extends Roo.util.Grid
37293 * This class extends the Grid to provide a calendar widget
37294 * <br><br>Usage:<pre><code>
37295 var grid = new Roo.grid.Calendar("my-container-id", {
37298 selModel: mySelectionModel,
37299 autoSizeColumns: true,
37300 monitorWindowResize: false,
37301 trackMouseOver: true
37302 eventstore : real data store..
37308 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37309 * The container MUST have some type of size defined for the grid to fill. The container will be
37310 * automatically set to position relative if it isn't already.
37311 * @param {Object} config A config object that sets properties on this grid.
37313 Roo.grid.Calendar = function(container, config){
37314 // initialize the container
37315 this.container = Roo.get(container);
37316 this.container.update("");
37317 this.container.setStyle("overflow", "hidden");
37318 this.container.addClass('x-grid-container');
37320 this.id = this.container.id;
37322 Roo.apply(this, config);
37323 // check and correct shorthanded configs
37327 for (var r = 0;r < 6;r++) {
37330 for (var c =0;c < 7;c++) {
37334 if (this.eventStore) {
37335 this.eventStore= Roo.factory(this.eventStore, Roo.data);
37336 this.eventStore.on('load',this.onLoad, this);
37337 this.eventStore.on('beforeload',this.clearEvents, this);
37341 this.dataSource = new Roo.data.Store({
37342 proxy: new Roo.data.MemoryProxy(rows),
37343 reader: new Roo.data.ArrayReader({}, [
37344 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37347 this.dataSource.load();
37348 this.ds = this.dataSource;
37349 this.ds.xmodule = this.xmodule || false;
37352 var cellRender = function(v,x,r)
37354 return String.format(
37355 '<div class="fc-day fc-widget-content"><div>' +
37356 '<div class="fc-event-container"></div>' +
37357 '<div class="fc-day-number">{0}</div>'+
37359 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37360 '</div></div>', v);
37365 this.colModel = new Roo.grid.ColumnModel( [
37367 xtype: 'ColumnModel',
37369 dataIndex : 'weekday0',
37371 renderer : cellRender
37374 xtype: 'ColumnModel',
37376 dataIndex : 'weekday1',
37378 renderer : cellRender
37381 xtype: 'ColumnModel',
37383 dataIndex : 'weekday2',
37384 header : 'Tuesday',
37385 renderer : cellRender
37388 xtype: 'ColumnModel',
37390 dataIndex : 'weekday3',
37391 header : 'Wednesday',
37392 renderer : cellRender
37395 xtype: 'ColumnModel',
37397 dataIndex : 'weekday4',
37398 header : 'Thursday',
37399 renderer : cellRender
37402 xtype: 'ColumnModel',
37404 dataIndex : 'weekday5',
37406 renderer : cellRender
37409 xtype: 'ColumnModel',
37411 dataIndex : 'weekday6',
37412 header : 'Saturday',
37413 renderer : cellRender
37416 this.cm = this.colModel;
37417 this.cm.xmodule = this.xmodule || false;
37421 //this.selModel = new Roo.grid.CellSelectionModel();
37422 //this.sm = this.selModel;
37423 //this.selModel.init(this);
37427 this.container.setWidth(this.width);
37431 this.container.setHeight(this.height);
37438 * The raw click event for the entire grid.
37439 * @param {Roo.EventObject} e
37444 * The raw dblclick event for the entire grid.
37445 * @param {Roo.EventObject} e
37449 * @event contextmenu
37450 * The raw contextmenu event for the entire grid.
37451 * @param {Roo.EventObject} e
37453 "contextmenu" : true,
37456 * The raw mousedown event for the entire grid.
37457 * @param {Roo.EventObject} e
37459 "mousedown" : true,
37462 * The raw mouseup event for the entire grid.
37463 * @param {Roo.EventObject} e
37468 * The raw mouseover event for the entire grid.
37469 * @param {Roo.EventObject} e
37471 "mouseover" : true,
37474 * The raw mouseout event for the entire grid.
37475 * @param {Roo.EventObject} e
37480 * The raw keypress event for the entire grid.
37481 * @param {Roo.EventObject} e
37486 * The raw keydown event for the entire grid.
37487 * @param {Roo.EventObject} e
37495 * Fires when a cell is clicked
37496 * @param {Grid} this
37497 * @param {Number} rowIndex
37498 * @param {Number} columnIndex
37499 * @param {Roo.EventObject} e
37501 "cellclick" : true,
37503 * @event celldblclick
37504 * Fires when a cell is double clicked
37505 * @param {Grid} this
37506 * @param {Number} rowIndex
37507 * @param {Number} columnIndex
37508 * @param {Roo.EventObject} e
37510 "celldblclick" : true,
37513 * Fires when a row is clicked
37514 * @param {Grid} this
37515 * @param {Number} rowIndex
37516 * @param {Roo.EventObject} e
37520 * @event rowdblclick
37521 * Fires when a row is double clicked
37522 * @param {Grid} this
37523 * @param {Number} rowIndex
37524 * @param {Roo.EventObject} e
37526 "rowdblclick" : true,
37528 * @event headerclick
37529 * Fires when a header is clicked
37530 * @param {Grid} this
37531 * @param {Number} columnIndex
37532 * @param {Roo.EventObject} e
37534 "headerclick" : true,
37536 * @event headerdblclick
37537 * Fires when a header cell is double clicked
37538 * @param {Grid} this
37539 * @param {Number} columnIndex
37540 * @param {Roo.EventObject} e
37542 "headerdblclick" : true,
37544 * @event rowcontextmenu
37545 * Fires when a row is right clicked
37546 * @param {Grid} this
37547 * @param {Number} rowIndex
37548 * @param {Roo.EventObject} e
37550 "rowcontextmenu" : true,
37552 * @event cellcontextmenu
37553 * Fires when a cell is right clicked
37554 * @param {Grid} this
37555 * @param {Number} rowIndex
37556 * @param {Number} cellIndex
37557 * @param {Roo.EventObject} e
37559 "cellcontextmenu" : true,
37561 * @event headercontextmenu
37562 * Fires when a header is right clicked
37563 * @param {Grid} this
37564 * @param {Number} columnIndex
37565 * @param {Roo.EventObject} e
37567 "headercontextmenu" : true,
37569 * @event bodyscroll
37570 * Fires when the body element is scrolled
37571 * @param {Number} scrollLeft
37572 * @param {Number} scrollTop
37574 "bodyscroll" : true,
37576 * @event columnresize
37577 * Fires when the user resizes a column
37578 * @param {Number} columnIndex
37579 * @param {Number} newSize
37581 "columnresize" : true,
37583 * @event columnmove
37584 * Fires when the user moves a column
37585 * @param {Number} oldIndex
37586 * @param {Number} newIndex
37588 "columnmove" : true,
37591 * Fires when row(s) start being dragged
37592 * @param {Grid} this
37593 * @param {Roo.GridDD} dd The drag drop object
37594 * @param {event} e The raw browser event
37596 "startdrag" : true,
37599 * Fires when a drag operation is complete
37600 * @param {Grid} this
37601 * @param {Roo.GridDD} dd The drag drop object
37602 * @param {event} e The raw browser event
37607 * Fires when dragged row(s) are dropped on a valid DD target
37608 * @param {Grid} this
37609 * @param {Roo.GridDD} dd The drag drop object
37610 * @param {String} targetId The target drag drop object
37611 * @param {event} e The raw browser event
37616 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37617 * @param {Grid} this
37618 * @param {Roo.GridDD} dd The drag drop object
37619 * @param {String} targetId The target drag drop object
37620 * @param {event} e The raw browser event
37625 * Fires when the dragged row(s) first cross another DD target while being dragged
37626 * @param {Grid} this
37627 * @param {Roo.GridDD} dd The drag drop object
37628 * @param {String} targetId The target drag drop object
37629 * @param {event} e The raw browser event
37631 "dragenter" : true,
37634 * Fires when the dragged row(s) leave another DD target while being dragged
37635 * @param {Grid} this
37636 * @param {Roo.GridDD} dd The drag drop object
37637 * @param {String} targetId The target drag drop object
37638 * @param {event} e The raw browser event
37643 * Fires when a row is rendered, so you can change add a style to it.
37644 * @param {GridView} gridview The grid view
37645 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
37651 * Fires when the grid is rendered
37652 * @param {Grid} grid
37657 * Fires when a date is selected
37658 * @param {DatePicker} this
37659 * @param {Date} date The selected date
37663 * @event monthchange
37664 * Fires when the displayed month changes
37665 * @param {DatePicker} this
37666 * @param {Date} date The selected month
37668 'monthchange': true,
37670 * @event evententer
37671 * Fires when mouse over an event
37672 * @param {Calendar} this
37673 * @param {event} Event
37675 'evententer': true,
37677 * @event eventleave
37678 * Fires when the mouse leaves an
37679 * @param {Calendar} this
37682 'eventleave': true,
37684 * @event eventclick
37685 * Fires when the mouse click an
37686 * @param {Calendar} this
37689 'eventclick': true,
37691 * @event eventrender
37692 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37693 * @param {Calendar} this
37694 * @param {data} data to be modified
37696 'eventrender': true
37700 Roo.grid.Grid.superclass.constructor.call(this);
37701 this.on('render', function() {
37702 this.view.el.addClass('x-grid-cal');
37704 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37708 if (!Roo.grid.Calendar.style) {
37709 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37712 '.x-grid-cal .x-grid-col' : {
37713 height: 'auto !important',
37714 'vertical-align': 'top'
37716 '.x-grid-cal .fc-event-hori' : {
37727 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37729 * @cfg {Store} eventStore The store that loads events.
37734 activeDate : false,
37737 monitorWindowResize : false,
37740 resizeColumns : function() {
37741 var col = (this.view.el.getWidth() / 7) - 3;
37742 // loop through cols, and setWidth
37743 for(var i =0 ; i < 7 ; i++){
37744 this.cm.setColumnWidth(i, col);
37747 setDate :function(date) {
37749 Roo.log('setDate?');
37751 this.resizeColumns();
37752 var vd = this.activeDate;
37753 this.activeDate = date;
37754 // if(vd && this.el){
37755 // var t = date.getTime();
37756 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37757 // Roo.log('using add remove');
37759 // this.fireEvent('monthchange', this, date);
37761 // this.cells.removeClass("fc-state-highlight");
37762 // this.cells.each(function(c){
37763 // if(c.dateValue == t){
37764 // c.addClass("fc-state-highlight");
37765 // setTimeout(function(){
37766 // try{c.dom.firstChild.focus();}catch(e){}
37776 var days = date.getDaysInMonth();
37778 var firstOfMonth = date.getFirstDateOfMonth();
37779 var startingPos = firstOfMonth.getDay()-this.startDay;
37781 if(startingPos < this.startDay){
37785 var pm = date.add(Date.MONTH, -1);
37786 var prevStart = pm.getDaysInMonth()-startingPos;
37790 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37792 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37793 //this.cells.addClassOnOver('fc-state-hover');
37795 var cells = this.cells.elements;
37796 var textEls = this.textNodes;
37798 //Roo.each(cells, function(cell){
37799 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37802 days += startingPos;
37804 // convert everything to numbers so it's fast
37805 var day = 86400000;
37806 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37809 //Roo.log(prevStart);
37811 var today = new Date().clearTime().getTime();
37812 var sel = date.clearTime().getTime();
37813 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37814 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37815 var ddMatch = this.disabledDatesRE;
37816 var ddText = this.disabledDatesText;
37817 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37818 var ddaysText = this.disabledDaysText;
37819 var format = this.format;
37821 var setCellClass = function(cal, cell){
37823 //Roo.log('set Cell Class');
37825 var t = d.getTime();
37830 cell.dateValue = t;
37832 cell.className += " fc-today";
37833 cell.className += " fc-state-highlight";
37834 cell.title = cal.todayText;
37837 // disable highlight in other month..
37838 cell.className += " fc-state-highlight";
37843 //cell.className = " fc-state-disabled";
37844 cell.title = cal.minText;
37848 //cell.className = " fc-state-disabled";
37849 cell.title = cal.maxText;
37853 if(ddays.indexOf(d.getDay()) != -1){
37854 // cell.title = ddaysText;
37855 // cell.className = " fc-state-disabled";
37858 if(ddMatch && format){
37859 var fvalue = d.dateFormat(format);
37860 if(ddMatch.test(fvalue)){
37861 cell.title = ddText.replace("%0", fvalue);
37862 cell.className = " fc-state-disabled";
37866 if (!cell.initialClassName) {
37867 cell.initialClassName = cell.dom.className;
37870 cell.dom.className = cell.initialClassName + ' ' + cell.className;
37875 for(; i < startingPos; i++) {
37876 cells[i].dayName = (++prevStart);
37877 Roo.log(textEls[i]);
37878 d.setDate(d.getDate()+1);
37880 //cells[i].className = "fc-past fc-other-month";
37881 setCellClass(this, cells[i]);
37886 for(; i < days; i++){
37887 intDay = i - startingPos + 1;
37888 cells[i].dayName = (intDay);
37889 d.setDate(d.getDate()+1);
37891 cells[i].className = ''; // "x-date-active";
37892 setCellClass(this, cells[i]);
37896 for(; i < 42; i++) {
37897 //textEls[i].innerHTML = (++extraDays);
37899 d.setDate(d.getDate()+1);
37900 cells[i].dayName = (++extraDays);
37901 cells[i].className = "fc-future fc-other-month";
37902 setCellClass(this, cells[i]);
37905 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37907 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37909 // this will cause all the cells to mis
37912 for (var r = 0;r < 6;r++) {
37913 for (var c =0;c < 7;c++) {
37914 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37918 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37919 for(i=0;i<cells.length;i++) {
37921 this.cells.elements[i].dayName = cells[i].dayName ;
37922 this.cells.elements[i].className = cells[i].className;
37923 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37924 this.cells.elements[i].title = cells[i].title ;
37925 this.cells.elements[i].dateValue = cells[i].dateValue ;
37931 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37932 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37934 ////if(totalRows != 6){
37935 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37936 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37939 this.fireEvent('monthchange', this, date);
37944 * Returns the grid's SelectionModel.
37945 * @return {SelectionModel}
37947 getSelectionModel : function(){
37948 if(!this.selModel){
37949 this.selModel = new Roo.grid.CellSelectionModel();
37951 return this.selModel;
37955 this.eventStore.load()
37961 findCell : function(dt) {
37962 dt = dt.clearTime().getTime();
37964 this.cells.each(function(c){
37965 //Roo.log("check " +c.dateValue + '?=' + dt);
37966 if(c.dateValue == dt){
37976 findCells : function(rec) {
37977 var s = rec.data.start_dt.clone().clearTime().getTime();
37979 var e= rec.data.end_dt.clone().clearTime().getTime();
37982 this.cells.each(function(c){
37983 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37985 if(c.dateValue > e){
37988 if(c.dateValue < s){
37997 findBestRow: function(cells)
38001 for (var i =0 ; i < cells.length;i++) {
38002 ret = Math.max(cells[i].rows || 0,ret);
38009 addItem : function(rec)
38011 // look for vertical location slot in
38012 var cells = this.findCells(rec);
38014 rec.row = this.findBestRow(cells);
38016 // work out the location.
38020 for(var i =0; i < cells.length; i++) {
38028 if (crow.start.getY() == cells[i].getY()) {
38030 crow.end = cells[i];
38046 for (var i = 0; i < cells.length;i++) {
38047 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
38054 clearEvents: function() {
38056 if (!this.eventStore.getCount()) {
38059 // reset number of rows in cells.
38060 Roo.each(this.cells.elements, function(c){
38064 this.eventStore.each(function(e) {
38065 this.clearEvent(e);
38070 clearEvent : function(ev)
38073 Roo.each(ev.els, function(el) {
38074 el.un('mouseenter' ,this.onEventEnter, this);
38075 el.un('mouseleave' ,this.onEventLeave, this);
38083 renderEvent : function(ev,ctr) {
38085 ctr = this.view.el.select('.fc-event-container',true).first();
38089 this.clearEvent(ev);
38095 var cells = ev.cells;
38096 var rows = ev.rows;
38097 this.fireEvent('eventrender', this, ev);
38099 for(var i =0; i < rows.length; i++) {
38103 cls += ' fc-event-start';
38105 if ((i+1) == rows.length) {
38106 cls += ' fc-event-end';
38109 //Roo.log(ev.data);
38110 // how many rows should it span..
38111 var cg = this.eventTmpl.append(ctr,Roo.apply({
38114 }, ev.data) , true);
38117 cg.on('mouseenter' ,this.onEventEnter, this, ev);
38118 cg.on('mouseleave' ,this.onEventLeave, this, ev);
38119 cg.on('click', this.onEventClick, this, ev);
38123 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38124 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38127 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
38128 cg.setWidth(ebox.right - sbox.x -2);
38132 renderEvents: function()
38134 // first make sure there is enough space..
38136 if (!this.eventTmpl) {
38137 this.eventTmpl = new Roo.Template(
38138 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
38139 '<div class="fc-event-inner">' +
38140 '<span class="fc-event-time">{time}</span>' +
38141 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38143 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
38151 this.cells.each(function(c) {
38152 //Roo.log(c.select('.fc-day-content div',true).first());
38153 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38156 var ctr = this.view.el.select('.fc-event-container',true).first();
38159 this.eventStore.each(function(ev){
38161 this.renderEvent(ev);
38165 this.view.layout();
38169 onEventEnter: function (e, el,event,d) {
38170 this.fireEvent('evententer', this, el, event);
38173 onEventLeave: function (e, el,event,d) {
38174 this.fireEvent('eventleave', this, el, event);
38177 onEventClick: function (e, el,event,d) {
38178 this.fireEvent('eventclick', this, el, event);
38181 onMonthChange: function () {
38185 onLoad: function () {
38187 //Roo.log('calendar onload');
38189 if(this.eventStore.getCount() > 0){
38193 this.eventStore.each(function(d){
38198 if (typeof(add.end_dt) == 'undefined') {
38199 Roo.log("Missing End time in calendar data: ");
38203 if (typeof(add.start_dt) == 'undefined') {
38204 Roo.log("Missing Start time in calendar data: ");
38208 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38209 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38210 add.id = add.id || d.id;
38211 add.title = add.title || '??';
38219 this.renderEvents();
38229 render : function ()
38233 if (!this.view.el.hasClass('course-timesheet')) {
38234 this.view.el.addClass('course-timesheet');
38236 if (this.tsStyle) {
38241 Roo.log(_this.grid.view.el.getWidth());
38244 this.tsStyle = Roo.util.CSS.createStyleSheet({
38245 '.course-timesheet .x-grid-row' : {
38248 '.x-grid-row td' : {
38249 'vertical-align' : 0
38251 '.course-edit-link' : {
38253 'text-overflow' : 'ellipsis',
38254 'overflow' : 'hidden',
38255 'white-space' : 'nowrap',
38256 'cursor' : 'pointer'
38261 '.de-act-sup-link' : {
38262 'color' : 'purple',
38263 'text-decoration' : 'line-through'
38267 'text-decoration' : 'line-through'
38269 '.course-timesheet .course-highlight' : {
38270 'border-top-style': 'dashed !important',
38271 'border-bottom-bottom': 'dashed !important'
38273 '.course-timesheet .course-item' : {
38274 'font-family' : 'tahoma, arial, helvetica',
38275 'font-size' : '11px',
38276 'overflow' : 'hidden',
38277 'padding-left' : '10px',
38278 'padding-right' : '10px',
38279 'padding-top' : '10px'
38287 monitorWindowResize : false,
38288 cellrenderer : function(v,x,r)
38293 xtype: 'CellSelectionModel',
38300 beforeload : function (_self, options)
38302 options.params = options.params || {};
38303 options.params._month = _this.monthField.getValue();
38304 options.params.limit = 9999;
38305 options.params['sort'] = 'when_dt';
38306 options.params['dir'] = 'ASC';
38307 this.proxy.loadResponse = this.loadResponse;
38309 //this.addColumns();
38311 load : function (_self, records, options)
38313 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38314 // if you click on the translation.. you can edit it...
38315 var el = Roo.get(this);
38316 var id = el.dom.getAttribute('data-id');
38317 var d = el.dom.getAttribute('data-date');
38318 var t = el.dom.getAttribute('data-time');
38319 //var id = this.child('span').dom.textContent;
38322 Pman.Dialog.CourseCalendar.show({
38326 productitem_active : id ? 1 : 0
38328 _this.grid.ds.load({});
38333 _this.panel.fireEvent('resize', [ '', '' ]);
38336 loadResponse : function(o, success, response){
38337 // this is overridden on before load..
38339 Roo.log("our code?");
38340 //Roo.log(success);
38341 //Roo.log(response)
38342 delete this.activeRequest;
38344 this.fireEvent("loadexception", this, o, response);
38345 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38350 result = o.reader.read(response);
38352 Roo.log("load exception?");
38353 this.fireEvent("loadexception", this, o, response, e);
38354 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38357 Roo.log("ready...");
38358 // loop through result.records;
38359 // and set this.tdate[date] = [] << array of records..
38361 Roo.each(result.records, function(r){
38363 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38364 _this.tdata[r.data.when_dt.format('j')] = [];
38366 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38369 //Roo.log(_this.tdata);
38371 result.records = [];
38372 result.totalRecords = 6;
38374 // let's generate some duumy records for the rows.
38375 //var st = _this.dateField.getValue();
38377 // work out monday..
38378 //st = st.add(Date.DAY, -1 * st.format('w'));
38380 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38382 var firstOfMonth = date.getFirstDayOfMonth();
38383 var days = date.getDaysInMonth();
38385 var firstAdded = false;
38386 for (var i = 0; i < result.totalRecords ; i++) {
38387 //var d= st.add(Date.DAY, i);
38390 for(var w = 0 ; w < 7 ; w++){
38391 if(!firstAdded && firstOfMonth != w){
38398 var dd = (d > 0 && d < 10) ? "0"+d : d;
38399 row['weekday'+w] = String.format(
38400 '<span style="font-size: 16px;"><b>{0}</b></span>'+
38401 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38403 date.format('Y-m-')+dd
38406 if(typeof(_this.tdata[d]) != 'undefined'){
38407 Roo.each(_this.tdata[d], function(r){
38411 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38412 if(r.parent_id*1>0){
38413 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38416 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38417 deactive = 'de-act-link';
38420 row['weekday'+w] += String.format(
38421 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38423 r.product_id_name, //1
38424 r.when_dt.format('h:ia'), //2
38434 // only do this if something added..
38436 result.records.push(_this.grid.dataSource.reader.newRow(row));
38440 // push it twice. (second one with an hour..
38444 this.fireEvent("load", this, o, o.request.arg);
38445 o.request.callback.call(o.request.scope, result, o.request.arg, true);
38447 sortInfo : {field: 'when_dt', direction : 'ASC' },
38449 xtype: 'HttpProxy',
38452 url : baseURL + '/Roo/Shop_course.php'
38455 xtype: 'JsonReader',
38472 'name': 'parent_id',
38476 'name': 'product_id',
38480 'name': 'productitem_id',
38498 click : function (_self, e)
38500 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38501 sd.setMonth(sd.getMonth()-1);
38502 _this.monthField.setValue(sd.format('Y-m-d'));
38503 _this.grid.ds.load({});
38509 xtype: 'Separator',
38513 xtype: 'MonthField',
38516 render : function (_self)
38518 _this.monthField = _self;
38519 // _this.monthField.set today
38521 select : function (combo, date)
38523 _this.grid.ds.load({});
38526 value : (function() { return new Date(); })()
38529 xtype: 'Separator',
38535 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38545 click : function (_self, e)
38547 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38548 sd.setMonth(sd.getMonth()+1);
38549 _this.monthField.setValue(sd.format('Y-m-d'));
38550 _this.grid.ds.load({});
38563 * Ext JS Library 1.1.1
38564 * Copyright(c) 2006-2007, Ext JS, LLC.
38566 * Originally Released Under LGPL - original licence link has changed is not relivant.
38569 * <script type="text/javascript">
38573 * @class Roo.LoadMask
38574 * A simple utility class for generically masking elements while loading data. If the element being masked has
38575 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38576 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
38577 * element's UpdateManager load indicator and will be destroyed after the initial load.
38579 * Create a new LoadMask
38580 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38581 * @param {Object} config The config object
38583 Roo.LoadMask = function(el, config){
38584 this.el = Roo.get(el);
38585 Roo.apply(this, config);
38587 this.store.on('beforeload', this.onBeforeLoad, this);
38588 this.store.on('load', this.onLoad, this);
38589 this.store.on('loadexception', this.onLoadException, this);
38590 this.removeMask = false;
38592 var um = this.el.getUpdateManager();
38593 um.showLoadIndicator = false; // disable the default indicator
38594 um.on('beforeupdate', this.onBeforeLoad, this);
38595 um.on('update', this.onLoad, this);
38596 um.on('failure', this.onLoad, this);
38597 this.removeMask = true;
38601 Roo.LoadMask.prototype = {
38603 * @cfg {Boolean} removeMask
38604 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38605 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
38608 * @cfg {String} msg
38609 * The text to display in a centered loading message box (defaults to 'Loading...')
38611 msg : 'Loading...',
38613 * @cfg {String} msgCls
38614 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38616 msgCls : 'x-mask-loading',
38619 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38625 * Disables the mask to prevent it from being displayed
38627 disable : function(){
38628 this.disabled = true;
38632 * Enables the mask so that it can be displayed
38634 enable : function(){
38635 this.disabled = false;
38638 onLoadException : function()
38640 Roo.log(arguments);
38642 if (typeof(arguments[3]) != 'undefined') {
38643 Roo.MessageBox.alert("Error loading",arguments[3]);
38647 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38648 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38655 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38658 onLoad : function()
38660 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38664 onBeforeLoad : function(){
38665 if(!this.disabled){
38666 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38671 destroy : function(){
38673 this.store.un('beforeload', this.onBeforeLoad, this);
38674 this.store.un('load', this.onLoad, this);
38675 this.store.un('loadexception', this.onLoadException, this);
38677 var um = this.el.getUpdateManager();
38678 um.un('beforeupdate', this.onBeforeLoad, this);
38679 um.un('update', this.onLoad, this);
38680 um.un('failure', this.onLoad, this);
38685 * Ext JS Library 1.1.1
38686 * Copyright(c) 2006-2007, Ext JS, LLC.
38688 * Originally Released Under LGPL - original licence link has changed is not relivant.
38691 * <script type="text/javascript">
38696 * @class Roo.XTemplate
38697 * @extends Roo.Template
38698 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38700 var t = new Roo.XTemplate(
38701 '<select name="{name}">',
38702 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
38706 // then append, applying the master template values
38709 * Supported features:
38714 {a_variable} - output encoded.
38715 {a_variable.format:("Y-m-d")} - call a method on the variable
38716 {a_variable:raw} - unencoded output
38717 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38718 {a_variable:this.method_on_template(...)} - call a method on the template object.
38723 <tpl for="a_variable or condition.."></tpl>
38724 <tpl if="a_variable or condition"></tpl>
38725 <tpl exec="some javascript"></tpl>
38726 <tpl name="named_template"></tpl> (experimental)
38728 <tpl for="."></tpl> - just iterate the property..
38729 <tpl for=".."></tpl> - iterates with the parent (probably the template)
38733 Roo.XTemplate = function()
38735 Roo.XTemplate.superclass.constructor.apply(this, arguments);
38742 Roo.extend(Roo.XTemplate, Roo.Template, {
38745 * The various sub templates
38750 * basic tag replacing syntax
38753 * // you can fake an object call by doing this
38757 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38760 * compile the template
38762 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38765 compile: function()
38769 s = ['<tpl>', s, '</tpl>'].join('');
38771 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38772 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38773 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
38774 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38775 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
38780 while(true == !!(m = s.match(re))){
38781 var forMatch = m[0].match(nameRe),
38782 ifMatch = m[0].match(ifRe),
38783 execMatch = m[0].match(execRe),
38784 namedMatch = m[0].match(namedRe),
38789 name = forMatch && forMatch[1] ? forMatch[1] : '';
38792 // if - puts fn into test..
38793 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38795 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38800 // exec - calls a function... returns empty if true is returned.
38801 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38803 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38811 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38812 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38813 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38816 var uid = namedMatch ? namedMatch[1] : id;
38820 id: namedMatch ? namedMatch[1] : id,
38827 s = s.replace(m[0], '');
38829 s = s.replace(m[0], '{xtpl'+ id + '}');
38834 for(var i = tpls.length-1; i >= 0; --i){
38835 this.compileTpl(tpls[i]);
38836 this.tpls[tpls[i].id] = tpls[i];
38838 this.master = tpls[tpls.length-1];
38842 * same as applyTemplate, except it's done to one of the subTemplates
38843 * when using named templates, you can do:
38845 * var str = pl.applySubTemplate('your-name', values);
38848 * @param {Number} id of the template
38849 * @param {Object} values to apply to template
38850 * @param {Object} parent (normaly the instance of this object)
38852 applySubTemplate : function(id, values, parent)
38856 var t = this.tpls[id];
38860 if(t.test && !t.test.call(this, values, parent)){
38864 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38865 Roo.log(e.toString());
38871 if(t.exec && t.exec.call(this, values, parent)){
38875 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38876 Roo.log(e.toString());
38881 var vs = t.target ? t.target.call(this, values, parent) : values;
38882 parent = t.target ? values : parent;
38883 if(t.target && vs instanceof Array){
38885 for(var i = 0, len = vs.length; i < len; i++){
38886 buf[buf.length] = t.compiled.call(this, vs[i], parent);
38888 return buf.join('');
38890 return t.compiled.call(this, vs, parent);
38892 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38893 Roo.log(e.toString());
38894 Roo.log(t.compiled);
38899 compileTpl : function(tpl)
38901 var fm = Roo.util.Format;
38902 var useF = this.disableFormats !== true;
38903 var sep = Roo.isGecko ? "+" : ",";
38904 var undef = function(str) {
38905 Roo.log("Property not found :" + str);
38909 var fn = function(m, name, format, args)
38911 //Roo.log(arguments);
38912 args = args ? args.replace(/\\'/g,"'") : args;
38913 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38914 if (typeof(format) == 'undefined') {
38915 format= 'htmlEncode';
38917 if (format == 'raw' ) {
38921 if(name.substr(0, 4) == 'xtpl'){
38922 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38925 // build an array of options to determine if value is undefined..
38927 // basically get 'xxxx.yyyy' then do
38928 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38929 // (function () { Roo.log("Property not found"); return ''; })() :
38934 Roo.each(name.split('.'), function(st) {
38935 lookfor += (lookfor.length ? '.': '') + st;
38936 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
38939 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38942 if(format && useF){
38944 args = args ? ',' + args : "";
38946 if(format.substr(0, 5) != "this."){
38947 format = "fm." + format + '(';
38949 format = 'this.call("'+ format.substr(5) + '", ';
38953 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
38957 // called with xxyx.yuu:(test,test)
38959 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
38961 // raw.. - :raw modifier..
38962 return "'"+ sep + udef_st + name + ")"+sep+"'";
38966 // branched to use + in gecko and [].join() in others
38968 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
38969 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38972 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
38973 body.push(tpl.body.replace(/(\r\n|\n)/g,
38974 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38975 body.push("'].join('');};};");
38976 body = body.join('');
38979 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38981 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
38987 applyTemplate : function(values){
38988 return this.master.compiled.call(this, values, {});
38989 //var s = this.subs;
38992 apply : function(){
38993 return this.applyTemplate.apply(this, arguments);
38998 Roo.XTemplate.from = function(el){
38999 el = Roo.getDom(el);
39000 return new Roo.XTemplate(el.value || el.innerHTML);