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)
794 this.loadData(this.reader.toLoadData(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
1993 // used when loading children.. @see loadDataFromChildren
1994 toLoadData: function(rec)
1996 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
1997 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
1998 return { data : data, total : data.length };
2003 * Ext JS Library 1.1.1
2004 * Copyright(c) 2006-2007, Ext JS, LLC.
2006 * Originally Released Under LGPL - original licence link has changed is not relivant.
2009 * <script type="text/javascript">
2013 * @class Roo.data.XmlReader
2014 * @extends Roo.data.DataReader
2015 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2016 * based on mappings in a provided Roo.data.Record constructor.<br><br>
2018 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2019 * header in the HTTP response must be set to "text/xml".</em>
2023 var RecordDef = Roo.data.Record.create([
2024 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
2025 {name: 'occupation'} // This field will use "occupation" as the mapping.
2027 var myReader = new Roo.data.XmlReader({
2028 totalRecords: "results", // The element which contains the total dataset size (optional)
2029 record: "row", // The repeated element which contains row information
2030 id: "id" // The element within the row that provides an ID for the record (optional)
2034 * This would consume an XML file like this:
2038 <results>2</results>
2041 <name>Bill</name>
2042 <occupation>Gardener</occupation>
2046 <name>Ben</name>
2047 <occupation>Horticulturalist</occupation>
2051 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2052 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2053 * paged from the remote server.
2054 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2055 * @cfg {String} success The DomQuery path to the success attribute used by forms.
2056 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2057 * a record identifier value.
2059 * Create a new XmlReader
2060 * @param {Object} meta Metadata configuration options
2061 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
2062 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2063 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
2065 Roo.data.XmlReader = function(meta, recordType){
2067 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2069 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2074 * This method is only used by a DataProxy which has retrieved data from a remote server.
2075 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
2076 * to contain a method called 'responseXML' that returns an XML document object.
2077 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2078 * a cache of Roo.data.Records.
2080 read : function(response){
2081 var doc = response.responseXML;
2083 throw {message: "XmlReader.read: XML Document not available"};
2085 return this.readRecords(doc);
2089 * Create a data block containing Roo.data.Records from an XML document.
2090 * @param {Object} doc A parsed XML document.
2091 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2092 * a cache of Roo.data.Records.
2094 readRecords : function(doc){
2096 * After any data loads/reads, the raw XML Document is available for further custom processing.
2100 var root = doc.documentElement || doc;
2101 var q = Roo.DomQuery;
2102 var recordType = this.recordType, fields = recordType.prototype.fields;
2103 var sid = this.meta.id;
2104 var totalRecords = 0, success = true;
2105 if(this.meta.totalRecords){
2106 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2109 if(this.meta.success){
2110 var sv = q.selectValue(this.meta.success, root, true);
2111 success = sv !== false && sv !== 'false';
2114 var ns = q.select(this.meta.record, root);
2115 for(var i = 0, len = ns.length; i < len; i++) {
2118 var id = sid ? q.selectValue(sid, n) : undefined;
2119 for(var j = 0, jlen = fields.length; j < jlen; j++){
2120 var f = fields.items[j];
2121 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2125 var record = new recordType(values, id);
2127 records[records.length] = record;
2133 totalRecords : totalRecords || records.length
2138 * Ext JS Library 1.1.1
2139 * Copyright(c) 2006-2007, Ext JS, LLC.
2141 * Originally Released Under LGPL - original licence link has changed is not relivant.
2144 * <script type="text/javascript">
2148 * @class Roo.data.ArrayReader
2149 * @extends Roo.data.DataReader
2150 * Data reader class to create an Array of Roo.data.Record objects from an Array.
2151 * Each element of that Array represents a row of data fields. The
2152 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2153 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2157 var RecordDef = Roo.data.Record.create([
2158 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
2159 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
2161 var myReader = new Roo.data.ArrayReader({
2162 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
2166 * This would consume an Array like this:
2168 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2172 * Create a new JsonReader
2173 * @param {Object} meta Metadata configuration options.
2174 * @param {Object|Array} recordType Either an Array of field definition objects
2176 * @cfg {Array} fields Array of field definition objects
2177 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2178 * as specified to {@link Roo.data.Record#create},
2179 * or an {@link Roo.data.Record} object
2182 * created using {@link Roo.data.Record#create}.
2184 Roo.data.ArrayReader = function(meta, recordType)
2186 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2189 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2192 * Create a data block containing Roo.data.Records from an XML document.
2193 * @param {Object} o An Array of row objects which represents the dataset.
2194 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2195 * a cache of Roo.data.Records.
2197 readRecords : function(o)
2199 var sid = this.meta ? this.meta.id : null;
2200 var recordType = this.recordType, fields = recordType.prototype.fields;
2203 for(var i = 0; i < root.length; i++){
2206 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2207 for(var j = 0, jlen = fields.length; j < jlen; j++){
2208 var f = fields.items[j];
2209 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2210 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2214 var record = new recordType(values, id);
2216 records[records.length] = record;
2220 totalRecords : records.length
2224 * using 'cn' the nested child reader read the child array into it's child stores.
2225 * @param {Object} rec The record with a 'children array
2227 toLoadData: function(rec)
2229 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2230 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2237 * Ext JS Library 1.1.1
2238 * Copyright(c) 2006-2007, Ext JS, LLC.
2240 * Originally Released Under LGPL - original licence link has changed is not relivant.
2243 * <script type="text/javascript">
2248 * @class Roo.data.Tree
2249 * @extends Roo.util.Observable
2250 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2251 * in the tree have most standard DOM functionality.
2253 * @param {Node} root (optional) The root node
2255 Roo.data.Tree = function(root){
2258 * The root node for this tree
2263 this.setRootNode(root);
2268 * Fires when a new child node is appended to a node in this tree.
2269 * @param {Tree} tree The owner tree
2270 * @param {Node} parent The parent node
2271 * @param {Node} node The newly appended node
2272 * @param {Number} index The index of the newly appended node
2277 * Fires when a child node is removed from a node in this tree.
2278 * @param {Tree} tree The owner tree
2279 * @param {Node} parent The parent node
2280 * @param {Node} node The child node removed
2285 * Fires when a node is moved to a new location in the tree
2286 * @param {Tree} tree The owner tree
2287 * @param {Node} node The node moved
2288 * @param {Node} oldParent The old parent of this node
2289 * @param {Node} newParent The new parent of this node
2290 * @param {Number} index The index it was moved to
2295 * Fires when a new child node is inserted in a node in this tree.
2296 * @param {Tree} tree The owner tree
2297 * @param {Node} parent The parent node
2298 * @param {Node} node The child node inserted
2299 * @param {Node} refNode The child node the node was inserted before
2303 * @event beforeappend
2304 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2305 * @param {Tree} tree The owner tree
2306 * @param {Node} parent The parent node
2307 * @param {Node} node The child node to be appended
2309 "beforeappend" : true,
2311 * @event beforeremove
2312 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2313 * @param {Tree} tree The owner tree
2314 * @param {Node} parent The parent node
2315 * @param {Node} node The child node to be removed
2317 "beforeremove" : true,
2320 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2321 * @param {Tree} tree The owner tree
2322 * @param {Node} node The node being moved
2323 * @param {Node} oldParent The parent of the node
2324 * @param {Node} newParent The new parent the node is moving to
2325 * @param {Number} index The index it is being moved to
2327 "beforemove" : true,
2329 * @event beforeinsert
2330 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2331 * @param {Tree} tree The owner tree
2332 * @param {Node} parent The parent node
2333 * @param {Node} node The child node to be inserted
2334 * @param {Node} refNode The child node the node is being inserted before
2336 "beforeinsert" : true
2339 Roo.data.Tree.superclass.constructor.call(this);
2342 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2345 proxyNodeEvent : function(){
2346 return this.fireEvent.apply(this, arguments);
2350 * Returns the root node for this tree.
2353 getRootNode : function(){
2358 * Sets the root node for this tree.
2359 * @param {Node} node
2362 setRootNode : function(node){
2364 node.ownerTree = this;
2366 this.registerNode(node);
2371 * Gets a node in this tree by its id.
2372 * @param {String} id
2375 getNodeById : function(id){
2376 return this.nodeHash[id];
2379 registerNode : function(node){
2380 this.nodeHash[node.id] = node;
2383 unregisterNode : function(node){
2384 delete this.nodeHash[node.id];
2387 toString : function(){
2388 return "[Tree"+(this.id?" "+this.id:"")+"]";
2393 * @class Roo.data.Node
2394 * @extends Roo.util.Observable
2395 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2396 * @cfg {String} id The id for this node. If one is not specified, one is generated.
2398 * @param {Object} attributes The attributes/config for the node
2400 Roo.data.Node = function(attributes){
2402 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2405 this.attributes = attributes || {};
2406 this.leaf = this.attributes.leaf;
2408 * The node id. @type String
2410 this.id = this.attributes.id;
2412 this.id = Roo.id(null, "ynode-");
2413 this.attributes.id = this.id;
2418 * All child nodes of this node. @type Array
2420 this.childNodes = [];
2421 if(!this.childNodes.indexOf){ // indexOf is a must
2422 this.childNodes.indexOf = function(o){
2423 for(var i = 0, len = this.length; i < len; i++){
2432 * The parent node for this node. @type Node
2434 this.parentNode = null;
2436 * The first direct child node of this node, or null if this node has no child nodes. @type Node
2438 this.firstChild = null;
2440 * The last direct child node of this node, or null if this node has no child nodes. @type Node
2442 this.lastChild = null;
2444 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2446 this.previousSibling = null;
2448 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2450 this.nextSibling = null;
2455 * Fires when a new child node is appended
2456 * @param {Tree} tree The owner tree
2457 * @param {Node} this This node
2458 * @param {Node} node The newly appended node
2459 * @param {Number} index The index of the newly appended node
2464 * Fires when a child node is removed
2465 * @param {Tree} tree The owner tree
2466 * @param {Node} this This node
2467 * @param {Node} node The removed node
2472 * Fires when this node is moved to a new location in the tree
2473 * @param {Tree} tree The owner tree
2474 * @param {Node} this This node
2475 * @param {Node} oldParent The old parent of this node
2476 * @param {Node} newParent The new parent of this node
2477 * @param {Number} index The index it was moved to
2482 * Fires when a new child node is inserted.
2483 * @param {Tree} tree The owner tree
2484 * @param {Node} this This node
2485 * @param {Node} node The child node inserted
2486 * @param {Node} refNode The child node the node was inserted before
2490 * @event beforeappend
2491 * Fires before a new child is appended, return false to cancel the append.
2492 * @param {Tree} tree The owner tree
2493 * @param {Node} this This node
2494 * @param {Node} node The child node to be appended
2496 "beforeappend" : true,
2498 * @event beforeremove
2499 * Fires before a child is removed, return false to cancel the remove.
2500 * @param {Tree} tree The owner tree
2501 * @param {Node} this This node
2502 * @param {Node} node The child node to be removed
2504 "beforeremove" : true,
2507 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2508 * @param {Tree} tree The owner tree
2509 * @param {Node} this This node
2510 * @param {Node} oldParent The parent of this node
2511 * @param {Node} newParent The new parent this node is moving to
2512 * @param {Number} index The index it is being moved to
2514 "beforemove" : true,
2516 * @event beforeinsert
2517 * Fires before a new child is inserted, return false to cancel the insert.
2518 * @param {Tree} tree The owner tree
2519 * @param {Node} this This node
2520 * @param {Node} node The child node to be inserted
2521 * @param {Node} refNode The child node the node is being inserted before
2523 "beforeinsert" : true
2525 this.listeners = this.attributes.listeners;
2526 Roo.data.Node.superclass.constructor.call(this);
2529 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2530 fireEvent : function(evtName){
2531 // first do standard event for this node
2532 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2535 // then bubble it up to the tree if the event wasn't cancelled
2536 var ot = this.getOwnerTree();
2538 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2546 * Returns true if this node is a leaf
2549 isLeaf : function(){
2550 return this.leaf === true;
2554 setFirstChild : function(node){
2555 this.firstChild = node;
2559 setLastChild : function(node){
2560 this.lastChild = node;
2565 * Returns true if this node is the last child of its parent
2568 isLast : function(){
2569 return (!this.parentNode ? true : this.parentNode.lastChild == this);
2573 * Returns true if this node is the first child of its parent
2576 isFirst : function(){
2577 return (!this.parentNode ? true : this.parentNode.firstChild == this);
2580 hasChildNodes : function(){
2581 return !this.isLeaf() && this.childNodes.length > 0;
2585 * Insert node(s) as the last child node of this node.
2586 * @param {Node/Array} node The node or Array of nodes to append
2587 * @return {Node} The appended node if single append, or null if an array was passed
2589 appendChild : function(node){
2591 if(node instanceof Array){
2593 }else if(arguments.length > 1){
2597 // if passed an array or multiple args do them one by one
2599 for(var i = 0, len = multi.length; i < len; i++) {
2600 this.appendChild(multi[i]);
2603 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2606 var index = this.childNodes.length;
2607 var oldParent = node.parentNode;
2608 // it's a move, make sure we move it cleanly
2610 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2613 oldParent.removeChild(node);
2616 index = this.childNodes.length;
2618 this.setFirstChild(node);
2620 this.childNodes.push(node);
2621 node.parentNode = this;
2622 var ps = this.childNodes[index-1];
2624 node.previousSibling = ps;
2625 ps.nextSibling = node;
2627 node.previousSibling = null;
2629 node.nextSibling = null;
2630 this.setLastChild(node);
2631 node.setOwnerTree(this.getOwnerTree());
2632 this.fireEvent("append", this.ownerTree, this, node, index);
2633 if(this.ownerTree) {
2634 this.ownerTree.fireEvent("appendnode", this, node, index);
2637 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2644 * Removes a child node from this node.
2645 * @param {Node} node The node to remove
2646 * @return {Node} The removed node
2648 removeChild : function(node){
2649 var index = this.childNodes.indexOf(node);
2653 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2657 // remove it from childNodes collection
2658 this.childNodes.splice(index, 1);
2661 if(node.previousSibling){
2662 node.previousSibling.nextSibling = node.nextSibling;
2664 if(node.nextSibling){
2665 node.nextSibling.previousSibling = node.previousSibling;
2668 // update child refs
2669 if(this.firstChild == node){
2670 this.setFirstChild(node.nextSibling);
2672 if(this.lastChild == node){
2673 this.setLastChild(node.previousSibling);
2676 node.setOwnerTree(null);
2677 // clear any references from the node
2678 node.parentNode = null;
2679 node.previousSibling = null;
2680 node.nextSibling = null;
2681 this.fireEvent("remove", this.ownerTree, this, node);
2686 * Inserts the first node before the second node in this nodes childNodes collection.
2687 * @param {Node} node The node to insert
2688 * @param {Node} refNode The node to insert before (if null the node is appended)
2689 * @return {Node} The inserted node
2691 insertBefore : function(node, refNode){
2692 if(!refNode){ // like standard Dom, refNode can be null for append
2693 return this.appendChild(node);
2696 if(node == refNode){
2700 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2703 var index = this.childNodes.indexOf(refNode);
2704 var oldParent = node.parentNode;
2705 var refIndex = index;
2707 // when moving internally, indexes will change after remove
2708 if(oldParent == this && this.childNodes.indexOf(node) < index){
2712 // it's a move, make sure we move it cleanly
2714 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2717 oldParent.removeChild(node);
2720 this.setFirstChild(node);
2722 this.childNodes.splice(refIndex, 0, node);
2723 node.parentNode = this;
2724 var ps = this.childNodes[refIndex-1];
2726 node.previousSibling = ps;
2727 ps.nextSibling = node;
2729 node.previousSibling = null;
2731 node.nextSibling = refNode;
2732 refNode.previousSibling = node;
2733 node.setOwnerTree(this.getOwnerTree());
2734 this.fireEvent("insert", this.ownerTree, this, node, refNode);
2736 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2742 * Returns the child node at the specified index.
2743 * @param {Number} index
2746 item : function(index){
2747 return this.childNodes[index];
2751 * Replaces one child node in this node with another.
2752 * @param {Node} newChild The replacement node
2753 * @param {Node} oldChild The node to replace
2754 * @return {Node} The replaced node
2756 replaceChild : function(newChild, oldChild){
2757 this.insertBefore(newChild, oldChild);
2758 this.removeChild(oldChild);
2763 * Returns the index of a child node
2764 * @param {Node} node
2765 * @return {Number} The index of the node or -1 if it was not found
2767 indexOf : function(child){
2768 return this.childNodes.indexOf(child);
2772 * Returns the tree this node is in.
2775 getOwnerTree : function(){
2776 // if it doesn't have one, look for one
2777 if(!this.ownerTree){
2781 this.ownerTree = p.ownerTree;
2787 return this.ownerTree;
2791 * Returns depth of this node (the root node has a depth of 0)
2794 getDepth : function(){
2797 while(p.parentNode){
2805 setOwnerTree : function(tree){
2806 // if it's move, we need to update everyone
2807 if(tree != this.ownerTree){
2809 this.ownerTree.unregisterNode(this);
2811 this.ownerTree = tree;
2812 var cs = this.childNodes;
2813 for(var i = 0, len = cs.length; i < len; i++) {
2814 cs[i].setOwnerTree(tree);
2817 tree.registerNode(this);
2823 * Returns the path for this node. The path can be used to expand or select this node programmatically.
2824 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2825 * @return {String} The path
2827 getPath : function(attr){
2828 attr = attr || "id";
2829 var p = this.parentNode;
2830 var b = [this.attributes[attr]];
2832 b.unshift(p.attributes[attr]);
2835 var sep = this.getOwnerTree().pathSeparator;
2836 return sep + b.join(sep);
2840 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2841 * function call will be the scope provided or the current node. The arguments to the function
2842 * will be the args provided or the current node. If the function returns false at any point,
2843 * the bubble is stopped.
2844 * @param {Function} fn The function to call
2845 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2846 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2848 bubble : function(fn, scope, args){
2851 if(fn.call(scope || p, args || p) === false){
2859 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2860 * function call will be the scope provided or the current node. The arguments to the function
2861 * will be the args provided or the current node. If the function returns false at any point,
2862 * the cascade is stopped on that branch.
2863 * @param {Function} fn The function to call
2864 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2865 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2867 cascade : function(fn, scope, args){
2868 if(fn.call(scope || this, args || this) !== false){
2869 var cs = this.childNodes;
2870 for(var i = 0, len = cs.length; i < len; i++) {
2871 cs[i].cascade(fn, scope, args);
2877 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2878 * function call will be the scope provided or the current node. The arguments to the function
2879 * will be the args provided or the current node. If the function returns false at any point,
2880 * the iteration stops.
2881 * @param {Function} fn The function to call
2882 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2883 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2885 eachChild : function(fn, scope, args){
2886 var cs = this.childNodes;
2887 for(var i = 0, len = cs.length; i < len; i++) {
2888 if(fn.call(scope || this, args || cs[i]) === false){
2895 * Finds the first child that has the attribute with the specified value.
2896 * @param {String} attribute The attribute name
2897 * @param {Mixed} value The value to search for
2898 * @return {Node} The found child or null if none was found
2900 findChild : function(attribute, value){
2901 var cs = this.childNodes;
2902 for(var i = 0, len = cs.length; i < len; i++) {
2903 if(cs[i].attributes[attribute] == value){
2911 * Finds the first child by a custom function. The child matches if the function passed
2913 * @param {Function} fn
2914 * @param {Object} scope (optional)
2915 * @return {Node} The found child or null if none was found
2917 findChildBy : function(fn, scope){
2918 var cs = this.childNodes;
2919 for(var i = 0, len = cs.length; i < len; i++) {
2920 if(fn.call(scope||cs[i], cs[i]) === true){
2928 * Sorts this nodes children using the supplied sort function
2929 * @param {Function} fn
2930 * @param {Object} scope (optional)
2932 sort : function(fn, scope){
2933 var cs = this.childNodes;
2934 var len = cs.length;
2936 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2938 for(var i = 0; i < len; i++){
2940 n.previousSibling = cs[i-1];
2941 n.nextSibling = cs[i+1];
2943 this.setFirstChild(n);
2946 this.setLastChild(n);
2953 * Returns true if this node is an ancestor (at any point) of the passed node.
2954 * @param {Node} node
2957 contains : function(node){
2958 return node.isAncestor(this);
2962 * Returns true if the passed node is an ancestor (at any point) of this node.
2963 * @param {Node} node
2966 isAncestor : function(node){
2967 var p = this.parentNode;
2977 toString : function(){
2978 return "[Node"+(this.id?" "+this.id:"")+"]";
2982 * Ext JS Library 1.1.1
2983 * Copyright(c) 2006-2007, Ext JS, LLC.
2985 * Originally Released Under LGPL - original licence link has changed is not relivant.
2988 * <script type="text/javascript">
2993 * @extends Roo.Element
2994 * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2995 * automatic maintaining of shadow/shim positions.
2996 * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2997 * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2998 * you can pass a string with a CSS class name. False turns off the shadow.
2999 * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
3000 * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
3001 * @cfg {String} cls CSS class to add to the element
3002 * @cfg {Number} zindex Starting z-index (defaults to 11000)
3003 * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
3005 * @param {Object} config An object with config options.
3006 * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
3009 Roo.Layer = function(config, existingEl){
3010 config = config || {};
3011 var dh = Roo.DomHelper;
3012 var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
3014 this.dom = Roo.getDom(existingEl);
3017 var o = config.dh || {tag: "div", cls: "x-layer"};
3018 this.dom = dh.append(pel, o);
3021 this.addClass(config.cls);
3023 this.constrain = config.constrain !== false;
3024 this.visibilityMode = Roo.Element.VISIBILITY;
3026 this.id = this.dom.id = config.id;
3028 this.id = Roo.id(this.dom);
3030 this.zindex = config.zindex || this.getZIndex();
3031 this.position("absolute", this.zindex);
3033 this.shadowOffset = config.shadowOffset || 4;
3034 this.shadow = new Roo.Shadow({
3035 offset : this.shadowOffset,
3036 mode : config.shadow
3039 this.shadowOffset = 0;
3041 this.useShim = config.shim !== false && Roo.useShims;
3042 this.useDisplay = config.useDisplay;
3046 var supr = Roo.Element.prototype;
3048 // shims are shared among layer to keep from having 100 iframes
3051 Roo.extend(Roo.Layer, Roo.Element, {
3053 getZIndex : function(){
3054 return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3057 getShim : function(){
3064 var shim = shims.shift();
3066 shim = this.createShim();
3067 shim.enableDisplayMode('block');
3068 shim.dom.style.display = 'none';
3069 shim.dom.style.visibility = 'visible';
3071 var pn = this.dom.parentNode;
3072 if(shim.dom.parentNode != pn){
3073 pn.insertBefore(shim.dom, this.dom);
3075 shim.setStyle('z-index', this.getZIndex()-2);
3080 hideShim : function(){
3082 this.shim.setDisplayed(false);
3083 shims.push(this.shim);
3088 disableShadow : function(){
3090 this.shadowDisabled = true;
3092 this.lastShadowOffset = this.shadowOffset;
3093 this.shadowOffset = 0;
3097 enableShadow : function(show){
3099 this.shadowDisabled = false;
3100 this.shadowOffset = this.lastShadowOffset;
3101 delete this.lastShadowOffset;
3109 // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3110 // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3111 sync : function(doShow){
3112 var sw = this.shadow;
3113 if(!this.updating && this.isVisible() && (sw || this.useShim)){
3114 var sh = this.getShim();
3116 var w = this.getWidth(),
3117 h = this.getHeight();
3119 var l = this.getLeft(true),
3120 t = this.getTop(true);
3122 if(sw && !this.shadowDisabled){
3123 if(doShow && !sw.isVisible()){
3126 sw.realign(l, t, w, h);
3132 // fit the shim behind the shadow, so it is shimmed too
3133 var a = sw.adjusts, s = sh.dom.style;
3134 s.left = (Math.min(l, l+a.l))+"px";
3135 s.top = (Math.min(t, t+a.t))+"px";
3136 s.width = (w+a.w)+"px";
3137 s.height = (h+a.h)+"px";
3144 sh.setLeftTop(l, t);
3151 destroy : function(){
3156 this.removeAllListeners();
3157 var pn = this.dom.parentNode;
3159 pn.removeChild(this.dom);
3161 Roo.Element.uncache(this.id);
3164 remove : function(){
3169 beginUpdate : function(){
3170 this.updating = true;
3174 endUpdate : function(){
3175 this.updating = false;
3180 hideUnders : function(negOffset){
3188 constrainXY : function(){
3190 var vw = Roo.lib.Dom.getViewWidth(),
3191 vh = Roo.lib.Dom.getViewHeight();
3192 var s = Roo.get(document).getScroll();
3194 var xy = this.getXY();
3195 var x = xy[0], y = xy[1];
3196 var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3197 // only move it if it needs it
3199 // first validate right/bottom
3200 if((x + w) > vw+s.left){
3201 x = vw - w - this.shadowOffset;
3204 if((y + h) > vh+s.top){
3205 y = vh - h - this.shadowOffset;
3208 // then make sure top/left isn't negative
3219 var ay = this.avoidY;
3220 if(y <= ay && (y+h) >= ay){
3226 supr.setXY.call(this, xy);
3232 isVisible : function(){
3233 return this.visible;
3237 showAction : function(){
3238 this.visible = true; // track visibility to prevent getStyle calls
3239 if(this.useDisplay === true){
3240 this.setDisplayed("");
3241 }else if(this.lastXY){
3242 supr.setXY.call(this, this.lastXY);
3243 }else if(this.lastLT){
3244 supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3249 hideAction : function(){
3250 this.visible = false;
3251 if(this.useDisplay === true){
3252 this.setDisplayed(false);
3254 this.setLeftTop(-10000,-10000);
3258 // overridden Element method
3259 setVisible : function(v, a, d, c, e){
3264 var cb = function(){
3269 }.createDelegate(this);
3270 supr.setVisible.call(this, true, true, d, cb, e);
3273 this.hideUnders(true);
3282 }.createDelegate(this);
3284 supr.setVisible.call(this, v, a, d, cb, e);
3293 storeXY : function(xy){
3298 storeLeftTop : function(left, top){
3300 this.lastLT = [left, top];
3304 beforeFx : function(){
3305 this.beforeAction();
3306 return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3310 afterFx : function(){
3311 Roo.Layer.superclass.afterFx.apply(this, arguments);
3312 this.sync(this.isVisible());
3316 beforeAction : function(){
3317 if(!this.updating && this.shadow){
3322 // overridden Element method
3323 setLeft : function(left){
3324 this.storeLeftTop(left, this.getTop(true));
3325 supr.setLeft.apply(this, arguments);
3329 setTop : function(top){
3330 this.storeLeftTop(this.getLeft(true), top);
3331 supr.setTop.apply(this, arguments);
3335 setLeftTop : function(left, top){
3336 this.storeLeftTop(left, top);
3337 supr.setLeftTop.apply(this, arguments);
3341 setXY : function(xy, a, d, c, e){
3343 this.beforeAction();
3345 var cb = this.createCB(c);
3346 supr.setXY.call(this, xy, a, d, cb, e);
3353 createCB : function(c){
3364 // overridden Element method
3365 setX : function(x, a, d, c, e){
3366 this.setXY([x, this.getY()], a, d, c, e);
3369 // overridden Element method
3370 setY : function(y, a, d, c, e){
3371 this.setXY([this.getX(), y], a, d, c, e);
3374 // overridden Element method
3375 setSize : function(w, h, a, d, c, e){
3376 this.beforeAction();
3377 var cb = this.createCB(c);
3378 supr.setSize.call(this, w, h, a, d, cb, e);
3384 // overridden Element method
3385 setWidth : function(w, a, d, c, e){
3386 this.beforeAction();
3387 var cb = this.createCB(c);
3388 supr.setWidth.call(this, w, a, d, cb, e);
3394 // overridden Element method
3395 setHeight : function(h, a, d, c, e){
3396 this.beforeAction();
3397 var cb = this.createCB(c);
3398 supr.setHeight.call(this, h, a, d, cb, e);
3404 // overridden Element method
3405 setBounds : function(x, y, w, h, a, d, c, e){
3406 this.beforeAction();
3407 var cb = this.createCB(c);
3409 this.storeXY([x, y]);
3410 supr.setXY.call(this, [x, y]);
3411 supr.setSize.call(this, w, h, a, d, cb, e);
3414 supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3420 * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3421 * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3422 * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3423 * @param {Number} zindex The new z-index to set
3424 * @return {this} The Layer
3426 setZIndex : function(zindex){
3427 this.zindex = zindex;
3428 this.setStyle("z-index", zindex + 2);
3430 this.shadow.setZIndex(zindex + 1);
3433 this.shim.setStyle("z-index", zindex);
3439 * Ext JS Library 1.1.1
3440 * Copyright(c) 2006-2007, Ext JS, LLC.
3442 * Originally Released Under LGPL - original licence link has changed is not relivant.
3445 * <script type="text/javascript">
3451 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
3452 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
3453 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3455 * Create a new Shadow
3456 * @param {Object} config The config object
3458 Roo.Shadow = function(config){
3459 Roo.apply(this, config);
3460 if(typeof this.mode != "string"){
3461 this.mode = this.defaultMode;
3463 var o = this.offset, a = {h: 0};
3464 var rad = Math.floor(this.offset/2);
3465 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3471 a.l -= this.offset + rad;
3472 a.t -= this.offset + rad;
3483 a.l -= (this.offset - rad);
3484 a.t -= this.offset + rad;
3486 a.w -= (this.offset - rad)*2;
3497 a.l -= (this.offset - rad);
3498 a.t -= (this.offset - rad);
3500 a.w -= (this.offset + rad + 1);
3501 a.h -= (this.offset + rad);
3510 Roo.Shadow.prototype = {
3512 * @cfg {String} mode
3513 * The shadow display mode. Supports the following options:<br />
3514 * sides: Shadow displays on both sides and bottom only<br />
3515 * frame: Shadow displays equally on all four sides<br />
3516 * drop: Traditional bottom-right drop shadow (default)
3519 * @cfg {String} offset
3520 * The number of pixels to offset the shadow from the element (defaults to 4)
3525 defaultMode: "drop",
3528 * Displays the shadow under the target element
3529 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3531 show : function(target){
3532 target = Roo.get(target);
3534 this.el = Roo.Shadow.Pool.pull();
3535 if(this.el.dom.nextSibling != target.dom){
3536 this.el.insertBefore(target);
3539 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3541 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3544 target.getLeft(true),
3545 target.getTop(true),
3549 this.el.dom.style.display = "block";
3553 * Returns true if the shadow is visible, else false
3555 isVisible : function(){
3556 return this.el ? true : false;
3560 * Direct alignment when values are already available. Show must be called at least once before
3561 * calling this method to ensure it is initialized.
3562 * @param {Number} left The target element left position
3563 * @param {Number} top The target element top position
3564 * @param {Number} width The target element width
3565 * @param {Number} height The target element height
3567 realign : function(l, t, w, h){
3571 var a = this.adjusts, d = this.el.dom, s = d.style;
3573 s.left = (l+a.l)+"px";
3574 s.top = (t+a.t)+"px";
3575 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3577 if(s.width != sws || s.height != shs){
3581 var cn = d.childNodes;
3582 var sww = Math.max(0, (sw-12))+"px";
3583 cn[0].childNodes[1].style.width = sww;
3584 cn[1].childNodes[1].style.width = sww;
3585 cn[2].childNodes[1].style.width = sww;
3586 cn[1].style.height = Math.max(0, (sh-12))+"px";
3596 this.el.dom.style.display = "none";
3597 Roo.Shadow.Pool.push(this.el);
3603 * Adjust the z-index of this shadow
3604 * @param {Number} zindex The new z-index
3606 setZIndex : function(z){
3609 this.el.setStyle("z-index", z);
3614 // Private utility class that manages the internal Shadow cache
3615 Roo.Shadow.Pool = function(){
3617 var markup = Roo.isIE ?
3618 '<div class="x-ie-shadow"></div>' :
3619 '<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>';
3624 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3625 sh.autoBoxAdjust = false;
3630 push : function(sh){
3636 * Ext JS Library 1.1.1
3637 * Copyright(c) 2006-2007, Ext JS, LLC.
3639 * Originally Released Under LGPL - original licence link has changed is not relivant.
3642 * <script type="text/javascript">
3647 * @class Roo.SplitBar
3648 * @extends Roo.util.Observable
3649 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3653 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3654 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3655 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3656 split.minSize = 100;
3657 split.maxSize = 600;
3658 split.animate = true;
3659 split.on('moved', splitterMoved);
3662 * Create a new SplitBar
3663 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
3664 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
3665 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3666 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
3667 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3668 position of the SplitBar).
3670 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3673 this.el = Roo.get(dragElement, true);
3674 this.el.dom.unselectable = "on";
3676 this.resizingEl = Roo.get(resizingElement, true);
3680 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3681 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3684 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3687 * The minimum size of the resizing element. (Defaults to 0)
3693 * The maximum size of the resizing element. (Defaults to 2000)
3696 this.maxSize = 2000;
3699 * Whether to animate the transition to the new size
3702 this.animate = false;
3705 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3708 this.useShim = false;
3715 this.proxy = Roo.SplitBar.createProxy(this.orientation);
3717 this.proxy = Roo.get(existingProxy).dom;
3720 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3723 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3726 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3729 this.dragSpecs = {};
3732 * @private The adapter to use to positon and resize elements
3734 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3735 this.adapter.init(this);
3737 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3739 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3740 this.el.addClass("x-splitbar-h");
3743 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3744 this.el.addClass("x-splitbar-v");
3750 * Fires when the splitter is moved (alias for {@link #event-moved})
3751 * @param {Roo.SplitBar} this
3752 * @param {Number} newSize the new width or height
3757 * Fires when the splitter is moved
3758 * @param {Roo.SplitBar} this
3759 * @param {Number} newSize the new width or height
3763 * @event beforeresize
3764 * Fires before the splitter is dragged
3765 * @param {Roo.SplitBar} this
3767 "beforeresize" : true,
3769 "beforeapply" : true
3772 Roo.util.Observable.call(this);
3775 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3776 onStartProxyDrag : function(x, y){
3777 this.fireEvent("beforeresize", this);
3779 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
3781 o.enableDisplayMode("block");
3782 // all splitbars share the same overlay
3783 Roo.SplitBar.prototype.overlay = o;
3785 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3786 this.overlay.show();
3787 Roo.get(this.proxy).setDisplayed("block");
3788 var size = this.adapter.getElementSize(this);
3789 this.activeMinSize = this.getMinimumSize();;
3790 this.activeMaxSize = this.getMaximumSize();;
3791 var c1 = size - this.activeMinSize;
3792 var c2 = Math.max(this.activeMaxSize - size, 0);
3793 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3794 this.dd.resetConstraints();
3795 this.dd.setXConstraint(
3796 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
3797 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3799 this.dd.setYConstraint(0, 0);
3801 this.dd.resetConstraints();
3802 this.dd.setXConstraint(0, 0);
3803 this.dd.setYConstraint(
3804 this.placement == Roo.SplitBar.TOP ? c1 : c2,
3805 this.placement == Roo.SplitBar.TOP ? c2 : c1
3808 this.dragSpecs.startSize = size;
3809 this.dragSpecs.startPoint = [x, y];
3810 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3814 * @private Called after the drag operation by the DDProxy
3816 onEndProxyDrag : function(e){
3817 Roo.get(this.proxy).setDisplayed(false);
3818 var endPoint = Roo.lib.Event.getXY(e);
3820 this.overlay.hide();
3823 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3824 newSize = this.dragSpecs.startSize +
3825 (this.placement == Roo.SplitBar.LEFT ?
3826 endPoint[0] - this.dragSpecs.startPoint[0] :
3827 this.dragSpecs.startPoint[0] - endPoint[0]
3830 newSize = this.dragSpecs.startSize +
3831 (this.placement == Roo.SplitBar.TOP ?
3832 endPoint[1] - this.dragSpecs.startPoint[1] :
3833 this.dragSpecs.startPoint[1] - endPoint[1]
3836 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3837 if(newSize != this.dragSpecs.startSize){
3838 if(this.fireEvent('beforeapply', this, newSize) !== false){
3839 this.adapter.setElementSize(this, newSize);
3840 this.fireEvent("moved", this, newSize);
3841 this.fireEvent("resize", this, newSize);
3847 * Get the adapter this SplitBar uses
3848 * @return The adapter object
3850 getAdapter : function(){
3851 return this.adapter;
3855 * Set the adapter this SplitBar uses
3856 * @param {Object} adapter A SplitBar adapter object
3858 setAdapter : function(adapter){
3859 this.adapter = adapter;
3860 this.adapter.init(this);
3864 * Gets the minimum size for the resizing element
3865 * @return {Number} The minimum size
3867 getMinimumSize : function(){
3868 return this.minSize;
3872 * Sets the minimum size for the resizing element
3873 * @param {Number} minSize The minimum size
3875 setMinimumSize : function(minSize){
3876 this.minSize = minSize;
3880 * Gets the maximum size for the resizing element
3881 * @return {Number} The maximum size
3883 getMaximumSize : function(){
3884 return this.maxSize;
3888 * Sets the maximum size for the resizing element
3889 * @param {Number} maxSize The maximum size
3891 setMaximumSize : function(maxSize){
3892 this.maxSize = maxSize;
3896 * Sets the initialize size for the resizing element
3897 * @param {Number} size The initial size
3899 setCurrentSize : function(size){
3900 var oldAnimate = this.animate;
3901 this.animate = false;
3902 this.adapter.setElementSize(this, size);
3903 this.animate = oldAnimate;
3907 * Destroy this splitbar.
3908 * @param {Boolean} removeEl True to remove the element
3910 destroy : function(removeEl){
3915 this.proxy.parentNode.removeChild(this.proxy);
3923 * @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.
3925 Roo.SplitBar.createProxy = function(dir){
3926 var proxy = new Roo.Element(document.createElement("div"));
3927 proxy.unselectable();
3928 var cls = 'x-splitbar-proxy';
3929 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3930 document.body.appendChild(proxy.dom);
3935 * @class Roo.SplitBar.BasicLayoutAdapter
3936 * Default Adapter. It assumes the splitter and resizing element are not positioned
3937 * elements and only gets/sets the width of the element. Generally used for table based layouts.
3939 Roo.SplitBar.BasicLayoutAdapter = function(){
3942 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3943 // do nothing for now
3948 * Called before drag operations to get the current size of the resizing element.
3949 * @param {Roo.SplitBar} s The SplitBar using this adapter
3951 getElementSize : function(s){
3952 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3953 return s.resizingEl.getWidth();
3955 return s.resizingEl.getHeight();
3960 * Called after drag operations to set the size of the resizing element.
3961 * @param {Roo.SplitBar} s The SplitBar using this adapter
3962 * @param {Number} newSize The new size to set
3963 * @param {Function} onComplete A function to be invoked when resizing is complete
3965 setElementSize : function(s, newSize, onComplete){
3966 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3968 s.resizingEl.setWidth(newSize);
3970 onComplete(s, newSize);
3973 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3978 s.resizingEl.setHeight(newSize);
3980 onComplete(s, newSize);
3983 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3990 *@class Roo.SplitBar.AbsoluteLayoutAdapter
3991 * @extends Roo.SplitBar.BasicLayoutAdapter
3992 * Adapter that moves the splitter element to align with the resized sizing element.
3993 * Used with an absolute positioned SplitBar.
3994 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3995 * document.body, make sure you assign an id to the body element.
3997 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3998 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3999 this.container = Roo.get(container);
4002 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
4007 getElementSize : function(s){
4008 return this.basic.getElementSize(s);
4011 setElementSize : function(s, newSize, onComplete){
4012 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
4015 moveSplitter : function(s){
4016 var yes = Roo.SplitBar;
4017 switch(s.placement){
4019 s.el.setX(s.resizingEl.getRight());
4022 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
4025 s.el.setY(s.resizingEl.getBottom());
4028 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
4035 * Orientation constant - Create a vertical SplitBar
4039 Roo.SplitBar.VERTICAL = 1;
4042 * Orientation constant - Create a horizontal SplitBar
4046 Roo.SplitBar.HORIZONTAL = 2;
4049 * Placement constant - The resizing element is to the left of the splitter element
4053 Roo.SplitBar.LEFT = 1;
4056 * Placement constant - The resizing element is to the right of the splitter element
4060 Roo.SplitBar.RIGHT = 2;
4063 * Placement constant - The resizing element is positioned above the splitter element
4067 Roo.SplitBar.TOP = 3;
4070 * Placement constant - The resizing element is positioned under splitter element
4074 Roo.SplitBar.BOTTOM = 4;
4077 * Ext JS Library 1.1.1
4078 * Copyright(c) 2006-2007, Ext JS, LLC.
4080 * Originally Released Under LGPL - original licence link has changed is not relivant.
4083 * <script type="text/javascript">
4088 * @extends Roo.util.Observable
4089 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
4090 * This class also supports single and multi selection modes. <br>
4091 * Create a data model bound view:
4093 var store = new Roo.data.Store(...);
4095 var view = new Roo.View({
4097 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
4100 selectedClass: "ydataview-selected",
4104 // listen for node click?
4105 view.on("click", function(vw, index, node, e){
4106 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4110 dataModel.load("foobar.xml");
4112 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4114 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4115 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4117 * Note: old style constructor is still suported (container, template, config)
4121 * @param {Object} config The config object
4124 Roo.View = function(config, depreciated_tpl, depreciated_config){
4126 this.parent = false;
4128 if (typeof(depreciated_tpl) == 'undefined') {
4129 // new way.. - universal constructor.
4130 Roo.apply(this, config);
4131 this.el = Roo.get(this.el);
4134 this.el = Roo.get(config);
4135 this.tpl = depreciated_tpl;
4136 Roo.apply(this, depreciated_config);
4138 this.wrapEl = this.el.wrap().wrap();
4139 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4142 if(typeof(this.tpl) == "string"){
4143 this.tpl = new Roo.Template(this.tpl);
4145 // support xtype ctors..
4146 this.tpl = new Roo.factory(this.tpl, Roo);
4155 * @event beforeclick
4156 * Fires before a click is processed. Returns false to cancel the default action.
4157 * @param {Roo.View} this
4158 * @param {Number} index The index of the target node
4159 * @param {HTMLElement} node The target node
4160 * @param {Roo.EventObject} e The raw event object
4162 "beforeclick" : true,
4165 * Fires when a template node is clicked.
4166 * @param {Roo.View} this
4167 * @param {Number} index The index of the target node
4168 * @param {HTMLElement} node The target node
4169 * @param {Roo.EventObject} e The raw event object
4174 * Fires when a template node is double clicked.
4175 * @param {Roo.View} this
4176 * @param {Number} index The index of the target node
4177 * @param {HTMLElement} node The target node
4178 * @param {Roo.EventObject} e The raw event object
4182 * @event contextmenu
4183 * Fires when a template node is right clicked.
4184 * @param {Roo.View} this
4185 * @param {Number} index The index of the target node
4186 * @param {HTMLElement} node The target node
4187 * @param {Roo.EventObject} e The raw event object
4189 "contextmenu" : true,
4191 * @event selectionchange
4192 * Fires when the selected nodes change.
4193 * @param {Roo.View} this
4194 * @param {Array} selections Array of the selected nodes
4196 "selectionchange" : true,
4199 * @event beforeselect
4200 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4201 * @param {Roo.View} this
4202 * @param {HTMLElement} node The node to be selected
4203 * @param {Array} selections Array of currently selected nodes
4205 "beforeselect" : true,
4207 * @event preparedata
4208 * Fires on every row to render, to allow you to change the data.
4209 * @param {Roo.View} this
4210 * @param {Object} data to be rendered (change this)
4212 "preparedata" : true
4220 "click": this.onClick,
4221 "dblclick": this.onDblClick,
4222 "contextmenu": this.onContextMenu,
4226 this.selections = [];
4228 this.cmp = new Roo.CompositeElementLite([]);
4230 this.store = Roo.factory(this.store, Roo.data);
4231 this.setStore(this.store, true);
4234 if ( this.footer && this.footer.xtype) {
4236 var fctr = this.wrapEl.appendChild(document.createElement("div"));
4238 this.footer.dataSource = this.store;
4239 this.footer.container = fctr;
4240 this.footer = Roo.factory(this.footer, Roo);
4241 fctr.insertFirst(this.el);
4243 // this is a bit insane - as the paging toolbar seems to detach the el..
4244 // dom.parentNode.parentNode.parentNode
4245 // they get detached?
4249 Roo.View.superclass.constructor.call(this);
4254 Roo.extend(Roo.View, Roo.util.Observable, {
4257 * @cfg {Roo.data.Store} store Data store to load data from.
4262 * @cfg {String|Roo.Element} el The container element.
4267 * @cfg {String|Roo.Template} tpl The template used by this View
4271 * @cfg {String} dataName the named area of the template to use as the data area
4272 * Works with domtemplates roo-name="name"
4276 * @cfg {String} selectedClass The css class to add to selected nodes
4278 selectedClass : "x-view-selected",
4280 * @cfg {String} emptyText The empty text to show when nothing is loaded.
4285 * @cfg {String} text to display on mask (default Loading)
4289 * @cfg {Boolean} multiSelect Allow multiple selection
4291 multiSelect : false,
4293 * @cfg {Boolean} singleSelect Allow single selection
4295 singleSelect: false,
4298 * @cfg {Boolean} toggleSelect - selecting
4300 toggleSelect : false,
4303 * @cfg {Boolean} tickable - selecting
4308 * Returns the element this view is bound to.
4309 * @return {Roo.Element}
4318 * Refreshes the view. - called by datachanged on the store. - do not call directly.
4320 refresh : function(){
4321 //Roo.log('refresh');
4324 // if we are using something like 'domtemplate', then
4325 // the what gets used is:
4326 // t.applySubtemplate(NAME, data, wrapping data..)
4327 // the outer template then get' applied with
4328 // the store 'extra data'
4329 // and the body get's added to the
4330 // roo-name="data" node?
4331 // <span class='roo-tpl-{name}'></span> ?????
4335 this.clearSelections();
4338 var records = this.store.getRange();
4339 if(records.length < 1) {
4341 // is this valid?? = should it render a template??
4343 this.el.update(this.emptyText);
4347 if (this.dataName) {
4348 this.el.update(t.apply(this.store.meta)); //????
4349 el = this.el.child('.roo-tpl-' + this.dataName);
4352 for(var i = 0, len = records.length; i < len; i++){
4353 var data = this.prepareData(records[i].data, i, records[i]);
4354 this.fireEvent("preparedata", this, data, i, records[i]);
4356 var d = Roo.apply({}, data);
4359 Roo.apply(d, {'roo-id' : Roo.id()});
4363 Roo.each(this.parent.item, function(item){
4364 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4367 Roo.apply(d, {'roo-data-checked' : 'checked'});
4371 html[html.length] = Roo.util.Format.trim(
4373 t.applySubtemplate(this.dataName, d, this.store.meta) :
4380 el.update(html.join(""));
4381 this.nodes = el.dom.childNodes;
4382 this.updateIndexes(0);
4387 * Function to override to reformat the data that is sent to
4388 * the template for each node.
4389 * DEPRICATED - use the preparedata event handler.
4390 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4391 * a JSON object for an UpdateManager bound view).
4393 prepareData : function(data, index, record)
4395 this.fireEvent("preparedata", this, data, index, record);
4399 onUpdate : function(ds, record){
4400 // Roo.log('on update');
4401 this.clearSelections();
4402 var index = this.store.indexOf(record);
4403 var n = this.nodes[index];
4404 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4405 n.parentNode.removeChild(n);
4406 this.updateIndexes(index, index);
4412 onAdd : function(ds, records, index)
4414 //Roo.log(['on Add', ds, records, index] );
4415 this.clearSelections();
4416 if(this.nodes.length == 0){
4420 var n = this.nodes[index];
4421 for(var i = 0, len = records.length; i < len; i++){
4422 var d = this.prepareData(records[i].data, i, records[i]);
4424 this.tpl.insertBefore(n, d);
4427 this.tpl.append(this.el, d);
4430 this.updateIndexes(index);
4433 onRemove : function(ds, record, index){
4434 // Roo.log('onRemove');
4435 this.clearSelections();
4436 var el = this.dataName ?
4437 this.el.child('.roo-tpl-' + this.dataName) :
4440 el.dom.removeChild(this.nodes[index]);
4441 this.updateIndexes(index);
4445 * Refresh an individual node.
4446 * @param {Number} index
4448 refreshNode : function(index){
4449 this.onUpdate(this.store, this.store.getAt(index));
4452 updateIndexes : function(startIndex, endIndex){
4453 var ns = this.nodes;
4454 startIndex = startIndex || 0;
4455 endIndex = endIndex || ns.length - 1;
4456 for(var i = startIndex; i <= endIndex; i++){
4457 ns[i].nodeIndex = i;
4462 * Changes the data store this view uses and refresh the view.
4463 * @param {Store} store
4465 setStore : function(store, initial){
4466 if(!initial && this.store){
4467 this.store.un("datachanged", this.refresh);
4468 this.store.un("add", this.onAdd);
4469 this.store.un("remove", this.onRemove);
4470 this.store.un("update", this.onUpdate);
4471 this.store.un("clear", this.refresh);
4472 this.store.un("beforeload", this.onBeforeLoad);
4473 this.store.un("load", this.onLoad);
4474 this.store.un("loadexception", this.onLoad);
4478 store.on("datachanged", this.refresh, this);
4479 store.on("add", this.onAdd, this);
4480 store.on("remove", this.onRemove, this);
4481 store.on("update", this.onUpdate, this);
4482 store.on("clear", this.refresh, this);
4483 store.on("beforeload", this.onBeforeLoad, this);
4484 store.on("load", this.onLoad, this);
4485 store.on("loadexception", this.onLoad, this);
4493 * onbeforeLoad - masks the loading area.
4496 onBeforeLoad : function(store,opts)
4498 //Roo.log('onBeforeLoad');
4502 this.el.mask(this.mask ? this.mask : "Loading" );
4504 onLoad : function ()
4511 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4512 * @param {HTMLElement} node
4513 * @return {HTMLElement} The template node
4515 findItemFromChild : function(node){
4516 var el = this.dataName ?
4517 this.el.child('.roo-tpl-' + this.dataName,true) :
4520 if(!node || node.parentNode == el){
4523 var p = node.parentNode;
4524 while(p && p != el){
4525 if(p.parentNode == el){
4534 onClick : function(e){
4535 var item = this.findItemFromChild(e.getTarget());
4537 var index = this.indexOf(item);
4538 if(this.onItemClick(item, index, e) !== false){
4539 this.fireEvent("click", this, index, item, e);
4542 this.clearSelections();
4547 onContextMenu : function(e){
4548 var item = this.findItemFromChild(e.getTarget());
4550 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4555 onDblClick : function(e){
4556 var item = this.findItemFromChild(e.getTarget());
4558 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4562 onItemClick : function(item, index, e)
4564 if(this.fireEvent("beforeclick", this, index, item, e) === false){
4567 if (this.toggleSelect) {
4568 var m = this.isSelected(item) ? 'unselect' : 'select';
4571 _t[m](item, true, false);
4574 if(this.multiSelect || this.singleSelect){
4575 if(this.multiSelect && e.shiftKey && this.lastSelection){
4576 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4578 this.select(item, this.multiSelect && e.ctrlKey);
4579 this.lastSelection = item;
4591 * Get the number of selected nodes.
4594 getSelectionCount : function(){
4595 return this.selections.length;
4599 * Get the currently selected nodes.
4600 * @return {Array} An array of HTMLElements
4602 getSelectedNodes : function(){
4603 return this.selections;
4607 * Get the indexes of the selected nodes.
4610 getSelectedIndexes : function(){
4611 var indexes = [], s = this.selections;
4612 for(var i = 0, len = s.length; i < len; i++){
4613 indexes.push(s[i].nodeIndex);
4619 * Clear all selections
4620 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4622 clearSelections : function(suppressEvent){
4623 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4624 this.cmp.elements = this.selections;
4625 this.cmp.removeClass(this.selectedClass);
4626 this.selections = [];
4628 this.fireEvent("selectionchange", this, this.selections);
4634 * Returns true if the passed node is selected
4635 * @param {HTMLElement/Number} node The node or node index
4638 isSelected : function(node){
4639 var s = this.selections;
4643 node = this.getNode(node);
4644 return s.indexOf(node) !== -1;
4649 * @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
4650 * @param {Boolean} keepExisting (optional) true to keep existing selections
4651 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4653 select : function(nodeInfo, keepExisting, suppressEvent){
4654 if(nodeInfo instanceof Array){
4656 this.clearSelections(true);
4658 for(var i = 0, len = nodeInfo.length; i < len; i++){
4659 this.select(nodeInfo[i], true, true);
4663 var node = this.getNode(nodeInfo);
4664 if(!node || this.isSelected(node)){
4665 return; // already selected.
4668 this.clearSelections(true);
4671 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4672 Roo.fly(node).addClass(this.selectedClass);
4673 this.selections.push(node);
4675 this.fireEvent("selectionchange", this, this.selections);
4683 * @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
4684 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4685 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4687 unselect : function(nodeInfo, keepExisting, suppressEvent)
4689 if(nodeInfo instanceof Array){
4690 Roo.each(this.selections, function(s) {
4691 this.unselect(s, nodeInfo);
4695 var node = this.getNode(nodeInfo);
4696 if(!node || !this.isSelected(node)){
4697 //Roo.log("not selected");
4698 return; // not selected.
4702 Roo.each(this.selections, function(s) {
4704 Roo.fly(node).removeClass(this.selectedClass);
4711 this.selections= ns;
4712 this.fireEvent("selectionchange", this, this.selections);
4716 * Gets a template node.
4717 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4718 * @return {HTMLElement} The node or null if it wasn't found
4720 getNode : function(nodeInfo){
4721 if(typeof nodeInfo == "string"){
4722 return document.getElementById(nodeInfo);
4723 }else if(typeof nodeInfo == "number"){
4724 return this.nodes[nodeInfo];
4730 * Gets a range template nodes.
4731 * @param {Number} startIndex
4732 * @param {Number} endIndex
4733 * @return {Array} An array of nodes
4735 getNodes : function(start, end){
4736 var ns = this.nodes;
4738 end = typeof end == "undefined" ? ns.length - 1 : end;
4741 for(var i = start; i <= end; i++){
4745 for(var i = start; i >= end; i--){
4753 * Finds the index of the passed node
4754 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4755 * @return {Number} The index of the node or -1
4757 indexOf : function(node){
4758 node = this.getNode(node);
4759 if(typeof node.nodeIndex == "number"){
4760 return node.nodeIndex;
4762 var ns = this.nodes;
4763 for(var i = 0, len = ns.length; i < len; i++){
4773 * Ext JS Library 1.1.1
4774 * Copyright(c) 2006-2007, Ext JS, LLC.
4776 * Originally Released Under LGPL - original licence link has changed is not relivant.
4779 * <script type="text/javascript">
4783 * @class Roo.JsonView
4785 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4787 var view = new Roo.JsonView({
4788 container: "my-element",
4789 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
4794 // listen for node click?
4795 view.on("click", function(vw, index, node, e){
4796 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4799 // direct load of JSON data
4800 view.load("foobar.php");
4802 // Example from my blog list
4803 var tpl = new Roo.Template(
4804 '<div class="entry">' +
4805 '<a class="entry-title" href="{link}">{title}</a>' +
4806 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
4807 "</div><hr />"
4810 var moreView = new Roo.JsonView({
4811 container : "entry-list",
4815 moreView.on("beforerender", this.sortEntries, this);
4817 url: "/blog/get-posts.php",
4818 params: "allposts=true",
4819 text: "Loading Blog Entries..."
4823 * Note: old code is supported with arguments : (container, template, config)
4827 * Create a new JsonView
4829 * @param {Object} config The config object
4832 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4835 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4837 var um = this.el.getUpdateManager();
4838 um.setRenderer(this);
4839 um.on("update", this.onLoad, this);
4840 um.on("failure", this.onLoadException, this);
4843 * @event beforerender
4844 * Fires before rendering of the downloaded JSON data.
4845 * @param {Roo.JsonView} this
4846 * @param {Object} data The JSON data loaded
4850 * Fires when data is loaded.
4851 * @param {Roo.JsonView} this
4852 * @param {Object} data The JSON data loaded
4853 * @param {Object} response The raw Connect response object
4856 * @event loadexception
4857 * Fires when loading fails.
4858 * @param {Roo.JsonView} this
4859 * @param {Object} response The raw Connect response object
4862 'beforerender' : true,
4864 'loadexception' : true
4867 Roo.extend(Roo.JsonView, Roo.View, {
4869 * @type {String} The root property in the loaded JSON object that contains the data
4874 * Refreshes the view.
4876 refresh : function(){
4877 this.clearSelections();
4880 var o = this.jsonData;
4881 if(o && o.length > 0){
4882 for(var i = 0, len = o.length; i < len; i++){
4883 var data = this.prepareData(o[i], i, o);
4884 html[html.length] = this.tpl.apply(data);
4887 html.push(this.emptyText);
4889 this.el.update(html.join(""));
4890 this.nodes = this.el.dom.childNodes;
4891 this.updateIndexes(0);
4895 * 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.
4896 * @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:
4899 url: "your-url.php",
4900 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4901 callback: yourFunction,
4902 scope: yourObject, //(optional scope)
4910 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4911 * 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.
4912 * @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}
4913 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4914 * @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.
4917 var um = this.el.getUpdateManager();
4918 um.update.apply(um, arguments);
4921 // note - render is a standard framework call...
4922 // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4923 render : function(el, response){
4925 this.clearSelections();
4929 if (response != '') {
4930 o = Roo.util.JSON.decode(response.responseText);
4933 o = o[this.jsonRoot];
4939 * The current JSON data or null
4942 this.beforeRender();
4947 * Get the number of records in the current JSON dataset
4950 getCount : function(){
4951 return this.jsonData ? this.jsonData.length : 0;
4955 * Returns the JSON object for the specified node(s)
4956 * @param {HTMLElement/Array} node The node or an array of nodes
4957 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4958 * you get the JSON object for the node
4960 getNodeData : function(node){
4961 if(node instanceof Array){
4963 for(var i = 0, len = node.length; i < len; i++){
4964 data.push(this.getNodeData(node[i]));
4968 return this.jsonData[this.indexOf(node)] || null;
4971 beforeRender : function(){
4972 this.snapshot = this.jsonData;
4974 this.sort.apply(this, this.sortInfo);
4976 this.fireEvent("beforerender", this, this.jsonData);
4979 onLoad : function(el, o){
4980 this.fireEvent("load", this, this.jsonData, o);
4983 onLoadException : function(el, o){
4984 this.fireEvent("loadexception", this, o);
4988 * Filter the data by a specific property.
4989 * @param {String} property A property on your JSON objects
4990 * @param {String/RegExp} value Either string that the property values
4991 * should start with, or a RegExp to test against the property
4993 filter : function(property, value){
4996 var ss = this.snapshot;
4997 if(typeof value == "string"){
4998 var vlen = value.length;
5003 value = value.toLowerCase();
5004 for(var i = 0, len = ss.length; i < len; i++){
5006 if(o[property].substr(0, vlen).toLowerCase() == value){
5010 } else if(value.exec){ // regex?
5011 for(var i = 0, len = ss.length; i < len; i++){
5013 if(value.test(o[property])){
5020 this.jsonData = data;
5026 * Filter by a function. The passed function will be called with each
5027 * object in the current dataset. If the function returns true the value is kept,
5028 * otherwise it is filtered.
5029 * @param {Function} fn
5030 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
5032 filterBy : function(fn, scope){
5035 var ss = this.snapshot;
5036 for(var i = 0, len = ss.length; i < len; i++){
5038 if(fn.call(scope || this, o)){
5042 this.jsonData = data;
5048 * Clears the current filter.
5050 clearFilter : function(){
5051 if(this.snapshot && this.jsonData != this.snapshot){
5052 this.jsonData = this.snapshot;
5059 * Sorts the data for this view and refreshes it.
5060 * @param {String} property A property on your JSON objects to sort on
5061 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5062 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5064 sort : function(property, dir, sortType){
5065 this.sortInfo = Array.prototype.slice.call(arguments, 0);
5068 var dsc = dir && dir.toLowerCase() == "desc";
5069 var f = function(o1, o2){
5070 var v1 = sortType ? sortType(o1[p]) : o1[p];
5071 var v2 = sortType ? sortType(o2[p]) : o2[p];
5074 return dsc ? +1 : -1;
5076 return dsc ? -1 : +1;
5081 this.jsonData.sort(f);
5083 if(this.jsonData != this.snapshot){
5084 this.snapshot.sort(f);
5090 * Ext JS Library 1.1.1
5091 * Copyright(c) 2006-2007, Ext JS, LLC.
5093 * Originally Released Under LGPL - original licence link has changed is not relivant.
5096 * <script type="text/javascript">
5101 * @class Roo.ColorPalette
5102 * @extends Roo.Component
5103 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
5104 * Here's an example of typical usage:
5106 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
5107 cp.render('my-div');
5109 cp.on('select', function(palette, selColor){
5110 // do something with selColor
5114 * Create a new ColorPalette
5115 * @param {Object} config The config object
5117 Roo.ColorPalette = function(config){
5118 Roo.ColorPalette.superclass.constructor.call(this, config);
5122 * Fires when a color is selected
5123 * @param {ColorPalette} this
5124 * @param {String} color The 6-digit color hex code (without the # symbol)
5130 this.on("select", this.handler, this.scope, true);
5133 Roo.extend(Roo.ColorPalette, Roo.Component, {
5135 * @cfg {String} itemCls
5136 * The CSS class to apply to the containing element (defaults to "x-color-palette")
5138 itemCls : "x-color-palette",
5140 * @cfg {String} value
5141 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
5142 * the hex codes are case-sensitive.
5147 ctype: "Roo.ColorPalette",
5150 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5152 allowReselect : false,
5155 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
5156 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
5157 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5158 * of colors with the width setting until the box is symmetrical.</p>
5159 * <p>You can override individual colors if needed:</p>
5161 var cp = new Roo.ColorPalette();
5162 cp.colors[0] = "FF0000"; // change the first box to red
5165 Or you can provide a custom array of your own for complete control:
5167 var cp = new Roo.ColorPalette();
5168 cp.colors = ["000000", "993300", "333300"];
5173 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5174 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5175 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5176 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5177 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5181 onRender : function(container, position){
5182 var t = new Roo.MasterTemplate(
5183 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
5185 var c = this.colors;
5186 for(var i = 0, len = c.length; i < len; i++){
5189 var el = document.createElement("div");
5190 el.className = this.itemCls;
5192 container.dom.insertBefore(el, position);
5193 this.el = Roo.get(el);
5194 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
5195 if(this.clickEvent != 'click'){
5196 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
5201 afterRender : function(){
5202 Roo.ColorPalette.superclass.afterRender.call(this);
5211 handleClick : function(e, t){
5214 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5215 this.select(c.toUpperCase());
5220 * Selects the specified color in the palette (fires the select event)
5221 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5223 select : function(color){
5224 color = color.replace("#", "");
5225 if(color != this.value || this.allowReselect){
5228 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5230 el.child("a.color-"+color).addClass("x-color-palette-sel");
5232 this.fireEvent("select", this, color);
5237 * Ext JS Library 1.1.1
5238 * Copyright(c) 2006-2007, Ext JS, LLC.
5240 * Originally Released Under LGPL - original licence link has changed is not relivant.
5243 * <script type="text/javascript">
5247 * @class Roo.DatePicker
5248 * @extends Roo.Component
5249 * Simple date picker class.
5251 * Create a new DatePicker
5252 * @param {Object} config The config object
5254 Roo.DatePicker = function(config){
5255 Roo.DatePicker.superclass.constructor.call(this, config);
5257 this.value = config && config.value ?
5258 config.value.clearTime() : new Date().clearTime();
5263 * Fires when a date is selected
5264 * @param {DatePicker} this
5265 * @param {Date} date The selected date
5269 * @event monthchange
5270 * Fires when the displayed month changes
5271 * @param {DatePicker} this
5272 * @param {Date} date The selected month
5278 this.on("select", this.handler, this.scope || this);
5280 // build the disabledDatesRE
5281 if(!this.disabledDatesRE && this.disabledDates){
5282 var dd = this.disabledDates;
5284 for(var i = 0; i < dd.length; i++){
5286 if(i != dd.length-1) {
5290 this.disabledDatesRE = new RegExp(re + ")");
5294 Roo.extend(Roo.DatePicker, Roo.Component, {
5296 * @cfg {String} todayText
5297 * The text to display on the button that selects the current date (defaults to "Today")
5299 todayText : "Today",
5301 * @cfg {String} okText
5302 * The text to display on the ok button
5304 okText : " OK ", //   to give the user extra clicking room
5306 * @cfg {String} cancelText
5307 * The text to display on the cancel button
5309 cancelText : "Cancel",
5311 * @cfg {String} todayTip
5312 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5314 todayTip : "{0} (Spacebar)",
5316 * @cfg {Date} minDate
5317 * Minimum allowable date (JavaScript date object, defaults to null)
5321 * @cfg {Date} maxDate
5322 * Maximum allowable date (JavaScript date object, defaults to null)
5326 * @cfg {String} minText
5327 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5329 minText : "This date is before the minimum date",
5331 * @cfg {String} maxText
5332 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5334 maxText : "This date is after the maximum date",
5336 * @cfg {String} format
5337 * The default date format string which can be overriden for localization support. The format must be
5338 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5342 * @cfg {Array} disabledDays
5343 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5345 disabledDays : null,
5347 * @cfg {String} disabledDaysText
5348 * The tooltip to display when the date falls on a disabled day (defaults to "")
5350 disabledDaysText : "",
5352 * @cfg {RegExp} disabledDatesRE
5353 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5355 disabledDatesRE : null,
5357 * @cfg {String} disabledDatesText
5358 * The tooltip text to display when the date falls on a disabled date (defaults to "")
5360 disabledDatesText : "",
5362 * @cfg {Boolean} constrainToViewport
5363 * True to constrain the date picker to the viewport (defaults to true)
5365 constrainToViewport : true,
5367 * @cfg {Array} monthNames
5368 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5370 monthNames : Date.monthNames,
5372 * @cfg {Array} dayNames
5373 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5375 dayNames : Date.dayNames,
5377 * @cfg {String} nextText
5378 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5380 nextText: 'Next Month (Control+Right)',
5382 * @cfg {String} prevText
5383 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5385 prevText: 'Previous Month (Control+Left)',
5387 * @cfg {String} monthYearText
5388 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5390 monthYearText: 'Choose a month (Control+Up/Down to move years)',
5392 * @cfg {Number} startDay
5393 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5397 * @cfg {Bool} showClear
5398 * Show a clear button (usefull for date form elements that can be blank.)
5404 * Sets the value of the date field
5405 * @param {Date} value The date to set
5407 setValue : function(value){
5408 var old = this.value;
5410 if (typeof(value) == 'string') {
5412 value = Date.parseDate(value, this.format);
5418 this.value = value.clearTime(true);
5420 this.update(this.value);
5425 * Gets the current selected value of the date field
5426 * @return {Date} The selected date
5428 getValue : function(){
5435 this.update(this.activeDate);
5440 onRender : function(container, position){
5443 '<table cellspacing="0">',
5444 '<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>',
5445 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5446 var dn = this.dayNames;
5447 for(var i = 0; i < 7; i++){
5448 var d = this.startDay+i;
5452 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5454 m[m.length] = "</tr></thead><tbody><tr>";
5455 for(var i = 0; i < 42; i++) {
5456 if(i % 7 == 0 && i != 0){
5457 m[m.length] = "</tr><tr>";
5459 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5461 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5462 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5464 var el = document.createElement("div");
5465 el.className = "x-date-picker";
5466 el.innerHTML = m.join("");
5468 container.dom.insertBefore(el, position);
5470 this.el = Roo.get(el);
5471 this.eventEl = Roo.get(el.firstChild);
5473 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5474 handler: this.showPrevMonth,
5476 preventDefault:true,
5480 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5481 handler: this.showNextMonth,
5483 preventDefault:true,
5487 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
5489 this.monthPicker = this.el.down('div.x-date-mp');
5490 this.monthPicker.enableDisplayMode('block');
5492 var kn = new Roo.KeyNav(this.eventEl, {
5493 "left" : function(e){
5495 this.showPrevMonth() :
5496 this.update(this.activeDate.add("d", -1));
5499 "right" : function(e){
5501 this.showNextMonth() :
5502 this.update(this.activeDate.add("d", 1));
5507 this.showNextYear() :
5508 this.update(this.activeDate.add("d", -7));
5511 "down" : function(e){
5513 this.showPrevYear() :
5514 this.update(this.activeDate.add("d", 7));
5517 "pageUp" : function(e){
5518 this.showNextMonth();
5521 "pageDown" : function(e){
5522 this.showPrevMonth();
5525 "enter" : function(e){
5526 e.stopPropagation();
5533 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
5535 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
5537 this.el.unselectable();
5539 this.cells = this.el.select("table.x-date-inner tbody td");
5540 this.textNodes = this.el.query("table.x-date-inner tbody span");
5542 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5544 tooltip: this.monthYearText
5547 this.mbtn.on('click', this.showMonthPicker, this);
5548 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5551 var today = (new Date()).dateFormat(this.format);
5553 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5554 if (this.showClear) {
5555 baseTb.add( new Roo.Toolbar.Fill());
5558 text: String.format(this.todayText, today),
5559 tooltip: String.format(this.todayTip, today),
5560 handler: this.selectToday,
5564 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5567 if (this.showClear) {
5569 baseTb.add( new Roo.Toolbar.Fill());
5572 cls: 'x-btn-icon x-btn-clear',
5573 handler: function() {
5575 this.fireEvent("select", this, '');
5585 this.update(this.value);
5588 createMonthPicker : function(){
5589 if(!this.monthPicker.dom.firstChild){
5590 var buf = ['<table border="0" cellspacing="0">'];
5591 for(var i = 0; i < 6; i++){
5593 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5594 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5596 '<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>' :
5597 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5601 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5603 '</button><button type="button" class="x-date-mp-cancel">',
5605 '</button></td></tr>',
5608 this.monthPicker.update(buf.join(''));
5609 this.monthPicker.on('click', this.onMonthClick, this);
5610 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5612 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5613 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5615 this.mpMonths.each(function(m, a, i){
5618 m.dom.xmonth = 5 + Math.round(i * .5);
5620 m.dom.xmonth = Math.round((i-1) * .5);
5626 showMonthPicker : function(){
5627 this.createMonthPicker();
5628 var size = this.el.getSize();
5629 this.monthPicker.setSize(size);
5630 this.monthPicker.child('table').setSize(size);
5632 this.mpSelMonth = (this.activeDate || this.value).getMonth();
5633 this.updateMPMonth(this.mpSelMonth);
5634 this.mpSelYear = (this.activeDate || this.value).getFullYear();
5635 this.updateMPYear(this.mpSelYear);
5637 this.monthPicker.slideIn('t', {duration:.2});
5640 updateMPYear : function(y){
5642 var ys = this.mpYears.elements;
5643 for(var i = 1; i <= 10; i++){
5644 var td = ys[i-1], y2;
5646 y2 = y + Math.round(i * .5);
5647 td.firstChild.innerHTML = y2;
5650 y2 = y - (5-Math.round(i * .5));
5651 td.firstChild.innerHTML = y2;
5654 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5658 updateMPMonth : function(sm){
5659 this.mpMonths.each(function(m, a, i){
5660 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5664 selectMPMonth: function(m){
5668 onMonthClick : function(e, t){
5670 var el = new Roo.Element(t), pn;
5671 if(el.is('button.x-date-mp-cancel')){
5672 this.hideMonthPicker();
5674 else if(el.is('button.x-date-mp-ok')){
5675 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5676 this.hideMonthPicker();
5678 else if(pn = el.up('td.x-date-mp-month', 2)){
5679 this.mpMonths.removeClass('x-date-mp-sel');
5680 pn.addClass('x-date-mp-sel');
5681 this.mpSelMonth = pn.dom.xmonth;
5683 else if(pn = el.up('td.x-date-mp-year', 2)){
5684 this.mpYears.removeClass('x-date-mp-sel');
5685 pn.addClass('x-date-mp-sel');
5686 this.mpSelYear = pn.dom.xyear;
5688 else if(el.is('a.x-date-mp-prev')){
5689 this.updateMPYear(this.mpyear-10);
5691 else if(el.is('a.x-date-mp-next')){
5692 this.updateMPYear(this.mpyear+10);
5696 onMonthDblClick : function(e, t){
5698 var el = new Roo.Element(t), pn;
5699 if(pn = el.up('td.x-date-mp-month', 2)){
5700 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5701 this.hideMonthPicker();
5703 else if(pn = el.up('td.x-date-mp-year', 2)){
5704 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5705 this.hideMonthPicker();
5709 hideMonthPicker : function(disableAnim){
5710 if(this.monthPicker){
5711 if(disableAnim === true){
5712 this.monthPicker.hide();
5714 this.monthPicker.slideOut('t', {duration:.2});
5720 showPrevMonth : function(e){
5721 this.update(this.activeDate.add("mo", -1));
5725 showNextMonth : function(e){
5726 this.update(this.activeDate.add("mo", 1));
5730 showPrevYear : function(){
5731 this.update(this.activeDate.add("y", -1));
5735 showNextYear : function(){
5736 this.update(this.activeDate.add("y", 1));
5740 handleMouseWheel : function(e){
5741 var delta = e.getWheelDelta();
5743 this.showPrevMonth();
5745 } else if(delta < 0){
5746 this.showNextMonth();
5752 handleDateClick : function(e, t){
5754 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5755 this.setValue(new Date(t.dateValue));
5756 this.fireEvent("select", this, this.value);
5761 selectToday : function(){
5762 this.setValue(new Date().clearTime());
5763 this.fireEvent("select", this, this.value);
5767 update : function(date)
5769 var vd = this.activeDate;
5770 this.activeDate = date;
5772 var t = date.getTime();
5773 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5774 this.cells.removeClass("x-date-selected");
5775 this.cells.each(function(c){
5776 if(c.dom.firstChild.dateValue == t){
5777 c.addClass("x-date-selected");
5778 setTimeout(function(){
5779 try{c.dom.firstChild.focus();}catch(e){}
5788 var days = date.getDaysInMonth();
5789 var firstOfMonth = date.getFirstDateOfMonth();
5790 var startingPos = firstOfMonth.getDay()-this.startDay;
5792 if(startingPos <= this.startDay){
5796 var pm = date.add("mo", -1);
5797 var prevStart = pm.getDaysInMonth()-startingPos;
5799 var cells = this.cells.elements;
5800 var textEls = this.textNodes;
5801 days += startingPos;
5803 // convert everything to numbers so it's fast
5805 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5806 var today = new Date().clearTime().getTime();
5807 var sel = date.clearTime().getTime();
5808 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5809 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5810 var ddMatch = this.disabledDatesRE;
5811 var ddText = this.disabledDatesText;
5812 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5813 var ddaysText = this.disabledDaysText;
5814 var format = this.format;
5816 var setCellClass = function(cal, cell){
5818 var t = d.getTime();
5819 cell.firstChild.dateValue = t;
5821 cell.className += " x-date-today";
5822 cell.title = cal.todayText;
5825 cell.className += " x-date-selected";
5826 setTimeout(function(){
5827 try{cell.firstChild.focus();}catch(e){}
5832 cell.className = " x-date-disabled";
5833 cell.title = cal.minText;
5837 cell.className = " x-date-disabled";
5838 cell.title = cal.maxText;
5842 if(ddays.indexOf(d.getDay()) != -1){
5843 cell.title = ddaysText;
5844 cell.className = " x-date-disabled";
5847 if(ddMatch && format){
5848 var fvalue = d.dateFormat(format);
5849 if(ddMatch.test(fvalue)){
5850 cell.title = ddText.replace("%0", fvalue);
5851 cell.className = " x-date-disabled";
5857 for(; i < startingPos; i++) {
5858 textEls[i].innerHTML = (++prevStart);
5859 d.setDate(d.getDate()+1);
5860 cells[i].className = "x-date-prevday";
5861 setCellClass(this, cells[i]);
5863 for(; i < days; i++){
5864 intDay = i - startingPos + 1;
5865 textEls[i].innerHTML = (intDay);
5866 d.setDate(d.getDate()+1);
5867 cells[i].className = "x-date-active";
5868 setCellClass(this, cells[i]);
5871 for(; i < 42; i++) {
5872 textEls[i].innerHTML = (++extraDays);
5873 d.setDate(d.getDate()+1);
5874 cells[i].className = "x-date-nextday";
5875 setCellClass(this, cells[i]);
5878 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5879 this.fireEvent('monthchange', this, date);
5881 if(!this.internalRender){
5882 var main = this.el.dom.firstChild;
5883 var w = main.offsetWidth;
5884 this.el.setWidth(w + this.el.getBorderWidth("lr"));
5885 Roo.fly(main).setWidth(w);
5886 this.internalRender = true;
5887 // opera does not respect the auto grow header center column
5888 // then, after it gets a width opera refuses to recalculate
5889 // without a second pass
5890 if(Roo.isOpera && !this.secondPass){
5891 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5892 this.secondPass = true;
5893 this.update.defer(10, this, [date]);
5901 * Ext JS Library 1.1.1
5902 * Copyright(c) 2006-2007, Ext JS, LLC.
5904 * Originally Released Under LGPL - original licence link has changed is not relivant.
5907 * <script type="text/javascript">
5910 * @class Roo.TabPanel
5911 * @extends Roo.util.Observable
5912 * A lightweight tab container.
5916 // basic tabs 1, built from existing content
5917 var tabs = new Roo.TabPanel("tabs1");
5918 tabs.addTab("script", "View Script");
5919 tabs.addTab("markup", "View Markup");
5920 tabs.activate("script");
5922 // more advanced tabs, built from javascript
5923 var jtabs = new Roo.TabPanel("jtabs");
5924 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5926 // set up the UpdateManager
5927 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5928 var updater = tab2.getUpdateManager();
5929 updater.setDefaultUrl("ajax1.htm");
5930 tab2.on('activate', updater.refresh, updater, true);
5932 // Use setUrl for Ajax loading
5933 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5934 tab3.setUrl("ajax2.htm", null, true);
5937 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5940 jtabs.activate("jtabs-1");
5943 * Create a new TabPanel.
5944 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5945 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5947 Roo.TabPanel = function(container, config){
5949 * The container element for this TabPanel.
5952 this.el = Roo.get(container, true);
5954 if(typeof config == "boolean"){
5955 this.tabPosition = config ? "bottom" : "top";
5957 Roo.apply(this, config);
5960 if(this.tabPosition == "bottom"){
5961 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5962 this.el.addClass("x-tabs-bottom");
5964 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5965 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5966 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5968 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5970 if(this.tabPosition != "bottom"){
5971 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5974 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5975 this.el.addClass("x-tabs-top");
5979 this.bodyEl.setStyle("position", "relative");
5982 this.activateDelegate = this.activate.createDelegate(this);
5987 * Fires when the active tab changes
5988 * @param {Roo.TabPanel} this
5989 * @param {Roo.TabPanelItem} activePanel The new active tab
5993 * @event beforetabchange
5994 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5995 * @param {Roo.TabPanel} this
5996 * @param {Object} e Set cancel to true on this object to cancel the tab change
5997 * @param {Roo.TabPanelItem} tab The tab being changed to
5999 "beforetabchange" : true
6002 Roo.EventManager.onWindowResize(this.onResize, this);
6003 this.cpad = this.el.getPadding("lr");
6004 this.hiddenCount = 0;
6007 // toolbar on the tabbar support...
6009 var tcfg = this.toolbar;
6010 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
6011 this.toolbar = new Roo.Toolbar(tcfg);
6013 var tbl = tcfg.container.child('table', true);
6014 tbl.setAttribute('width', '100%');
6021 Roo.TabPanel.superclass.constructor.call(this);
6024 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
6026 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
6028 tabPosition : "top",
6030 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
6032 currentTabWidth : 0,
6034 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
6038 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
6042 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
6044 preferredTabWidth : 175,
6046 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
6050 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
6052 monitorResize : true,
6054 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
6059 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6060 * @param {String} id The id of the div to use <b>or create</b>
6061 * @param {String} text The text for the tab
6062 * @param {String} content (optional) Content to put in the TabPanelItem body
6063 * @param {Boolean} closable (optional) True to create a close icon on the tab
6064 * @return {Roo.TabPanelItem} The created TabPanelItem
6066 addTab : function(id, text, content, closable){
6067 var item = new Roo.TabPanelItem(this, id, text, closable);
6068 this.addTabItem(item);
6070 item.setContent(content);
6076 * Returns the {@link Roo.TabPanelItem} with the specified id/index
6077 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6078 * @return {Roo.TabPanelItem}
6080 getTab : function(id){
6081 return this.items[id];
6085 * Hides the {@link Roo.TabPanelItem} with the specified id/index
6086 * @param {String/Number} id The id or index of the TabPanelItem to hide.
6088 hideTab : function(id){
6089 var t = this.items[id];
6093 this.autoSizeTabs();
6098 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6099 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6101 unhideTab : function(id){
6102 var t = this.items[id];
6106 this.autoSizeTabs();
6111 * Adds an existing {@link Roo.TabPanelItem}.
6112 * @param {Roo.TabPanelItem} item The TabPanelItem to add
6114 addTabItem : function(item){
6115 this.items[item.id] = item;
6116 this.items.push(item);
6117 if(this.resizeTabs){
6118 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6119 this.autoSizeTabs();
6126 * Removes a {@link Roo.TabPanelItem}.
6127 * @param {String/Number} id The id or index of the TabPanelItem to remove.
6129 removeTab : function(id){
6130 var items = this.items;
6131 var tab = items[id];
6132 if(!tab) { return; }
6133 var index = items.indexOf(tab);
6134 if(this.active == tab && items.length > 1){
6135 var newTab = this.getNextAvailable(index);
6140 this.stripEl.dom.removeChild(tab.pnode.dom);
6141 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6142 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6144 items.splice(index, 1);
6145 delete this.items[tab.id];
6146 tab.fireEvent("close", tab);
6147 tab.purgeListeners();
6148 this.autoSizeTabs();
6151 getNextAvailable : function(start){
6152 var items = this.items;
6154 // look for a next tab that will slide over to
6155 // replace the one being removed
6156 while(index < items.length){
6157 var item = items[++index];
6158 if(item && !item.isHidden()){
6162 // if one isn't found select the previous tab (on the left)
6165 var item = items[--index];
6166 if(item && !item.isHidden()){
6174 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6175 * @param {String/Number} id The id or index of the TabPanelItem to disable.
6177 disableTab : function(id){
6178 var tab = this.items[id];
6179 if(tab && this.active != tab){
6185 * Enables a {@link Roo.TabPanelItem} that is disabled.
6186 * @param {String/Number} id The id or index of the TabPanelItem to enable.
6188 enableTab : function(id){
6189 var tab = this.items[id];
6194 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6195 * @param {String/Number} id The id or index of the TabPanelItem to activate.
6196 * @return {Roo.TabPanelItem} The TabPanelItem.
6198 activate : function(id){
6199 var tab = this.items[id];
6203 if(tab == this.active || tab.disabled){
6207 this.fireEvent("beforetabchange", this, e, tab);
6208 if(e.cancel !== true && !tab.disabled){
6212 this.active = this.items[id];
6214 this.fireEvent("tabchange", this, this.active);
6220 * Gets the active {@link Roo.TabPanelItem}.
6221 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6223 getActiveTab : function(){
6228 * Updates the tab body element to fit the height of the container element
6229 * for overflow scrolling
6230 * @param {Number} targetHeight (optional) Override the starting height from the elements height
6232 syncHeight : function(targetHeight){
6233 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6234 var bm = this.bodyEl.getMargins();
6235 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6236 this.bodyEl.setHeight(newHeight);
6240 onResize : function(){
6241 if(this.monitorResize){
6242 this.autoSizeTabs();
6247 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6249 beginUpdate : function(){
6250 this.updating = true;
6254 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6256 endUpdate : function(){
6257 this.updating = false;
6258 this.autoSizeTabs();
6262 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6264 autoSizeTabs : function(){
6265 var count = this.items.length;
6266 var vcount = count - this.hiddenCount;
6267 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6270 var w = Math.max(this.el.getWidth() - this.cpad, 10);
6271 var availWidth = Math.floor(w / vcount);
6272 var b = this.stripBody;
6273 if(b.getWidth() > w){
6274 var tabs = this.items;
6275 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6276 if(availWidth < this.minTabWidth){
6277 /*if(!this.sleft){ // incomplete scrolling code
6278 this.createScrollButtons();
6281 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6284 if(this.currentTabWidth < this.preferredTabWidth){
6285 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6291 * Returns the number of tabs in this TabPanel.
6294 getCount : function(){
6295 return this.items.length;
6299 * Resizes all the tabs to the passed width
6300 * @param {Number} The new width
6302 setTabWidth : function(width){
6303 this.currentTabWidth = width;
6304 for(var i = 0, len = this.items.length; i < len; i++) {
6305 if(!this.items[i].isHidden()) {
6306 this.items[i].setWidth(width);
6312 * Destroys this TabPanel
6313 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6315 destroy : function(removeEl){
6316 Roo.EventManager.removeResizeListener(this.onResize, this);
6317 for(var i = 0, len = this.items.length; i < len; i++){
6318 this.items[i].purgeListeners();
6320 if(removeEl === true){
6328 * @class Roo.TabPanelItem
6329 * @extends Roo.util.Observable
6330 * Represents an individual item (tab plus body) in a TabPanel.
6331 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6332 * @param {String} id The id of this TabPanelItem
6333 * @param {String} text The text for the tab of this TabPanelItem
6334 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6336 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6338 * The {@link Roo.TabPanel} this TabPanelItem belongs to
6339 * @type Roo.TabPanel
6341 this.tabPanel = tabPanel;
6343 * The id for this TabPanelItem
6348 this.disabled = false;
6352 this.loaded = false;
6353 this.closable = closable;
6356 * The body element for this TabPanelItem.
6359 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6360 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6361 this.bodyEl.setStyle("display", "block");
6362 this.bodyEl.setStyle("zoom", "1");
6365 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6367 this.el = Roo.get(els.el, true);
6368 this.inner = Roo.get(els.inner, true);
6369 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6370 this.pnode = Roo.get(els.el.parentNode, true);
6371 this.el.on("mousedown", this.onTabMouseDown, this);
6372 this.el.on("click", this.onTabClick, this);
6375 var c = Roo.get(els.close, true);
6376 c.dom.title = this.closeText;
6377 c.addClassOnOver("close-over");
6378 c.on("click", this.closeClick, this);
6384 * Fires when this tab becomes the active tab.
6385 * @param {Roo.TabPanel} tabPanel The parent TabPanel
6386 * @param {Roo.TabPanelItem} this
6390 * @event beforeclose
6391 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6392 * @param {Roo.TabPanelItem} this
6393 * @param {Object} e Set cancel to true on this object to cancel the close.
6395 "beforeclose": true,
6398 * Fires when this tab is closed.
6399 * @param {Roo.TabPanelItem} this
6404 * Fires when this tab is no longer the active tab.
6405 * @param {Roo.TabPanel} tabPanel The parent TabPanel
6406 * @param {Roo.TabPanelItem} this
6410 this.hidden = false;
6412 Roo.TabPanelItem.superclass.constructor.call(this);
6415 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6416 purgeListeners : function(){
6417 Roo.util.Observable.prototype.purgeListeners.call(this);
6418 this.el.removeAllListeners();
6421 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6424 this.pnode.addClass("on");
6427 this.tabPanel.stripWrap.repaint();
6429 this.fireEvent("activate", this.tabPanel, this);
6433 * Returns true if this tab is the active tab.
6436 isActive : function(){
6437 return this.tabPanel.getActiveTab() == this;
6441 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6444 this.pnode.removeClass("on");
6446 this.fireEvent("deactivate", this.tabPanel, this);
6449 hideAction : function(){
6451 this.bodyEl.setStyle("position", "absolute");
6452 this.bodyEl.setLeft("-20000px");
6453 this.bodyEl.setTop("-20000px");
6456 showAction : function(){
6457 this.bodyEl.setStyle("position", "relative");
6458 this.bodyEl.setTop("");
6459 this.bodyEl.setLeft("");
6464 * Set the tooltip for the tab.
6465 * @param {String} tooltip The tab's tooltip
6467 setTooltip : function(text){
6468 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6469 this.textEl.dom.qtip = text;
6470 this.textEl.dom.removeAttribute('title');
6472 this.textEl.dom.title = text;
6476 onTabClick : function(e){
6478 this.tabPanel.activate(this.id);
6481 onTabMouseDown : function(e){
6483 this.tabPanel.activate(this.id);
6486 getWidth : function(){
6487 return this.inner.getWidth();
6490 setWidth : function(width){
6491 var iwidth = width - this.pnode.getPadding("lr");
6492 this.inner.setWidth(iwidth);
6493 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6494 this.pnode.setWidth(width);
6498 * Show or hide the tab
6499 * @param {Boolean} hidden True to hide or false to show.
6501 setHidden : function(hidden){
6502 this.hidden = hidden;
6503 this.pnode.setStyle("display", hidden ? "none" : "");
6507 * Returns true if this tab is "hidden"
6510 isHidden : function(){
6515 * Returns the text for this tab
6518 getText : function(){
6522 autoSize : function(){
6523 //this.el.beginMeasure();
6524 this.textEl.setWidth(1);
6526 * #2804 [new] Tabs in Roojs
6527 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6529 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6530 //this.el.endMeasure();
6534 * Sets the text for the tab (Note: this also sets the tooltip text)
6535 * @param {String} text The tab's text and tooltip
6537 setText : function(text){
6539 this.textEl.update(text);
6540 this.setTooltip(text);
6541 if(!this.tabPanel.resizeTabs){
6546 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6548 activate : function(){
6549 this.tabPanel.activate(this.id);
6553 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6555 disable : function(){
6556 if(this.tabPanel.active != this){
6557 this.disabled = true;
6558 this.pnode.addClass("disabled");
6563 * Enables this TabPanelItem if it was previously disabled.
6565 enable : function(){
6566 this.disabled = false;
6567 this.pnode.removeClass("disabled");
6571 * Sets the content for this TabPanelItem.
6572 * @param {String} content The content
6573 * @param {Boolean} loadScripts true to look for and load scripts
6575 setContent : function(content, loadScripts){
6576 this.bodyEl.update(content, loadScripts);
6580 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6581 * @return {Roo.UpdateManager} The UpdateManager
6583 getUpdateManager : function(){
6584 return this.bodyEl.getUpdateManager();
6588 * Set a URL to be used to load the content for this TabPanelItem.
6589 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6590 * @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)
6591 * @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)
6592 * @return {Roo.UpdateManager} The UpdateManager
6594 setUrl : function(url, params, loadOnce){
6595 if(this.refreshDelegate){
6596 this.un('activate', this.refreshDelegate);
6598 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6599 this.on("activate", this.refreshDelegate);
6600 return this.bodyEl.getUpdateManager();
6604 _handleRefresh : function(url, params, loadOnce){
6605 if(!loadOnce || !this.loaded){
6606 var updater = this.bodyEl.getUpdateManager();
6607 updater.update(url, params, this._setLoaded.createDelegate(this));
6612 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
6613 * Will fail silently if the setUrl method has not been called.
6614 * This does not activate the panel, just updates its content.
6616 refresh : function(){
6617 if(this.refreshDelegate){
6618 this.loaded = false;
6619 this.refreshDelegate();
6624 _setLoaded : function(){
6629 closeClick : function(e){
6632 this.fireEvent("beforeclose", this, o);
6633 if(o.cancel !== true){
6634 this.tabPanel.removeTab(this.id);
6638 * The text displayed in the tooltip for the close icon.
6641 closeText : "Close this tab"
6645 Roo.TabPanel.prototype.createStrip = function(container){
6646 var strip = document.createElement("div");
6647 strip.className = "x-tabs-wrap";
6648 container.appendChild(strip);
6652 Roo.TabPanel.prototype.createStripList = function(strip){
6653 // div wrapper for retard IE
6654 // returns the "tr" element.
6655 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6656 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6657 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6658 return strip.firstChild.firstChild.firstChild.firstChild;
6661 Roo.TabPanel.prototype.createBody = function(container){
6662 var body = document.createElement("div");
6663 Roo.id(body, "tab-body");
6664 Roo.fly(body).addClass("x-tabs-body");
6665 container.appendChild(body);
6669 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6670 var body = Roo.getDom(id);
6672 body = document.createElement("div");
6675 Roo.fly(body).addClass("x-tabs-item-body");
6676 bodyEl.insertBefore(body, bodyEl.firstChild);
6680 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6681 var td = document.createElement("td");
6682 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6683 //stripEl.appendChild(td);
6685 td.className = "x-tabs-closable";
6687 this.closeTpl = new Roo.Template(
6688 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6689 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6690 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
6693 var el = this.closeTpl.overwrite(td, {"text": text});
6694 var close = el.getElementsByTagName("div")[0];
6695 var inner = el.getElementsByTagName("em")[0];
6696 return {"el": el, "close": close, "inner": inner};
6699 this.tabTpl = new Roo.Template(
6700 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6701 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6704 var el = this.tabTpl.overwrite(td, {"text": text});
6705 var inner = el.getElementsByTagName("em")[0];
6706 return {"el": el, "inner": inner};
6710 * Ext JS Library 1.1.1
6711 * Copyright(c) 2006-2007, Ext JS, LLC.
6713 * Originally Released Under LGPL - original licence link has changed is not relivant.
6716 * <script type="text/javascript">
6721 * @extends Roo.util.Observable
6722 * Simple Button class
6723 * @cfg {String} text The button text
6724 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6725 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6726 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6727 * @cfg {Object} scope The scope of the handler
6728 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6729 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6730 * @cfg {Boolean} hidden True to start hidden (defaults to false)
6731 * @cfg {Boolean} disabled True to start disabled (defaults to false)
6732 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6733 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6734 applies if enableToggle = true)
6735 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6736 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6737 an {@link Roo.util.ClickRepeater} config object (defaults to false).
6739 * Create a new button
6740 * @param {Object} config The config object
6742 Roo.Button = function(renderTo, config)
6746 renderTo = config.renderTo || false;
6749 Roo.apply(this, config);
6753 * Fires when this button is clicked
6754 * @param {Button} this
6755 * @param {EventObject} e The click event
6760 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6761 * @param {Button} this
6762 * @param {Boolean} pressed
6767 * Fires when the mouse hovers over the button
6768 * @param {Button} this
6769 * @param {Event} e The event object
6774 * Fires when the mouse exits the button
6775 * @param {Button} this
6776 * @param {Event} e The event object
6781 * Fires when the button is rendered
6782 * @param {Button} this
6787 this.menu = Roo.menu.MenuMgr.get(this.menu);
6789 // register listeners first!! - so render can be captured..
6790 Roo.util.Observable.call(this);
6792 this.render(renderTo);
6798 Roo.extend(Roo.Button, Roo.util.Observable, {
6804 * Read-only. True if this button is hidden
6809 * Read-only. True if this button is disabled
6814 * Read-only. True if this button is pressed (only if enableToggle = true)
6820 * @cfg {Number} tabIndex
6821 * The DOM tabIndex for this button (defaults to undefined)
6823 tabIndex : undefined,
6826 * @cfg {Boolean} enableToggle
6827 * True to enable pressed/not pressed toggling (defaults to false)
6829 enableToggle: false,
6832 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6836 * @cfg {String} menuAlign
6837 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6839 menuAlign : "tl-bl?",
6842 * @cfg {String} iconCls
6843 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6845 iconCls : undefined,
6847 * @cfg {String} type
6848 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
6853 menuClassTarget: 'tr',
6856 * @cfg {String} clickEvent
6857 * The type of event to map to the button's event handler (defaults to 'click')
6859 clickEvent : 'click',
6862 * @cfg {Boolean} handleMouseEvents
6863 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6865 handleMouseEvents : true,
6868 * @cfg {String} tooltipType
6869 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6871 tooltipType : 'qtip',
6875 * A CSS class to apply to the button's main element.
6879 * @cfg {Roo.Template} template (Optional)
6880 * An {@link Roo.Template} with which to create the Button's main element. This Template must
6881 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6882 * require code modifications if required elements (e.g. a button) aren't present.
6886 render : function(renderTo){
6888 if(this.hideParent){
6889 this.parentEl = Roo.get(renderTo);
6893 if(!Roo.Button.buttonTemplate){
6894 // hideous table template
6895 Roo.Button.buttonTemplate = new Roo.Template(
6896 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6897 '<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>',
6898 "</tr></tbody></table>");
6900 this.template = Roo.Button.buttonTemplate;
6902 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
6903 var btnEl = btn.child("button:first");
6904 btnEl.on('focus', this.onFocus, this);
6905 btnEl.on('blur', this.onBlur, this);
6907 btn.addClass(this.cls);
6910 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6913 btnEl.addClass(this.iconCls);
6915 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6918 if(this.tabIndex !== undefined){
6919 btnEl.dom.tabIndex = this.tabIndex;
6922 if(typeof this.tooltip == 'object'){
6923 Roo.QuickTips.tips(Roo.apply({
6927 btnEl.dom[this.tooltipType] = this.tooltip;
6931 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6935 this.el.dom.id = this.el.id = this.id;
6938 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6939 this.menu.on("show", this.onMenuShow, this);
6940 this.menu.on("hide", this.onMenuHide, this);
6942 btn.addClass("x-btn");
6943 if(Roo.isIE && !Roo.isIE7){
6944 this.autoWidth.defer(1, this);
6948 if(this.handleMouseEvents){
6949 btn.on("mouseover", this.onMouseOver, this);
6950 btn.on("mouseout", this.onMouseOut, this);
6951 btn.on("mousedown", this.onMouseDown, this);
6953 btn.on(this.clickEvent, this.onClick, this);
6954 //btn.on("mouseup", this.onMouseUp, this);
6961 Roo.ButtonToggleMgr.register(this);
6963 this.el.addClass("x-btn-pressed");
6966 var repeater = new Roo.util.ClickRepeater(btn,
6967 typeof this.repeat == "object" ? this.repeat : {}
6969 repeater.on("click", this.onClick, this);
6972 this.fireEvent('render', this);
6976 * Returns the button's underlying element
6977 * @return {Roo.Element} The element
6984 * Destroys this Button and removes any listeners.
6986 destroy : function(){
6987 Roo.ButtonToggleMgr.unregister(this);
6988 this.el.removeAllListeners();
6989 this.purgeListeners();
6994 autoWidth : function(){
6996 this.el.setWidth("auto");
6997 if(Roo.isIE7 && Roo.isStrict){
6998 var ib = this.el.child('button');
6999 if(ib && ib.getWidth() > 20){
7001 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7006 this.el.beginMeasure();
7008 if(this.el.getWidth() < this.minWidth){
7009 this.el.setWidth(this.minWidth);
7012 this.el.endMeasure();
7019 * Assigns this button's click handler
7020 * @param {Function} handler The function to call when the button is clicked
7021 * @param {Object} scope (optional) Scope for the function passed in
7023 setHandler : function(handler, scope){
7024 this.handler = handler;
7029 * Sets this button's text
7030 * @param {String} text The button text
7032 setText : function(text){
7035 this.el.child("td.x-btn-center button.x-btn-text").update(text);
7041 * Gets the text for this button
7042 * @return {String} The button text
7044 getText : function(){
7052 this.hidden = false;
7054 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7064 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7069 * Convenience function for boolean show/hide
7070 * @param {Boolean} visible True to show, false to hide
7072 setVisible: function(visible){
7081 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7082 * @param {Boolean} state (optional) Force a particular state
7084 toggle : function(state){
7085 state = state === undefined ? !this.pressed : state;
7086 if(state != this.pressed){
7088 this.el.addClass("x-btn-pressed");
7089 this.pressed = true;
7090 this.fireEvent("toggle", this, true);
7092 this.el.removeClass("x-btn-pressed");
7093 this.pressed = false;
7094 this.fireEvent("toggle", this, false);
7096 if(this.toggleHandler){
7097 this.toggleHandler.call(this.scope || this, this, state);
7106 this.el.child('button:first').focus();
7110 * Disable this button
7112 disable : function(){
7114 this.el.addClass("x-btn-disabled");
7116 this.disabled = true;
7120 * Enable this button
7122 enable : function(){
7124 this.el.removeClass("x-btn-disabled");
7126 this.disabled = false;
7130 * Convenience function for boolean enable/disable
7131 * @param {Boolean} enabled True to enable, false to disable
7133 setDisabled : function(v){
7134 this[v !== true ? "enable" : "disable"]();
7138 onClick : function(e)
7147 if(this.enableToggle){
7150 if(this.menu && !this.menu.isVisible()){
7151 this.menu.show(this.el, this.menuAlign);
7153 this.fireEvent("click", this, e);
7155 this.el.removeClass("x-btn-over");
7156 this.handler.call(this.scope || this, this, e);
7161 onMouseOver : function(e){
7163 this.el.addClass("x-btn-over");
7164 this.fireEvent('mouseover', this, e);
7168 onMouseOut : function(e){
7169 if(!e.within(this.el, true)){
7170 this.el.removeClass("x-btn-over");
7171 this.fireEvent('mouseout', this, e);
7175 onFocus : function(e){
7177 this.el.addClass("x-btn-focus");
7181 onBlur : function(e){
7182 this.el.removeClass("x-btn-focus");
7185 onMouseDown : function(e){
7186 if(!this.disabled && e.button == 0){
7187 this.el.addClass("x-btn-click");
7188 Roo.get(document).on('mouseup', this.onMouseUp, this);
7192 onMouseUp : function(e){
7194 this.el.removeClass("x-btn-click");
7195 Roo.get(document).un('mouseup', this.onMouseUp, this);
7199 onMenuShow : function(e){
7200 this.el.addClass("x-btn-menu-active");
7203 onMenuHide : function(e){
7204 this.el.removeClass("x-btn-menu-active");
7208 // Private utility class used by Button
7209 Roo.ButtonToggleMgr = function(){
7212 function toggleGroup(btn, state){
7214 var g = groups[btn.toggleGroup];
7215 for(var i = 0, l = g.length; i < l; i++){
7224 register : function(btn){
7225 if(!btn.toggleGroup){
7228 var g = groups[btn.toggleGroup];
7230 g = groups[btn.toggleGroup] = [];
7233 btn.on("toggle", toggleGroup);
7236 unregister : function(btn){
7237 if(!btn.toggleGroup){
7240 var g = groups[btn.toggleGroup];
7243 btn.un("toggle", toggleGroup);
7249 * Ext JS Library 1.1.1
7250 * Copyright(c) 2006-2007, Ext JS, LLC.
7252 * Originally Released Under LGPL - original licence link has changed is not relivant.
7255 * <script type="text/javascript">
7259 * @class Roo.SplitButton
7260 * @extends Roo.Button
7261 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7262 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
7263 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7264 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7265 * @cfg {String} arrowTooltip The title attribute of the arrow
7267 * Create a new menu button
7268 * @param {String/HTMLElement/Element} renderTo The element to append the button to
7269 * @param {Object} config The config object
7271 Roo.SplitButton = function(renderTo, config){
7272 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7275 * Fires when this button's arrow is clicked
7276 * @param {SplitButton} this
7277 * @param {EventObject} e The click event
7279 this.addEvents({"arrowclick":true});
7282 Roo.extend(Roo.SplitButton, Roo.Button, {
7283 render : function(renderTo){
7284 // this is one sweet looking template!
7285 var tpl = new Roo.Template(
7286 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7287 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7288 '<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>',
7289 "</tbody></table></td><td>",
7290 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7291 '<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>',
7292 "</tbody></table></td></tr></table>"
7294 var btn = tpl.append(renderTo, [this.text, this.type], true);
7295 var btnEl = btn.child("button");
7297 btn.addClass(this.cls);
7300 btnEl.setStyle('background-image', 'url(' +this.icon +')');
7303 btnEl.addClass(this.iconCls);
7305 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7309 if(this.handleMouseEvents){
7310 btn.on("mouseover", this.onMouseOver, this);
7311 btn.on("mouseout", this.onMouseOut, this);
7312 btn.on("mousedown", this.onMouseDown, this);
7313 btn.on("mouseup", this.onMouseUp, this);
7315 btn.on(this.clickEvent, this.onClick, this);
7317 if(typeof this.tooltip == 'object'){
7318 Roo.QuickTips.tips(Roo.apply({
7322 btnEl.dom[this.tooltipType] = this.tooltip;
7325 if(this.arrowTooltip){
7326 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7335 this.el.addClass("x-btn-pressed");
7337 if(Roo.isIE && !Roo.isIE7){
7338 this.autoWidth.defer(1, this);
7343 this.menu.on("show", this.onMenuShow, this);
7344 this.menu.on("hide", this.onMenuHide, this);
7346 this.fireEvent('render', this);
7350 autoWidth : function(){
7352 var tbl = this.el.child("table:first");
7353 var tbl2 = this.el.child("table:last");
7354 this.el.setWidth("auto");
7355 tbl.setWidth("auto");
7356 if(Roo.isIE7 && Roo.isStrict){
7357 var ib = this.el.child('button:first');
7358 if(ib && ib.getWidth() > 20){
7360 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7365 this.el.beginMeasure();
7367 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7368 tbl.setWidth(this.minWidth-tbl2.getWidth());
7371 this.el.endMeasure();
7374 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7378 * Sets this button's click handler
7379 * @param {Function} handler The function to call when the button is clicked
7380 * @param {Object} scope (optional) Scope for the function passed above
7382 setHandler : function(handler, scope){
7383 this.handler = handler;
7388 * Sets this button's arrow click handler
7389 * @param {Function} handler The function to call when the arrow is clicked
7390 * @param {Object} scope (optional) Scope for the function passed above
7392 setArrowHandler : function(handler, scope){
7393 this.arrowHandler = handler;
7402 this.el.child("button:first").focus();
7407 onClick : function(e){
7410 if(e.getTarget(".x-btn-menu-arrow-wrap")){
7411 if(this.menu && !this.menu.isVisible()){
7412 this.menu.show(this.el, this.menuAlign);
7414 this.fireEvent("arrowclick", this, e);
7415 if(this.arrowHandler){
7416 this.arrowHandler.call(this.scope || this, this, e);
7419 this.fireEvent("click", this, e);
7421 this.handler.call(this.scope || this, this, e);
7427 onMouseDown : function(e){
7429 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7433 onMouseUp : function(e){
7434 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7440 Roo.MenuButton = Roo.SplitButton;/*
7442 * Ext JS Library 1.1.1
7443 * Copyright(c) 2006-2007, Ext JS, LLC.
7445 * Originally Released Under LGPL - original licence link has changed is not relivant.
7448 * <script type="text/javascript">
7452 * @class Roo.Toolbar
7453 * Basic Toolbar class.
7455 * Creates a new Toolbar
7456 * @param {Object} container The config object
7458 Roo.Toolbar = function(container, buttons, config)
7460 /// old consturctor format still supported..
7461 if(container instanceof Array){ // omit the container for later rendering
7462 buttons = container;
7466 if (typeof(container) == 'object' && container.xtype) {
7468 container = config.container;
7469 buttons = config.buttons || []; // not really - use items!!
7472 if (config && config.items) {
7473 xitems = config.items;
7474 delete config.items;
7476 Roo.apply(this, config);
7477 this.buttons = buttons;
7480 this.render(container);
7482 this.xitems = xitems;
7483 Roo.each(xitems, function(b) {
7489 Roo.Toolbar.prototype = {
7491 * @cfg {Array} items
7492 * array of button configs or elements to add (will be converted to a MixedCollection)
7496 * @cfg {String/HTMLElement/Element} container
7497 * The id or element that will contain the toolbar
7500 render : function(ct){
7501 this.el = Roo.get(ct);
7503 this.el.addClass(this.cls);
7505 // using a table allows for vertical alignment
7506 // 100% width is needed by Safari...
7507 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7508 this.tr = this.el.child("tr", true);
7510 this.items = new Roo.util.MixedCollection(false, function(o){
7511 return o.id || ("item" + (++autoId));
7514 this.add.apply(this, this.buttons);
7515 delete this.buttons;
7520 * Adds element(s) to the toolbar -- this function takes a variable number of
7521 * arguments of mixed type and adds them to the toolbar.
7522 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7524 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7525 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7526 * <li>Field: Any form field (equivalent to {@link #addField})</li>
7527 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7528 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7529 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7530 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7531 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7532 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7534 * @param {Mixed} arg2
7535 * @param {Mixed} etc.
7538 var a = arguments, l = a.length;
7539 for(var i = 0; i < l; i++){
7544 _add : function(el) {
7547 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7550 if (el.applyTo){ // some kind of form field
7551 return this.addField(el);
7553 if (el.render){ // some kind of Toolbar.Item
7554 return this.addItem(el);
7556 if (typeof el == "string"){ // string
7557 if(el == "separator" || el == "-"){
7558 return this.addSeparator();
7561 return this.addSpacer();
7564 return this.addFill();
7566 return this.addText(el);
7569 if(el.tagName){ // element
7570 return this.addElement(el);
7572 if(typeof el == "object"){ // must be button config?
7573 return this.addButton(el);
7581 * Add an Xtype element
7582 * @param {Object} xtype Xtype Object
7583 * @return {Object} created Object
7585 addxtype : function(e){
7590 * Returns the Element for this toolbar.
7591 * @return {Roo.Element}
7599 * @return {Roo.Toolbar.Item} The separator item
7601 addSeparator : function(){
7602 return this.addItem(new Roo.Toolbar.Separator());
7606 * Adds a spacer element
7607 * @return {Roo.Toolbar.Spacer} The spacer item
7609 addSpacer : function(){
7610 return this.addItem(new Roo.Toolbar.Spacer());
7614 * Adds a fill element that forces subsequent additions to the right side of the toolbar
7615 * @return {Roo.Toolbar.Fill} The fill item
7617 addFill : function(){
7618 return this.addItem(new Roo.Toolbar.Fill());
7622 * Adds any standard HTML element to the toolbar
7623 * @param {String/HTMLElement/Element} el The element or id of the element to add
7624 * @return {Roo.Toolbar.Item} The element's item
7626 addElement : function(el){
7627 return this.addItem(new Roo.Toolbar.Item(el));
7630 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7631 * @type Roo.util.MixedCollection
7636 * Adds any Toolbar.Item or subclass
7637 * @param {Roo.Toolbar.Item} item
7638 * @return {Roo.Toolbar.Item} The item
7640 addItem : function(item){
7641 var td = this.nextBlock();
7643 this.items.add(item);
7648 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7649 * @param {Object/Array} config A button config or array of configs
7650 * @return {Roo.Toolbar.Button/Array}
7652 addButton : function(config){
7653 if(config instanceof Array){
7655 for(var i = 0, len = config.length; i < len; i++) {
7656 buttons.push(this.addButton(config[i]));
7661 if(!(config instanceof Roo.Toolbar.Button)){
7663 new Roo.Toolbar.SplitButton(config) :
7664 new Roo.Toolbar.Button(config);
7666 var td = this.nextBlock();
7673 * Adds text to the toolbar
7674 * @param {String} text The text to add
7675 * @return {Roo.Toolbar.Item} The element's item
7677 addText : function(text){
7678 return this.addItem(new Roo.Toolbar.TextItem(text));
7682 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7683 * @param {Number} index The index where the item is to be inserted
7684 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7685 * @return {Roo.Toolbar.Button/Item}
7687 insertButton : function(index, item){
7688 if(item instanceof Array){
7690 for(var i = 0, len = item.length; i < len; i++) {
7691 buttons.push(this.insertButton(index + i, item[i]));
7695 if (!(item instanceof Roo.Toolbar.Button)){
7696 item = new Roo.Toolbar.Button(item);
7698 var td = document.createElement("td");
7699 this.tr.insertBefore(td, this.tr.childNodes[index]);
7701 this.items.insert(index, item);
7706 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7707 * @param {Object} config
7708 * @return {Roo.Toolbar.Item} The element's item
7710 addDom : function(config, returnEl){
7711 var td = this.nextBlock();
7712 Roo.DomHelper.overwrite(td, config);
7713 var ti = new Roo.Toolbar.Item(td.firstChild);
7720 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7721 * @type Roo.util.MixedCollection
7726 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7727 * Note: the field should not have been rendered yet. For a field that has already been
7728 * rendered, use {@link #addElement}.
7729 * @param {Roo.form.Field} field
7730 * @return {Roo.ToolbarItem}
7734 addField : function(field) {
7737 this.fields = new Roo.util.MixedCollection(false, function(o){
7738 return o.id || ("item" + (++autoId));
7743 var td = this.nextBlock();
7745 var ti = new Roo.Toolbar.Item(td.firstChild);
7748 this.fields.add(field);
7759 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7760 this.el.child('div').hide();
7768 this.el.child('div').show();
7772 nextBlock : function(){
7773 var td = document.createElement("td");
7774 this.tr.appendChild(td);
7779 destroy : function(){
7780 if(this.items){ // rendered?
7781 Roo.destroy.apply(Roo, this.items.items);
7783 if(this.fields){ // rendered?
7784 Roo.destroy.apply(Roo, this.fields.items);
7786 Roo.Element.uncache(this.el, this.tr);
7791 * @class Roo.Toolbar.Item
7792 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7794 * Creates a new Item
7795 * @param {HTMLElement} el
7797 Roo.Toolbar.Item = function(el){
7799 if (typeof (el.xtype) != 'undefined') {
7804 this.el = Roo.getDom(el);
7805 this.id = Roo.id(this.el);
7806 this.hidden = false;
7811 * Fires when the button is rendered
7812 * @param {Button} this
7816 Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7818 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7819 //Roo.Toolbar.Item.prototype = {
7822 * Get this item's HTML Element
7823 * @return {HTMLElement}
7830 render : function(td){
7833 td.appendChild(this.el);
7835 this.fireEvent('render', this);
7839 * Removes and destroys this item.
7841 destroy : function(){
7842 this.td.parentNode.removeChild(this.td);
7849 this.hidden = false;
7850 this.td.style.display = "";
7858 this.td.style.display = "none";
7862 * Convenience function for boolean show/hide.
7863 * @param {Boolean} visible true to show/false to hide
7865 setVisible: function(visible){
7874 * Try to focus this item.
7877 Roo.fly(this.el).focus();
7881 * Disables this item.
7883 disable : function(){
7884 Roo.fly(this.td).addClass("x-item-disabled");
7885 this.disabled = true;
7886 this.el.disabled = true;
7890 * Enables this item.
7892 enable : function(){
7893 Roo.fly(this.td).removeClass("x-item-disabled");
7894 this.disabled = false;
7895 this.el.disabled = false;
7901 * @class Roo.Toolbar.Separator
7902 * @extends Roo.Toolbar.Item
7903 * A simple toolbar separator class
7905 * Creates a new Separator
7907 Roo.Toolbar.Separator = function(cfg){
7909 var s = document.createElement("span");
7910 s.className = "ytb-sep";
7915 Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7917 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7919 disable:Roo.emptyFn,
7924 * @class Roo.Toolbar.Spacer
7925 * @extends Roo.Toolbar.Item
7926 * A simple element that adds extra horizontal space to a toolbar.
7928 * Creates a new Spacer
7930 Roo.Toolbar.Spacer = function(cfg){
7931 var s = document.createElement("div");
7932 s.className = "ytb-spacer";
7936 Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7938 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7940 disable:Roo.emptyFn,
7945 * @class Roo.Toolbar.Fill
7946 * @extends Roo.Toolbar.Spacer
7947 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7949 * Creates a new Spacer
7951 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7953 render : function(td){
7954 td.style.width = '100%';
7955 Roo.Toolbar.Fill.superclass.render.call(this, td);
7960 * @class Roo.Toolbar.TextItem
7961 * @extends Roo.Toolbar.Item
7962 * A simple class that renders text directly into a toolbar.
7964 * Creates a new TextItem
7965 * @param {String} text
7967 Roo.Toolbar.TextItem = function(cfg){
7968 var text = cfg || "";
7969 if (typeof(cfg) == 'object') {
7970 text = cfg.text || "";
7974 var s = document.createElement("span");
7975 s.className = "ytb-text";
7981 Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg || s);
7983 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7987 disable:Roo.emptyFn,
7992 * @class Roo.Toolbar.Button
7993 * @extends Roo.Button
7994 * A button that renders into a toolbar.
7996 * Creates a new Button
7997 * @param {Object} config A standard {@link Roo.Button} config object
7999 Roo.Toolbar.Button = function(config){
8000 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
8002 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
8003 render : function(td){
8005 Roo.Toolbar.Button.superclass.render.call(this, td);
8009 * Removes and destroys this button
8011 destroy : function(){
8012 Roo.Toolbar.Button.superclass.destroy.call(this);
8013 this.td.parentNode.removeChild(this.td);
8020 this.hidden = false;
8021 this.td.style.display = "";
8029 this.td.style.display = "none";
8033 * Disables this item
8035 disable : function(){
8036 Roo.fly(this.td).addClass("x-item-disabled");
8037 this.disabled = true;
8043 enable : function(){
8044 Roo.fly(this.td).removeClass("x-item-disabled");
8045 this.disabled = false;
8049 Roo.ToolbarButton = Roo.Toolbar.Button;
8052 * @class Roo.Toolbar.SplitButton
8053 * @extends Roo.SplitButton
8054 * A menu button that renders into a toolbar.
8056 * Creates a new SplitButton
8057 * @param {Object} config A standard {@link Roo.SplitButton} config object
8059 Roo.Toolbar.SplitButton = function(config){
8060 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8062 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8063 render : function(td){
8065 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8069 * Removes and destroys this button
8071 destroy : function(){
8072 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8073 this.td.parentNode.removeChild(this.td);
8080 this.hidden = false;
8081 this.td.style.display = "";
8089 this.td.style.display = "none";
8094 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8096 * Ext JS Library 1.1.1
8097 * Copyright(c) 2006-2007, Ext JS, LLC.
8099 * Originally Released Under LGPL - original licence link has changed is not relivant.
8102 * <script type="text/javascript">
8106 * @class Roo.PagingToolbar
8107 * @extends Roo.Toolbar
8108 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8110 * Create a new PagingToolbar
8111 * @param {Object} config The config object
8113 Roo.PagingToolbar = function(el, ds, config)
8115 // old args format still supported... - xtype is prefered..
8116 if (typeof(el) == 'object' && el.xtype) {
8117 // created from xtype...
8120 el = config.container;
8124 items = config.items;
8128 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8131 this.renderButtons(this.el);
8134 // supprot items array.
8136 Roo.each(items, function(e) {
8137 this.add(Roo.factory(e));
8142 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8144 * @cfg {Roo.data.Store} dataSource
8145 * The underlying data store providing the paged data
8148 * @cfg {String/HTMLElement/Element} container
8149 * container The id or element that will contain the toolbar
8152 * @cfg {Boolean} displayInfo
8153 * True to display the displayMsg (defaults to false)
8156 * @cfg {Number} pageSize
8157 * The number of records to display per page (defaults to 20)
8161 * @cfg {String} displayMsg
8162 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8164 displayMsg : 'Displaying {0} - {1} of {2}',
8166 * @cfg {String} emptyMsg
8167 * The message to display when no records are found (defaults to "No data to display")
8169 emptyMsg : 'No data to display',
8171 * Customizable piece of the default paging text (defaults to "Page")
8174 beforePageText : "Page",
8176 * Customizable piece of the default paging text (defaults to "of %0")
8179 afterPageText : "of {0}",
8181 * Customizable piece of the default paging text (defaults to "First Page")
8184 firstText : "First Page",
8186 * Customizable piece of the default paging text (defaults to "Previous Page")
8189 prevText : "Previous Page",
8191 * Customizable piece of the default paging text (defaults to "Next Page")
8194 nextText : "Next Page",
8196 * Customizable piece of the default paging text (defaults to "Last Page")
8199 lastText : "Last Page",
8201 * Customizable piece of the default paging text (defaults to "Refresh")
8204 refreshText : "Refresh",
8207 renderButtons : function(el){
8208 Roo.PagingToolbar.superclass.render.call(this, el);
8209 this.first = this.addButton({
8210 tooltip: this.firstText,
8211 cls: "x-btn-icon x-grid-page-first",
8213 handler: this.onClick.createDelegate(this, ["first"])
8215 this.prev = this.addButton({
8216 tooltip: this.prevText,
8217 cls: "x-btn-icon x-grid-page-prev",
8219 handler: this.onClick.createDelegate(this, ["prev"])
8221 //this.addSeparator();
8222 this.add(this.beforePageText);
8223 this.field = Roo.get(this.addDom({
8228 cls: "x-grid-page-number"
8230 this.field.on("keydown", this.onPagingKeydown, this);
8231 this.field.on("focus", function(){this.dom.select();});
8232 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8233 this.field.setHeight(18);
8234 //this.addSeparator();
8235 this.next = this.addButton({
8236 tooltip: this.nextText,
8237 cls: "x-btn-icon x-grid-page-next",
8239 handler: this.onClick.createDelegate(this, ["next"])
8241 this.last = this.addButton({
8242 tooltip: this.lastText,
8243 cls: "x-btn-icon x-grid-page-last",
8245 handler: this.onClick.createDelegate(this, ["last"])
8247 //this.addSeparator();
8248 this.loading = this.addButton({
8249 tooltip: this.refreshText,
8250 cls: "x-btn-icon x-grid-loading",
8251 handler: this.onClick.createDelegate(this, ["refresh"])
8254 if(this.displayInfo){
8255 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8260 updateInfo : function(){
8262 var count = this.ds.getCount();
8263 var msg = count == 0 ?
8267 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
8269 this.displayEl.update(msg);
8274 onLoad : function(ds, r, o){
8275 this.cursor = o.params ? o.params.start : 0;
8276 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8278 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8279 this.field.dom.value = ap;
8280 this.first.setDisabled(ap == 1);
8281 this.prev.setDisabled(ap == 1);
8282 this.next.setDisabled(ap == ps);
8283 this.last.setDisabled(ap == ps);
8284 this.loading.enable();
8289 getPageData : function(){
8290 var total = this.ds.getTotalCount();
8293 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8294 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8299 onLoadError : function(){
8300 this.loading.enable();
8304 onPagingKeydown : function(e){
8306 var d = this.getPageData();
8308 var v = this.field.dom.value, pageNum;
8309 if(!v || isNaN(pageNum = parseInt(v, 10))){
8310 this.field.dom.value = d.activePage;
8313 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8314 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8317 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))
8319 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8320 this.field.dom.value = pageNum;
8321 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8324 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8326 var v = this.field.dom.value, pageNum;
8327 var increment = (e.shiftKey) ? 10 : 1;
8328 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8331 if(!v || isNaN(pageNum = parseInt(v, 10))) {
8332 this.field.dom.value = d.activePage;
8335 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8337 this.field.dom.value = parseInt(v, 10) + increment;
8338 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8339 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8346 beforeLoad : function(){
8348 this.loading.disable();
8353 onClick : function(which){
8357 ds.load({params:{start: 0, limit: this.pageSize}});
8360 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8363 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8366 var total = ds.getTotalCount();
8367 var extra = total % this.pageSize;
8368 var lastStart = extra ? (total - extra) : total-this.pageSize;
8369 ds.load({params:{start: lastStart, limit: this.pageSize}});
8372 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8378 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8379 * @param {Roo.data.Store} store The data store to unbind
8381 unbind : function(ds){
8382 ds.un("beforeload", this.beforeLoad, this);
8383 ds.un("load", this.onLoad, this);
8384 ds.un("loadexception", this.onLoadError, this);
8385 ds.un("remove", this.updateInfo, this);
8386 ds.un("add", this.updateInfo, this);
8387 this.ds = undefined;
8391 * Binds the paging toolbar to the specified {@link Roo.data.Store}
8392 * @param {Roo.data.Store} store The data store to bind
8394 bind : function(ds){
8395 ds.on("beforeload", this.beforeLoad, this);
8396 ds.on("load", this.onLoad, this);
8397 ds.on("loadexception", this.onLoadError, this);
8398 ds.on("remove", this.updateInfo, this);
8399 ds.on("add", this.updateInfo, this);
8404 * Ext JS Library 1.1.1
8405 * Copyright(c) 2006-2007, Ext JS, LLC.
8407 * Originally Released Under LGPL - original licence link has changed is not relivant.
8410 * <script type="text/javascript">
8414 * @class Roo.Resizable
8415 * @extends Roo.util.Observable
8416 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8417 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8418 * 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
8419 * the element will be wrapped for you automatically.</p>
8420 * <p>Here is the list of valid resize handles:</p>
8423 ------ -------------------
8432 'hd' horizontal drag
8435 * <p>Here's an example showing the creation of a typical Resizable:</p>
8437 var resizer = new Roo.Resizable("element-id", {
8445 resizer.on("resize", myHandler);
8447 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8448 * resizer.east.setDisplayed(false);</p>
8449 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8450 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8451 * resize operation's new size (defaults to [0, 0])
8452 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8453 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8454 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8455 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8456 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8457 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8458 * @cfg {Number} width The width of the element in pixels (defaults to null)
8459 * @cfg {Number} height The height of the element in pixels (defaults to null)
8460 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8461 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8462 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8463 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8464 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
8465 * in favor of the handles config option (defaults to false)
8466 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8467 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8468 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8469 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8470 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8471 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8472 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8473 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8474 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8475 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8476 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8478 * Create a new resizable component
8479 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8480 * @param {Object} config configuration options
8482 Roo.Resizable = function(el, config)
8484 this.el = Roo.get(el);
8486 if(config && config.wrap){
8487 config.resizeChild = this.el;
8488 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8489 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8490 this.el.setStyle("overflow", "hidden");
8491 this.el.setPositioning(config.resizeChild.getPositioning());
8492 config.resizeChild.clearPositioning();
8493 if(!config.width || !config.height){
8494 var csize = config.resizeChild.getSize();
8495 this.el.setSize(csize.width, csize.height);
8497 if(config.pinned && !config.adjustments){
8498 config.adjustments = "auto";
8502 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8503 this.proxy.unselectable();
8504 this.proxy.enableDisplayMode('block');
8506 Roo.apply(this, config);
8509 this.disableTrackOver = true;
8510 this.el.addClass("x-resizable-pinned");
8512 // if the element isn't positioned, make it relative
8513 var position = this.el.getStyle("position");
8514 if(position != "absolute" && position != "fixed"){
8515 this.el.setStyle("position", "relative");
8517 if(!this.handles){ // no handles passed, must be legacy style
8518 this.handles = 's,e,se';
8519 if(this.multiDirectional){
8520 this.handles += ',n,w';
8523 if(this.handles == "all"){
8524 this.handles = "n s e w ne nw se sw";
8526 var hs = this.handles.split(/\s*?[,;]\s*?| /);
8527 var ps = Roo.Resizable.positions;
8528 for(var i = 0, len = hs.length; i < len; i++){
8529 if(hs[i] && ps[hs[i]]){
8530 var pos = ps[hs[i]];
8531 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8535 this.corner = this.southeast;
8537 // updateBox = the box can move..
8538 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8539 this.updateBox = true;
8542 this.activeHandle = null;
8544 if(this.resizeChild){
8545 if(typeof this.resizeChild == "boolean"){
8546 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8548 this.resizeChild = Roo.get(this.resizeChild, true);
8552 if(this.adjustments == "auto"){
8553 var rc = this.resizeChild;
8554 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8555 if(rc && (hw || hn)){
8556 rc.position("relative");
8557 rc.setLeft(hw ? hw.el.getWidth() : 0);
8558 rc.setTop(hn ? hn.el.getHeight() : 0);
8560 this.adjustments = [
8561 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8562 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8567 this.dd = this.dynamic ?
8568 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8569 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8575 * @event beforeresize
8576 * Fired before resize is allowed. Set enabled to false to cancel resize.
8577 * @param {Roo.Resizable} this
8578 * @param {Roo.EventObject} e The mousedown event
8580 "beforeresize" : true,
8584 * @param {Roo.Resizable} this
8585 * @param {Number} x The new x position
8586 * @param {Number} y The new y position
8587 * @param {Number} w The new w width
8588 * @param {Number} h The new h hight
8589 * @param {Roo.EventObject} e The mouseup event
8594 * Fired after a resize.
8595 * @param {Roo.Resizable} this
8596 * @param {Number} width The new width
8597 * @param {Number} height The new height
8598 * @param {Roo.EventObject} e The mouseup event
8603 if(this.width !== null && this.height !== null){
8604 this.resizeTo(this.width, this.height);
8606 this.updateChildSize();
8609 this.el.dom.style.zoom = 1;
8611 Roo.Resizable.superclass.constructor.call(this);
8614 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8615 resizeChild : false,
8616 adjustments : [0, 0],
8626 multiDirectional : false,
8627 disableTrackOver : false,
8628 easing : 'easeOutStrong',
8630 heightIncrement : 0,
8634 preserveRatio : false,
8641 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8643 constrainTo: undefined,
8645 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8647 resizeRegion: undefined,
8651 * Perform a manual resize
8652 * @param {Number} width
8653 * @param {Number} height
8655 resizeTo : function(width, height){
8656 this.el.setSize(width, height);
8657 this.updateChildSize();
8658 this.fireEvent("resize", this, width, height, null);
8662 startSizing : function(e, handle){
8663 this.fireEvent("beforeresize", this, e);
8664 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8667 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
8668 this.overlay.unselectable();
8669 this.overlay.enableDisplayMode("block");
8670 this.overlay.on("mousemove", this.onMouseMove, this);
8671 this.overlay.on("mouseup", this.onMouseUp, this);
8673 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8675 this.resizing = true;
8676 this.startBox = this.el.getBox();
8677 this.startPoint = e.getXY();
8678 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8679 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8681 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8682 this.overlay.show();
8684 if(this.constrainTo) {
8685 var ct = Roo.get(this.constrainTo);
8686 this.resizeRegion = ct.getRegion().adjust(
8687 ct.getFrameWidth('t'),
8688 ct.getFrameWidth('l'),
8689 -ct.getFrameWidth('b'),
8690 -ct.getFrameWidth('r')
8694 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8696 this.proxy.setBox(this.startBox);
8698 this.proxy.setStyle('visibility', 'visible');
8704 onMouseDown : function(handle, e){
8707 this.activeHandle = handle;
8708 this.startSizing(e, handle);
8713 onMouseUp : function(e){
8714 var size = this.resizeElement();
8715 this.resizing = false;
8717 this.overlay.hide();
8719 this.fireEvent("resize", this, size.width, size.height, e);
8723 updateChildSize : function(){
8725 if(this.resizeChild){
8727 var child = this.resizeChild;
8728 var adj = this.adjustments;
8729 if(el.dom.offsetWidth){
8730 var b = el.getSize(true);
8731 child.setSize(b.width+adj[0], b.height+adj[1]);
8733 // Second call here for IE
8734 // The first call enables instant resizing and
8735 // the second call corrects scroll bars if they
8738 setTimeout(function(){
8739 if(el.dom.offsetWidth){
8740 var b = el.getSize(true);
8741 child.setSize(b.width+adj[0], b.height+adj[1]);
8749 snap : function(value, inc, min){
8750 if(!inc || !value) {
8753 var newValue = value;
8754 var m = value % inc;
8757 newValue = value + (inc-m);
8759 newValue = value - m;
8762 return Math.max(min, newValue);
8766 resizeElement : function(){
8767 var box = this.proxy.getBox();
8769 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8771 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8773 this.updateChildSize();
8781 constrain : function(v, diff, m, mx){
8784 }else if(v - diff > mx){
8791 onMouseMove : function(e){
8794 try{// try catch so if something goes wrong the user doesn't get hung
8796 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8800 //var curXY = this.startPoint;
8801 var curSize = this.curSize || this.startBox;
8802 var x = this.startBox.x, y = this.startBox.y;
8804 var w = curSize.width, h = curSize.height;
8806 var mw = this.minWidth, mh = this.minHeight;
8807 var mxw = this.maxWidth, mxh = this.maxHeight;
8808 var wi = this.widthIncrement;
8809 var hi = this.heightIncrement;
8811 var eventXY = e.getXY();
8812 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8813 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8815 var pos = this.activeHandle.position;
8820 w = Math.min(Math.max(mw, w), mxw);
8825 h = Math.min(Math.max(mh, h), mxh);
8830 w = Math.min(Math.max(mw, w), mxw);
8831 h = Math.min(Math.max(mh, h), mxh);
8834 diffY = this.constrain(h, diffY, mh, mxh);
8841 var adiffX = Math.abs(diffX);
8842 var sub = (adiffX % wi); // how much
8843 if (sub > (wi/2)) { // far enough to snap
8844 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8846 // remove difference..
8847 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8851 x = Math.max(this.minX, x);
8854 diffX = this.constrain(w, diffX, mw, mxw);
8860 w = Math.min(Math.max(mw, w), mxw);
8861 diffY = this.constrain(h, diffY, mh, mxh);
8866 diffX = this.constrain(w, diffX, mw, mxw);
8867 diffY = this.constrain(h, diffY, mh, mxh);
8874 diffX = this.constrain(w, diffX, mw, mxw);
8876 h = Math.min(Math.max(mh, h), mxh);
8882 var sw = this.snap(w, wi, mw);
8883 var sh = this.snap(h, hi, mh);
8884 if(sw != w || sh != h){
8907 if(this.preserveRatio){
8912 h = Math.min(Math.max(mh, h), mxh);
8917 w = Math.min(Math.max(mw, w), mxw);
8922 w = Math.min(Math.max(mw, w), mxw);
8928 w = Math.min(Math.max(mw, w), mxw);
8934 h = Math.min(Math.max(mh, h), mxh);
8942 h = Math.min(Math.max(mh, h), mxh);
8952 h = Math.min(Math.max(mh, h), mxh);
8960 if (pos == 'hdrag') {
8963 this.proxy.setBounds(x, y, w, h);
8965 this.resizeElement();
8969 this.fireEvent("resizing", this, x, y, w, h, e);
8973 handleOver : function(){
8975 this.el.addClass("x-resizable-over");
8980 handleOut : function(){
8982 this.el.removeClass("x-resizable-over");
8987 * Returns the element this component is bound to.
8988 * @return {Roo.Element}
8995 * Returns the resizeChild element (or null).
8996 * @return {Roo.Element}
8998 getResizeChild : function(){
8999 return this.resizeChild;
9001 groupHandler : function()
9006 * Destroys this resizable. If the element was wrapped and
9007 * removeEl is not true then the element remains.
9008 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9010 destroy : function(removeEl){
9011 this.proxy.remove();
9013 this.overlay.removeAllListeners();
9014 this.overlay.remove();
9016 var ps = Roo.Resizable.positions;
9018 if(typeof ps[k] != "function" && this[ps[k]]){
9019 var h = this[ps[k]];
9020 h.el.removeAllListeners();
9032 // hash to map config positions to true positions
9033 Roo.Resizable.positions = {
9034 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
9039 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
9041 // only initialize the template if resizable is used
9042 var tpl = Roo.DomHelper.createTemplate(
9043 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
9046 Roo.Resizable.Handle.prototype.tpl = tpl;
9048 this.position = pos;
9050 // show north drag fro topdra
9051 var handlepos = pos == 'hdrag' ? 'north' : pos;
9053 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
9054 if (pos == 'hdrag') {
9055 this.el.setStyle('cursor', 'pointer');
9057 this.el.unselectable();
9059 this.el.setOpacity(0);
9061 this.el.on("mousedown", this.onMouseDown, this);
9062 if(!disableTrackOver){
9063 this.el.on("mouseover", this.onMouseOver, this);
9064 this.el.on("mouseout", this.onMouseOut, this);
9069 Roo.Resizable.Handle.prototype = {
9070 afterResize : function(rz){
9075 onMouseDown : function(e){
9076 this.rz.onMouseDown(this, e);
9079 onMouseOver : function(e){
9080 this.rz.handleOver(this, e);
9083 onMouseOut : function(e){
9084 this.rz.handleOut(this, e);
9088 * Ext JS Library 1.1.1
9089 * Copyright(c) 2006-2007, Ext JS, LLC.
9091 * Originally Released Under LGPL - original licence link has changed is not relivant.
9094 * <script type="text/javascript">
9099 * @extends Roo.Component
9100 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9102 * Create a new Editor
9103 * @param {Roo.form.Field} field The Field object (or descendant)
9104 * @param {Object} config The config object
9106 Roo.Editor = function(field, config){
9107 Roo.Editor.superclass.constructor.call(this, config);
9111 * @event beforestartedit
9112 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
9113 * false from the handler of this event.
9114 * @param {Editor} this
9115 * @param {Roo.Element} boundEl The underlying element bound to this editor
9116 * @param {Mixed} value The field value being set
9118 "beforestartedit" : true,
9121 * Fires when this editor is displayed
9122 * @param {Roo.Element} boundEl The underlying element bound to this editor
9123 * @param {Mixed} value The starting field value
9127 * @event beforecomplete
9128 * Fires after a change has been made to the field, but before the change is reflected in the underlying
9129 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
9130 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9131 * event will not fire since no edit actually occurred.
9132 * @param {Editor} this
9133 * @param {Mixed} value The current field value
9134 * @param {Mixed} startValue The original field value
9136 "beforecomplete" : true,
9139 * Fires after editing is complete and any changed value has been written to the underlying field.
9140 * @param {Editor} this
9141 * @param {Mixed} value The current field value
9142 * @param {Mixed} startValue The original field value
9147 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9148 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9149 * @param {Roo.form.Field} this
9150 * @param {Roo.EventObject} e The event object
9156 Roo.extend(Roo.Editor, Roo.Component, {
9158 * @cfg {Boolean/String} autosize
9159 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9160 * or "height" to adopt the height only (defaults to false)
9163 * @cfg {Boolean} revertInvalid
9164 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9165 * validation fails (defaults to true)
9168 * @cfg {Boolean} ignoreNoChange
9169 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9170 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
9171 * will never be ignored.
9174 * @cfg {Boolean} hideEl
9175 * False to keep the bound element visible while the editor is displayed (defaults to true)
9178 * @cfg {Mixed} value
9179 * The data value of the underlying field (defaults to "")
9183 * @cfg {String} alignment
9184 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9188 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9189 * for bottom-right shadow (defaults to "frame")
9193 * @cfg {Boolean} constrain True to constrain the editor to the viewport
9197 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9199 completeOnEnter : false,
9201 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9203 cancelOnEsc : false,
9205 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9210 onRender : function(ct, position){
9211 this.el = new Roo.Layer({
9212 shadow: this.shadow,
9218 constrain: this.constrain
9220 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9221 if(this.field.msgTarget != 'title'){
9222 this.field.msgTarget = 'qtip';
9224 this.field.render(this.el);
9226 this.field.el.dom.setAttribute('autocomplete', 'off');
9228 this.field.on("specialkey", this.onSpecialKey, this);
9229 if(this.swallowKeys){
9230 this.field.el.swallowEvent(['keydown','keypress']);
9233 this.field.on("blur", this.onBlur, this);
9234 if(this.field.grow){
9235 this.field.on("autosize", this.el.sync, this.el, {delay:1});
9239 onSpecialKey : function(field, e)
9241 //Roo.log('editor onSpecialKey');
9242 if(this.completeOnEnter && e.getKey() == e.ENTER){
9244 this.completeEdit();
9247 // do not fire special key otherwise it might hide close the editor...
9248 if(e.getKey() == e.ENTER){
9251 if(this.cancelOnEsc && e.getKey() == e.ESC){
9255 this.fireEvent('specialkey', field, e);
9260 * Starts the editing process and shows the editor.
9261 * @param {String/HTMLElement/Element} el The element to edit
9262 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9263 * to the innerHTML of el.
9265 startEdit : function(el, value){
9267 this.completeEdit();
9269 this.boundEl = Roo.get(el);
9270 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9272 this.render(this.parentEl || document.body);
9274 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9277 this.startValue = v;
9278 this.field.setValue(v);
9280 var sz = this.boundEl.getSize();
9281 switch(this.autoSize){
9283 this.setSize(sz.width, "");
9286 this.setSize("", sz.height);
9289 this.setSize(sz.width, sz.height);
9292 this.el.alignTo(this.boundEl, this.alignment);
9293 this.editing = true;
9295 Roo.QuickTips.disable();
9301 * Sets the height and width of this editor.
9302 * @param {Number} width The new width
9303 * @param {Number} height The new height
9305 setSize : function(w, h){
9306 this.field.setSize(w, h);
9313 * Realigns the editor to the bound field based on the current alignment config value.
9315 realign : function(){
9316 this.el.alignTo(this.boundEl, this.alignment);
9320 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9321 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9323 completeEdit : function(remainVisible){
9327 var v = this.getValue();
9328 if(this.revertInvalid !== false && !this.field.isValid()){
9329 v = this.startValue;
9330 this.cancelEdit(true);
9332 if(String(v) === String(this.startValue) && this.ignoreNoChange){
9333 this.editing = false;
9337 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9338 this.editing = false;
9339 if(this.updateEl && this.boundEl){
9340 this.boundEl.update(v);
9342 if(remainVisible !== true){
9345 this.fireEvent("complete", this, v, this.startValue);
9350 onShow : function(){
9352 if(this.hideEl !== false){
9353 this.boundEl.hide();
9356 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9357 this.fixIEFocus = true;
9358 this.deferredFocus.defer(50, this);
9362 this.fireEvent("startedit", this.boundEl, this.startValue);
9365 deferredFocus : function(){
9372 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
9373 * reverted to the original starting value.
9374 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9375 * cancel (defaults to false)
9377 cancelEdit : function(remainVisible){
9379 this.setValue(this.startValue);
9380 if(remainVisible !== true){
9387 onBlur : function(){
9388 if(this.allowBlur !== true && this.editing){
9389 this.completeEdit();
9394 onHide : function(){
9396 this.completeEdit();
9400 if(this.field.collapse){
9401 this.field.collapse();
9404 if(this.hideEl !== false){
9405 this.boundEl.show();
9408 Roo.QuickTips.enable();
9413 * Sets the data value of the editor
9414 * @param {Mixed} value Any valid value supported by the underlying field
9416 setValue : function(v){
9417 this.field.setValue(v);
9421 * Gets the data value of the editor
9422 * @return {Mixed} The data value
9424 getValue : function(){
9425 return this.field.getValue();
9429 * Ext JS Library 1.1.1
9430 * Copyright(c) 2006-2007, Ext JS, LLC.
9432 * Originally Released Under LGPL - original licence link has changed is not relivant.
9435 * <script type="text/javascript">
9439 * @class Roo.BasicDialog
9440 * @extends Roo.util.Observable
9441 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
9443 var dlg = new Roo.BasicDialog("my-dlg", {
9452 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9453 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
9454 dlg.addButton('Cancel', dlg.hide, dlg);
9457 <b>A Dialog should always be a direct child of the body element.</b>
9458 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9459 * @cfg {String} title Default text to display in the title bar (defaults to null)
9460 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9461 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9462 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9463 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9464 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9465 * (defaults to null with no animation)
9466 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9467 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9468 * property for valid values (defaults to 'all')
9469 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9470 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9471 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9472 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9473 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9474 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9475 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9476 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9477 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9478 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9479 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9480 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9481 * draggable = true (defaults to false)
9482 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9483 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9484 * shadow (defaults to false)
9485 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9486 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9487 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9488 * @cfg {Array} buttons Array of buttons
9489 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9491 * Create a new BasicDialog.
9492 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9493 * @param {Object} config Configuration options
9495 Roo.BasicDialog = function(el, config){
9496 this.el = Roo.get(el);
9497 var dh = Roo.DomHelper;
9498 if(!this.el && config && config.autoCreate){
9499 if(typeof config.autoCreate == "object"){
9500 if(!config.autoCreate.id){
9501 config.autoCreate.id = el;
9503 this.el = dh.append(document.body,
9504 config.autoCreate, true);
9506 this.el = dh.append(document.body,
9507 {tag: "div", id: el, style:'visibility:hidden;'}, true);
9511 el.setDisplayed(true);
9512 el.hide = this.hideAction;
9514 el.addClass("x-dlg");
9516 Roo.apply(this, config);
9518 this.proxy = el.createProxy("x-dlg-proxy");
9519 this.proxy.hide = this.hideAction;
9520 this.proxy.setOpacity(.5);
9524 el.setWidth(config.width);
9527 el.setHeight(config.height);
9529 this.size = el.getSize();
9530 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9531 this.xy = [config.x,config.y];
9533 this.xy = el.getCenterXY(true);
9535 /** The header element @type Roo.Element */
9536 this.header = el.child("> .x-dlg-hd");
9537 /** The body element @type Roo.Element */
9538 this.body = el.child("> .x-dlg-bd");
9539 /** The footer element @type Roo.Element */
9540 this.footer = el.child("> .x-dlg-ft");
9543 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
9546 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9549 this.header.unselectable();
9551 this.header.update(this.title);
9553 // this element allows the dialog to be focused for keyboard event
9554 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9555 this.focusEl.swallowEvent("click", true);
9557 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9559 // wrap the body and footer for special rendering
9560 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9562 this.bwrap.dom.appendChild(this.footer.dom);
9565 this.bg = this.el.createChild({
9566 tag: "div", cls:"x-dlg-bg",
9567 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
9569 this.centerBg = this.bg.child("div.x-dlg-bg-center");
9572 if(this.autoScroll !== false && !this.autoTabs){
9573 this.body.setStyle("overflow", "auto");
9576 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9578 if(this.closable !== false){
9579 this.el.addClass("x-dlg-closable");
9580 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9581 this.close.on("click", this.closeClick, this);
9582 this.close.addClassOnOver("x-dlg-close-over");
9584 if(this.collapsible !== false){
9585 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9586 this.collapseBtn.on("click", this.collapseClick, this);
9587 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9588 this.header.on("dblclick", this.collapseClick, this);
9590 if(this.resizable !== false){
9591 this.el.addClass("x-dlg-resizable");
9592 this.resizer = new Roo.Resizable(el, {
9593 minWidth: this.minWidth || 80,
9594 minHeight:this.minHeight || 80,
9595 handles: this.resizeHandles || "all",
9598 this.resizer.on("beforeresize", this.beforeResize, this);
9599 this.resizer.on("resize", this.onResize, this);
9601 if(this.draggable !== false){
9602 el.addClass("x-dlg-draggable");
9603 if (!this.proxyDrag) {
9604 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9607 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9609 dd.setHandleElId(this.header.id);
9610 dd.endDrag = this.endMove.createDelegate(this);
9611 dd.startDrag = this.startMove.createDelegate(this);
9612 dd.onDrag = this.onDrag.createDelegate(this);
9617 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9618 this.mask.enableDisplayMode("block");
9620 this.el.addClass("x-dlg-modal");
9623 this.shadow = new Roo.Shadow({
9624 mode : typeof this.shadow == "string" ? this.shadow : "sides",
9625 offset : this.shadowOffset
9628 this.shadowOffset = 0;
9630 if(Roo.useShims && this.shim !== false){
9631 this.shim = this.el.createShim();
9632 this.shim.hide = this.hideAction;
9641 var bts= this.buttons;
9643 Roo.each(bts, function(b) {
9652 * Fires when a key is pressed
9653 * @param {Roo.BasicDialog} this
9654 * @param {Roo.EventObject} e
9659 * Fires when this dialog is moved by the user.
9660 * @param {Roo.BasicDialog} this
9661 * @param {Number} x The new page X
9662 * @param {Number} y The new page Y
9667 * Fires when this dialog is resized by the user.
9668 * @param {Roo.BasicDialog} this
9669 * @param {Number} width The new width
9670 * @param {Number} height The new height
9675 * Fires before this dialog is hidden.
9676 * @param {Roo.BasicDialog} this
9678 "beforehide" : true,
9681 * Fires when this dialog is hidden.
9682 * @param {Roo.BasicDialog} this
9687 * Fires before this dialog is shown.
9688 * @param {Roo.BasicDialog} this
9690 "beforeshow" : true,
9693 * Fires when this dialog is shown.
9694 * @param {Roo.BasicDialog} this
9698 el.on("keydown", this.onKeyDown, this);
9699 el.on("mousedown", this.toFront, this);
9700 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9702 Roo.DialogManager.register(this);
9703 Roo.BasicDialog.superclass.constructor.call(this);
9706 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9707 shadowOffset: Roo.isIE ? 6 : 5,
9711 defaultButton: null,
9712 buttonAlign: "right",
9717 * Sets the dialog title text
9718 * @param {String} text The title text to display
9719 * @return {Roo.BasicDialog} this
9721 setTitle : function(text){
9722 this.header.update(text);
9727 closeClick : function(){
9732 collapseClick : function(){
9733 this[this.collapsed ? "expand" : "collapse"]();
9737 * Collapses the dialog to its minimized state (only the title bar is visible).
9738 * Equivalent to the user clicking the collapse dialog button.
9740 collapse : function(){
9741 if(!this.collapsed){
9742 this.collapsed = true;
9743 this.el.addClass("x-dlg-collapsed");
9744 this.restoreHeight = this.el.getHeight();
9745 this.resizeTo(this.el.getWidth(), this.header.getHeight());
9750 * Expands a collapsed dialog back to its normal state. Equivalent to the user
9751 * clicking the expand dialog button.
9753 expand : function(){
9755 this.collapsed = false;
9756 this.el.removeClass("x-dlg-collapsed");
9757 this.resizeTo(this.el.getWidth(), this.restoreHeight);
9762 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9763 * @return {Roo.TabPanel} The tabs component
9765 initTabs : function(){
9766 var tabs = this.getTabs();
9767 while(tabs.getTab(0)){
9770 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9772 tabs.addTab(Roo.id(dom), dom.title);
9780 beforeResize : function(){
9781 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9785 onResize : function(){
9787 this.syncBodyHeight();
9788 this.adjustAssets();
9790 this.fireEvent("resize", this, this.size.width, this.size.height);
9794 onKeyDown : function(e){
9795 if(this.isVisible()){
9796 this.fireEvent("keydown", this, e);
9801 * Resizes the dialog.
9802 * @param {Number} width
9803 * @param {Number} height
9804 * @return {Roo.BasicDialog} this
9806 resizeTo : function(width, height){
9807 this.el.setSize(width, height);
9808 this.size = {width: width, height: height};
9809 this.syncBodyHeight();
9810 if(this.fixedcenter){
9813 if(this.isVisible()){
9815 this.adjustAssets();
9817 this.fireEvent("resize", this, width, height);
9823 * Resizes the dialog to fit the specified content size.
9824 * @param {Number} width
9825 * @param {Number} height
9826 * @return {Roo.BasicDialog} this
9828 setContentSize : function(w, h){
9829 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9830 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9831 //if(!this.el.isBorderBox()){
9832 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9833 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9836 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9837 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9839 this.resizeTo(w, h);
9844 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
9845 * executed in response to a particular key being pressed while the dialog is active.
9846 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9847 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9848 * @param {Function} fn The function to call
9849 * @param {Object} scope (optional) The scope of the function
9850 * @return {Roo.BasicDialog} this
9852 addKeyListener : function(key, fn, scope){
9853 var keyCode, shift, ctrl, alt;
9854 if(typeof key == "object" && !(key instanceof Array)){
9855 keyCode = key["key"];
9856 shift = key["shift"];
9862 var handler = function(dlg, e){
9863 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
9865 if(keyCode instanceof Array){
9866 for(var i = 0, len = keyCode.length; i < len; i++){
9867 if(keyCode[i] == k){
9868 fn.call(scope || window, dlg, k, e);
9874 fn.call(scope || window, dlg, k, e);
9879 this.on("keydown", handler);
9884 * Returns the TabPanel component (creates it if it doesn't exist).
9885 * Note: If you wish to simply check for the existence of tabs without creating them,
9886 * check for a null 'tabs' property.
9887 * @return {Roo.TabPanel} The tabs component
9889 getTabs : function(){
9891 this.el.addClass("x-dlg-auto-tabs");
9892 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9893 this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9899 * Adds a button to the footer section of the dialog.
9900 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9901 * object or a valid Roo.DomHelper element config
9902 * @param {Function} handler The function called when the button is clicked
9903 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9904 * @return {Roo.Button} The new button
9906 addButton : function(config, handler, scope){
9907 var dh = Roo.DomHelper;
9909 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9911 if(!this.btnContainer){
9912 var tb = this.footer.createChild({
9914 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9915 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9917 this.btnContainer = tb.firstChild.firstChild.firstChild;
9922 minWidth: this.minButtonWidth,
9925 if(typeof config == "string"){
9926 bconfig.text = config;
9929 bconfig.dhconfig = config;
9931 Roo.apply(bconfig, config);
9935 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9936 bconfig.position = Math.max(0, bconfig.position);
9937 fc = this.btnContainer.childNodes[bconfig.position];
9940 var btn = new Roo.Button(
9942 this.btnContainer.insertBefore(document.createElement("td"),fc)
9943 : this.btnContainer.appendChild(document.createElement("td")),
9944 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
9947 this.syncBodyHeight();
9950 * Array of all the buttons that have been added to this dialog via addButton
9955 this.buttons.push(btn);
9960 * Sets the default button to be focused when the dialog is displayed.
9961 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9962 * @return {Roo.BasicDialog} this
9964 setDefaultButton : function(btn){
9965 this.defaultButton = btn;
9970 getHeaderFooterHeight : function(safe){
9973 height += this.header.getHeight();
9976 var fm = this.footer.getMargins();
9977 height += (this.footer.getHeight()+fm.top+fm.bottom);
9979 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9980 height += this.centerBg.getPadding("tb");
9985 syncBodyHeight : function()
9987 var bd = this.body, // the text
9988 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9990 var height = this.size.height - this.getHeaderFooterHeight(false);
9991 bd.setHeight(height-bd.getMargins("tb"));
9992 var hh = this.header.getHeight();
9993 var h = this.size.height-hh;
9996 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9997 bw.setHeight(h-cb.getPadding("tb"));
9999 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
10000 bd.setWidth(bw.getWidth(true));
10002 this.tabs.syncHeight();
10004 this.tabs.el.repaint();
10010 * Restores the previous state of the dialog if Roo.state is configured.
10011 * @return {Roo.BasicDialog} this
10013 restoreState : function(){
10014 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
10015 if(box && box.width){
10016 this.xy = [box.x, box.y];
10017 this.resizeTo(box.width, box.height);
10023 beforeShow : function(){
10025 if(this.fixedcenter){
10026 this.xy = this.el.getCenterXY(true);
10029 Roo.get(document.body).addClass("x-body-masked");
10030 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10033 this.constrainXY();
10037 animShow : function(){
10038 var b = Roo.get(this.animateTarget).getBox();
10039 this.proxy.setSize(b.width, b.height);
10040 this.proxy.setLocation(b.x, b.y);
10042 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
10043 true, .35, this.showEl.createDelegate(this));
10047 * Shows the dialog.
10048 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
10049 * @return {Roo.BasicDialog} this
10051 show : function(animateTarget){
10052 if (this.fireEvent("beforeshow", this) === false){
10055 if(this.syncHeightBeforeShow){
10056 this.syncBodyHeight();
10057 }else if(this.firstShow){
10058 this.firstShow = false;
10059 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10061 this.animateTarget = animateTarget || this.animateTarget;
10062 if(!this.el.isVisible()){
10064 if(this.animateTarget && Roo.get(this.animateTarget)){
10074 showEl : function(){
10076 this.el.setXY(this.xy);
10078 this.adjustAssets(true);
10081 // IE peekaboo bug - fix found by Dave Fenwick
10085 this.fireEvent("show", this);
10089 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
10090 * dialog itself will receive focus.
10092 focus : function(){
10093 if(this.defaultButton){
10094 this.defaultButton.focus();
10096 this.focusEl.focus();
10101 constrainXY : function(){
10102 if(this.constraintoviewport !== false){
10103 if(!this.viewSize){
10104 if(this.container){
10105 var s = this.container.getSize();
10106 this.viewSize = [s.width, s.height];
10108 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10111 var s = Roo.get(this.container||document).getScroll();
10113 var x = this.xy[0], y = this.xy[1];
10114 var w = this.size.width, h = this.size.height;
10115 var vw = this.viewSize[0], vh = this.viewSize[1];
10116 // only move it if it needs it
10118 // first validate right/bottom
10119 if(x + w > vw+s.left){
10123 if(y + h > vh+s.top){
10127 // then make sure top/left isn't negative
10139 if(this.isVisible()){
10140 this.el.setLocation(x, y);
10141 this.adjustAssets();
10148 onDrag : function(){
10149 if(!this.proxyDrag){
10150 this.xy = this.el.getXY();
10151 this.adjustAssets();
10156 adjustAssets : function(doShow){
10157 var x = this.xy[0], y = this.xy[1];
10158 var w = this.size.width, h = this.size.height;
10159 if(doShow === true){
10161 this.shadow.show(this.el);
10167 if(this.shadow && this.shadow.isVisible()){
10168 this.shadow.show(this.el);
10170 if(this.shim && this.shim.isVisible()){
10171 this.shim.setBounds(x, y, w, h);
10176 adjustViewport : function(w, h){
10178 w = Roo.lib.Dom.getViewWidth();
10179 h = Roo.lib.Dom.getViewHeight();
10182 this.viewSize = [w, h];
10183 if(this.modal && this.mask.isVisible()){
10184 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10185 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10187 if(this.isVisible()){
10188 this.constrainXY();
10193 * Destroys this dialog and all its supporting elements (including any tabs, shim,
10194 * shadow, proxy, mask, etc.) Also removes all event listeners.
10195 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10197 destroy : function(removeEl){
10198 if(this.isVisible()){
10199 this.animateTarget = null;
10202 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10204 this.tabs.destroy(removeEl);
10217 for(var i = 0, len = this.buttons.length; i < len; i++){
10218 this.buttons[i].destroy();
10221 this.el.removeAllListeners();
10222 if(removeEl === true){
10223 this.el.update("");
10226 Roo.DialogManager.unregister(this);
10230 startMove : function(){
10231 if(this.proxyDrag){
10234 if(this.constraintoviewport !== false){
10235 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10240 endMove : function(){
10241 if(!this.proxyDrag){
10242 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10244 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10247 this.refreshSize();
10248 this.adjustAssets();
10250 this.fireEvent("move", this, this.xy[0], this.xy[1]);
10254 * Brings this dialog to the front of any other visible dialogs
10255 * @return {Roo.BasicDialog} this
10257 toFront : function(){
10258 Roo.DialogManager.bringToFront(this);
10263 * Sends this dialog to the back (under) of any other visible dialogs
10264 * @return {Roo.BasicDialog} this
10266 toBack : function(){
10267 Roo.DialogManager.sendToBack(this);
10272 * Centers this dialog in the viewport
10273 * @return {Roo.BasicDialog} this
10275 center : function(){
10276 var xy = this.el.getCenterXY(true);
10277 this.moveTo(xy[0], xy[1]);
10282 * Moves the dialog's top-left corner to the specified point
10283 * @param {Number} x
10284 * @param {Number} y
10285 * @return {Roo.BasicDialog} this
10287 moveTo : function(x, y){
10289 if(this.isVisible()){
10290 this.el.setXY(this.xy);
10291 this.adjustAssets();
10297 * Aligns the dialog to the specified element
10298 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10299 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10300 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10301 * @return {Roo.BasicDialog} this
10303 alignTo : function(element, position, offsets){
10304 this.xy = this.el.getAlignToXY(element, position, offsets);
10305 if(this.isVisible()){
10306 this.el.setXY(this.xy);
10307 this.adjustAssets();
10313 * Anchors an element to another element and realigns it when the window is resized.
10314 * @param {String/HTMLElement/Roo.Element} element The element to align to.
10315 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10316 * @param {Array} offsets (optional) Offset the positioning by [x, y]
10317 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10318 * is a number, it is used as the buffer delay (defaults to 50ms).
10319 * @return {Roo.BasicDialog} this
10321 anchorTo : function(el, alignment, offsets, monitorScroll){
10322 var action = function(){
10323 this.alignTo(el, alignment, offsets);
10325 Roo.EventManager.onWindowResize(action, this);
10326 var tm = typeof monitorScroll;
10327 if(tm != 'undefined'){
10328 Roo.EventManager.on(window, 'scroll', action, this,
10329 {buffer: tm == 'number' ? monitorScroll : 50});
10336 * Returns true if the dialog is visible
10337 * @return {Boolean}
10339 isVisible : function(){
10340 return this.el.isVisible();
10344 animHide : function(callback){
10345 var b = Roo.get(this.animateTarget).getBox();
10347 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10349 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10350 this.hideEl.createDelegate(this, [callback]));
10354 * Hides the dialog.
10355 * @param {Function} callback (optional) Function to call when the dialog is hidden
10356 * @return {Roo.BasicDialog} this
10358 hide : function(callback){
10359 if (this.fireEvent("beforehide", this) === false){
10363 this.shadow.hide();
10368 // sometimes animateTarget seems to get set.. causing problems...
10369 // this just double checks..
10370 if(this.animateTarget && Roo.get(this.animateTarget)) {
10371 this.animHide(callback);
10374 this.hideEl(callback);
10380 hideEl : function(callback){
10384 Roo.get(document.body).removeClass("x-body-masked");
10386 this.fireEvent("hide", this);
10387 if(typeof callback == "function"){
10393 hideAction : function(){
10394 this.setLeft("-10000px");
10395 this.setTop("-10000px");
10396 this.setStyle("visibility", "hidden");
10400 refreshSize : function(){
10401 this.size = this.el.getSize();
10402 this.xy = this.el.getXY();
10403 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10407 // z-index is managed by the DialogManager and may be overwritten at any time
10408 setZIndex : function(index){
10410 this.mask.setStyle("z-index", index);
10413 this.shim.setStyle("z-index", ++index);
10416 this.shadow.setZIndex(++index);
10418 this.el.setStyle("z-index", ++index);
10420 this.proxy.setStyle("z-index", ++index);
10423 this.resizer.proxy.setStyle("z-index", ++index);
10426 this.lastZIndex = index;
10430 * Returns the element for this dialog
10431 * @return {Roo.Element} The underlying dialog Element
10433 getEl : function(){
10439 * @class Roo.DialogManager
10440 * Provides global access to BasicDialogs that have been created and
10441 * support for z-indexing (layering) multiple open dialogs.
10443 Roo.DialogManager = function(){
10445 var accessList = [];
10449 var sortDialogs = function(d1, d2){
10450 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10454 var orderDialogs = function(){
10455 accessList.sort(sortDialogs);
10456 var seed = Roo.DialogManager.zseed;
10457 for(var i = 0, len = accessList.length; i < len; i++){
10458 var dlg = accessList[i];
10460 dlg.setZIndex(seed + (i*10));
10467 * The starting z-index for BasicDialogs (defaults to 9000)
10468 * @type Number The z-index value
10473 register : function(dlg){
10474 list[dlg.id] = dlg;
10475 accessList.push(dlg);
10479 unregister : function(dlg){
10480 delete list[dlg.id];
10483 if(!accessList.indexOf){
10484 for( i = 0, len = accessList.length; i < len; i++){
10485 if(accessList[i] == dlg){
10486 accessList.splice(i, 1);
10491 i = accessList.indexOf(dlg);
10493 accessList.splice(i, 1);
10499 * Gets a registered dialog by id
10500 * @param {String/Object} id The id of the dialog or a dialog
10501 * @return {Roo.BasicDialog} this
10503 get : function(id){
10504 return typeof id == "object" ? id : list[id];
10508 * Brings the specified dialog to the front
10509 * @param {String/Object} dlg The id of the dialog or a dialog
10510 * @return {Roo.BasicDialog} this
10512 bringToFront : function(dlg){
10513 dlg = this.get(dlg);
10516 dlg._lastAccess = new Date().getTime();
10523 * Sends the specified dialog to the back
10524 * @param {String/Object} dlg The id of the dialog or a dialog
10525 * @return {Roo.BasicDialog} this
10527 sendToBack : function(dlg){
10528 dlg = this.get(dlg);
10529 dlg._lastAccess = -(new Date().getTime());
10535 * Hides all dialogs
10537 hideAll : function(){
10538 for(var id in list){
10539 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10548 * @class Roo.LayoutDialog
10549 * @extends Roo.BasicDialog
10550 * Dialog which provides adjustments for working with a layout in a Dialog.
10551 * Add your necessary layout config options to the dialog's config.<br>
10552 * Example usage (including a nested layout):
10555 dialog = new Roo.LayoutDialog("download-dlg", {
10564 // layout config merges with the dialog config
10566 tabPosition: "top",
10567 alwaysShowTabs: true
10570 dialog.addKeyListener(27, dialog.hide, dialog);
10571 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10572 dialog.addButton("Build It!", this.getDownload, this);
10574 // we can even add nested layouts
10575 var innerLayout = new Roo.BorderLayout("dl-inner", {
10585 innerLayout.beginUpdate();
10586 innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10587 innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10588 innerLayout.endUpdate(true);
10590 var layout = dialog.getLayout();
10591 layout.beginUpdate();
10592 layout.add("center", new Roo.ContentPanel("standard-panel",
10593 {title: "Download the Source", fitToFrame:true}));
10594 layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10595 {title: "Build your own roo.js"}));
10596 layout.getRegion("center").showPanel(sp);
10597 layout.endUpdate();
10601 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10602 * @param {Object} config configuration options
10604 Roo.LayoutDialog = function(el, cfg){
10607 if (typeof(cfg) == 'undefined') {
10608 config = Roo.apply({}, el);
10609 // not sure why we use documentElement here.. - it should always be body.
10610 // IE7 borks horribly if we use documentElement.
10611 // webkit also does not like documentElement - it creates a body element...
10612 el = Roo.get( document.body || document.documentElement ).createChild();
10613 //config.autoCreate = true;
10617 config.autoTabs = false;
10618 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10619 this.body.setStyle({overflow:"hidden", position:"relative"});
10620 this.layout = new Roo.BorderLayout(this.body.dom, config);
10621 this.layout.monitorWindowResize = false;
10622 this.el.addClass("x-dlg-auto-layout");
10623 // fix case when center region overwrites center function
10624 this.center = Roo.BasicDialog.prototype.center;
10625 this.on("show", this.layout.layout, this.layout, true);
10626 if (config.items) {
10627 var xitems = config.items;
10628 delete config.items;
10629 Roo.each(xitems, this.addxtype, this);
10634 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10636 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10639 endUpdate : function(){
10640 this.layout.endUpdate();
10644 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10647 beginUpdate : function(){
10648 this.layout.beginUpdate();
10652 * Get the BorderLayout for this dialog
10653 * @return {Roo.BorderLayout}
10655 getLayout : function(){
10656 return this.layout;
10659 showEl : function(){
10660 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10662 this.layout.layout();
10667 // Use the syncHeightBeforeShow config option to control this automatically
10668 syncBodyHeight : function(){
10669 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10670 if(this.layout){this.layout.layout();}
10674 * Add an xtype element (actually adds to the layout.)
10675 * @return {Object} xdata xtype object data.
10678 addxtype : function(c) {
10679 return this.layout.addxtype(c);
10683 * Ext JS Library 1.1.1
10684 * Copyright(c) 2006-2007, Ext JS, LLC.
10686 * Originally Released Under LGPL - original licence link has changed is not relivant.
10689 * <script type="text/javascript">
10693 * @class Roo.MessageBox
10694 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
10698 Roo.Msg.alert('Status', 'Changes saved successfully.');
10700 // Prompt for user data:
10701 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10703 // process text value...
10707 // Show a dialog using config options:
10709 title:'Save Changes?',
10710 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10711 buttons: Roo.Msg.YESNOCANCEL,
10718 Roo.MessageBox = function(){
10719 var dlg, opt, mask, waitTimer;
10720 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10721 var buttons, activeTextEl, bwidth;
10724 var handleButton = function(button){
10726 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10730 var handleHide = function(){
10731 if(opt && opt.cls){
10732 dlg.el.removeClass(opt.cls);
10735 Roo.TaskMgr.stop(waitTimer);
10741 var updateButtons = function(b){
10744 buttons["ok"].hide();
10745 buttons["cancel"].hide();
10746 buttons["yes"].hide();
10747 buttons["no"].hide();
10748 dlg.footer.dom.style.display = 'none';
10751 dlg.footer.dom.style.display = '';
10752 for(var k in buttons){
10753 if(typeof buttons[k] != "function"){
10756 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10757 width += buttons[k].el.getWidth()+15;
10767 var handleEsc = function(d, k, e){
10768 if(opt && opt.closable !== false){
10778 * Returns a reference to the underlying {@link Roo.BasicDialog} element
10779 * @return {Roo.BasicDialog} The BasicDialog element
10781 getDialog : function(){
10783 dlg = new Roo.BasicDialog("x-msg-box", {
10788 constraintoviewport:false,
10790 collapsible : false,
10793 width:400, height:100,
10794 buttonAlign:"center",
10795 closeClick : function(){
10796 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10797 handleButton("no");
10799 handleButton("cancel");
10803 dlg.on("hide", handleHide);
10805 dlg.addKeyListener(27, handleEsc);
10807 var bt = this.buttonText;
10808 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10809 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10810 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10811 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10812 bodyEl = dlg.body.createChild({
10814 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>'
10816 msgEl = bodyEl.dom.firstChild;
10817 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10818 textboxEl.enableDisplayMode();
10819 textboxEl.addKeyListener([10,13], function(){
10820 if(dlg.isVisible() && opt && opt.buttons){
10821 if(opt.buttons.ok){
10822 handleButton("ok");
10823 }else if(opt.buttons.yes){
10824 handleButton("yes");
10828 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10829 textareaEl.enableDisplayMode();
10830 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10831 progressEl.enableDisplayMode();
10832 var pf = progressEl.dom.firstChild;
10834 pp = Roo.get(pf.firstChild);
10835 pp.setHeight(pf.offsetHeight);
10843 * Updates the message box body text
10844 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10845 * the XHTML-compliant non-breaking space character '&#160;')
10846 * @return {Roo.MessageBox} This message box
10848 updateText : function(text){
10849 if(!dlg.isVisible() && !opt.width){
10850 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10852 msgEl.innerHTML = text || ' ';
10854 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10855 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10857 Math.min(opt.width || cw , this.maxWidth),
10858 Math.max(opt.minWidth || this.minWidth, bwidth)
10861 activeTextEl.setWidth(w);
10863 if(dlg.isVisible()){
10864 dlg.fixedcenter = false;
10866 // to big, make it scroll. = But as usual stupid IE does not support
10869 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10870 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10871 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10873 bodyEl.dom.style.height = '';
10874 bodyEl.dom.style.overflowY = '';
10877 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10879 bodyEl.dom.style.overflowX = '';
10882 dlg.setContentSize(w, bodyEl.getHeight());
10883 if(dlg.isVisible()){
10884 dlg.fixedcenter = true;
10890 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
10891 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10892 * @param {Number} value Any number between 0 and 1 (e.g., .5)
10893 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10894 * @return {Roo.MessageBox} This message box
10896 updateProgress : function(value, text){
10898 this.updateText(text);
10900 if (pp) { // weird bug on my firefox - for some reason this is not defined
10901 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10907 * Returns true if the message box is currently displayed
10908 * @return {Boolean} True if the message box is visible, else false
10910 isVisible : function(){
10911 return dlg && dlg.isVisible();
10915 * Hides the message box if it is displayed
10918 if(this.isVisible()){
10924 * Displays a new message box, or reinitializes an existing message box, based on the config options
10925 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10926 * The following config object properties are supported:
10928 Property Type Description
10929 ---------- --------------- ------------------------------------------------------------------------------------
10930 animEl String/Element An id or Element from which the message box should animate as it opens and
10931 closes (defaults to undefined)
10932 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10933 cancel:'Bar'}), or false to not show any buttons (defaults to false)
10934 closable Boolean False to hide the top-right close button (defaults to true). Note that
10935 progress and wait dialogs will ignore this property and always hide the
10936 close button as they can only be closed programmatically.
10937 cls String A custom CSS class to apply to the message box element
10938 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
10939 displayed (defaults to 75)
10940 fn Function A callback function to execute after closing the dialog. The arguments to the
10941 function will be btn (the name of the button that was clicked, if applicable,
10942 e.g. "ok"), and text (the value of the active text field, if applicable).
10943 Progress and wait dialogs will ignore this option since they do not respond to
10944 user actions and can only be closed programmatically, so any required function
10945 should be called by the same code after it closes the dialog.
10946 icon String A CSS class that provides a background image to be used as an icon for
10947 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10948 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
10949 minWidth Number The minimum width in pixels of the message box (defaults to 100)
10950 modal Boolean False to allow user interaction with the page while the message box is
10951 displayed (defaults to true)
10952 msg String A string that will replace the existing message box body text (defaults
10953 to the XHTML-compliant non-breaking space character ' ')
10954 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
10955 progress Boolean True to display a progress bar (defaults to false)
10956 progressText String The text to display inside the progress bar if progress = true (defaults to '')
10957 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
10958 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
10959 title String The title text
10960 value String The string value to set into the active textbox element if displayed
10961 wait Boolean True to display a progress bar (defaults to false)
10962 width Number The width of the dialog in pixels
10969 msg: 'Please enter your address:',
10971 buttons: Roo.MessageBox.OKCANCEL,
10974 animEl: 'addAddressBtn'
10977 * @param {Object} config Configuration options
10978 * @return {Roo.MessageBox} This message box
10980 show : function(options)
10983 // this causes nightmares if you show one dialog after another
10984 // especially on callbacks..
10986 if(this.isVisible()){
10989 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10990 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
10991 Roo.log("New Dialog Message:" + options.msg )
10992 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10993 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10996 var d = this.getDialog();
10998 d.setTitle(opt.title || " ");
10999 d.close.setDisplayed(opt.closable !== false);
11000 activeTextEl = textboxEl;
11001 opt.prompt = opt.prompt || (opt.multiline ? true : false);
11006 textareaEl.setHeight(typeof opt.multiline == "number" ?
11007 opt.multiline : this.defaultTextHeight);
11008 activeTextEl = textareaEl;
11017 progressEl.setDisplayed(opt.progress === true);
11018 this.updateProgress(0);
11019 activeTextEl.dom.value = opt.value || "";
11021 dlg.setDefaultButton(activeTextEl);
11023 var bs = opt.buttons;
11026 db = buttons["ok"];
11027 }else if(bs && bs.yes){
11028 db = buttons["yes"];
11030 dlg.setDefaultButton(db);
11032 bwidth = updateButtons(opt.buttons);
11033 this.updateText(opt.msg);
11035 d.el.addClass(opt.cls);
11037 d.proxyDrag = opt.proxyDrag === true;
11038 d.modal = opt.modal !== false;
11039 d.mask = opt.modal !== false ? mask : false;
11040 if(!d.isVisible()){
11041 // force it to the end of the z-index stack so it gets a cursor in FF
11042 document.body.appendChild(dlg.el.dom);
11043 d.animateTarget = null;
11044 d.show(options.animEl);
11050 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
11051 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
11052 * and closing the message box when the process is complete.
11053 * @param {String} title The title bar text
11054 * @param {String} msg The message box body text
11055 * @return {Roo.MessageBox} This message box
11057 progress : function(title, msg){
11064 minWidth: this.minProgressWidth,
11071 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11072 * If a callback function is passed it will be called after the user clicks the button, and the
11073 * id of the button that was clicked will be passed as the only parameter to the callback
11074 * (could also be the top-right close button).
11075 * @param {String} title The title bar text
11076 * @param {String} msg The message box body text
11077 * @param {Function} fn (optional) The callback function invoked after the message box is closed
11078 * @param {Object} scope (optional) The scope of the callback function
11079 * @return {Roo.MessageBox} This message box
11081 alert : function(title, msg, fn, scope){
11094 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
11095 * interaction while waiting for a long-running process to complete that does not have defined intervals.
11096 * You are responsible for closing the message box when the process is complete.
11097 * @param {String} msg The message box body text
11098 * @param {String} title (optional) The title bar text
11099 * @return {Roo.MessageBox} This message box
11101 wait : function(msg, title){
11112 waitTimer = Roo.TaskMgr.start({
11114 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11122 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11123 * If a callback function is passed it will be called after the user clicks either button, and the id of the
11124 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11125 * @param {String} title The title bar text
11126 * @param {String} msg The message box body text
11127 * @param {Function} fn (optional) The callback function invoked after the message box is closed
11128 * @param {Object} scope (optional) The scope of the callback function
11129 * @return {Roo.MessageBox} This message box
11131 confirm : function(title, msg, fn, scope){
11135 buttons: this.YESNO,
11144 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11145 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
11146 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11147 * (could also be the top-right close button) and the text that was entered will be passed as the two
11148 * parameters to the callback.
11149 * @param {String} title The title bar text
11150 * @param {String} msg The message box body text
11151 * @param {Function} fn (optional) The callback function invoked after the message box is closed
11152 * @param {Object} scope (optional) The scope of the callback function
11153 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11154 * property, or the height in pixels to create the textbox (defaults to false / single-line)
11155 * @return {Roo.MessageBox} This message box
11157 prompt : function(title, msg, fn, scope, multiline){
11161 buttons: this.OKCANCEL,
11166 multiline: multiline,
11173 * Button config that displays a single OK button
11178 * Button config that displays Yes and No buttons
11181 YESNO : {yes:true, no:true},
11183 * Button config that displays OK and Cancel buttons
11186 OKCANCEL : {ok:true, cancel:true},
11188 * Button config that displays Yes, No and Cancel buttons
11191 YESNOCANCEL : {yes:true, no:true, cancel:true},
11194 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11197 defaultTextHeight : 75,
11199 * The maximum width in pixels of the message box (defaults to 600)
11204 * The minimum width in pixels of the message box (defaults to 100)
11209 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
11210 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11213 minProgressWidth : 250,
11215 * An object containing the default button text strings that can be overriden for localized language support.
11216 * Supported properties are: ok, cancel, yes and no.
11217 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11230 * Shorthand for {@link Roo.MessageBox}
11232 Roo.Msg = Roo.MessageBox;/*
11234 * Ext JS Library 1.1.1
11235 * Copyright(c) 2006-2007, Ext JS, LLC.
11237 * Originally Released Under LGPL - original licence link has changed is not relivant.
11240 * <script type="text/javascript">
11243 * @class Roo.QuickTips
11244 * Provides attractive and customizable tooltips for any element.
11247 Roo.QuickTips = function(){
11248 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11249 var ce, bd, xy, dd;
11250 var visible = false, disabled = true, inited = false;
11251 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11253 var onOver = function(e){
11257 var t = e.getTarget();
11258 if(!t || t.nodeType !== 1 || t == document || t == document.body){
11261 if(ce && t == ce.el){
11262 clearTimeout(hideProc);
11265 if(t && tagEls[t.id]){
11266 tagEls[t.id].el = t;
11267 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11270 var ttp, et = Roo.fly(t);
11271 var ns = cfg.namespace;
11272 if(tm.interceptTitles && t.title){
11275 t.removeAttribute("title");
11276 e.preventDefault();
11278 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11281 showProc = show.defer(tm.showDelay, tm, [{
11283 text: ttp.replace(/\\n/g,'<br/>'),
11284 width: et.getAttributeNS(ns, cfg.width),
11285 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11286 title: et.getAttributeNS(ns, cfg.title),
11287 cls: et.getAttributeNS(ns, cfg.cls)
11292 var onOut = function(e){
11293 clearTimeout(showProc);
11294 var t = e.getTarget();
11295 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11296 hideProc = setTimeout(hide, tm.hideDelay);
11300 var onMove = function(e){
11306 if(tm.trackMouse && ce){
11311 var onDown = function(e){
11312 clearTimeout(showProc);
11313 clearTimeout(hideProc);
11315 if(tm.hideOnClick){
11318 tm.enable.defer(100, tm);
11323 var getPad = function(){
11324 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11327 var show = function(o){
11331 clearTimeout(dismissProc);
11333 if(removeCls){ // in case manually hidden
11334 el.removeClass(removeCls);
11338 el.addClass(ce.cls);
11339 removeCls = ce.cls;
11342 tipTitle.update(ce.title);
11345 tipTitle.update('');
11348 el.dom.style.width = tm.maxWidth+'px';
11349 //tipBody.dom.style.width = '';
11350 tipBodyText.update(o.text);
11351 var p = getPad(), w = ce.width;
11353 var td = tipBodyText.dom;
11354 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11355 if(aw > tm.maxWidth){
11357 }else if(aw < tm.minWidth){
11363 //tipBody.setWidth(w);
11364 el.setWidth(parseInt(w, 10) + p);
11365 if(ce.autoHide === false){
11366 close.setDisplayed(true);
11371 close.setDisplayed(false);
11377 el.avoidY = xy[1]-18;
11382 el.setStyle("visibility", "visible");
11383 el.fadeIn({callback: afterShow});
11389 var afterShow = function(){
11393 if(tm.autoDismiss && ce.autoHide !== false){
11394 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11399 var hide = function(noanim){
11400 clearTimeout(dismissProc);
11401 clearTimeout(hideProc);
11403 if(el.isVisible()){
11405 if(noanim !== true && tm.animate){
11406 el.fadeOut({callback: afterHide});
11413 var afterHide = function(){
11416 el.removeClass(removeCls);
11423 * @cfg {Number} minWidth
11424 * The minimum width of the quick tip (defaults to 40)
11428 * @cfg {Number} maxWidth
11429 * The maximum width of the quick tip (defaults to 300)
11433 * @cfg {Boolean} interceptTitles
11434 * True to automatically use the element's DOM title value if available (defaults to false)
11436 interceptTitles : false,
11438 * @cfg {Boolean} trackMouse
11439 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11441 trackMouse : false,
11443 * @cfg {Boolean} hideOnClick
11444 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11446 hideOnClick : true,
11448 * @cfg {Number} showDelay
11449 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11453 * @cfg {Number} hideDelay
11454 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11458 * @cfg {Boolean} autoHide
11459 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11460 * Used in conjunction with hideDelay.
11465 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11466 * (defaults to true). Used in conjunction with autoDismissDelay.
11468 autoDismiss : true,
11471 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11473 autoDismissDelay : 5000,
11475 * @cfg {Boolean} animate
11476 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11481 * @cfg {String} title
11482 * Title text to display (defaults to ''). This can be any valid HTML markup.
11486 * @cfg {String} text
11487 * Body text to display (defaults to ''). This can be any valid HTML markup.
11491 * @cfg {String} cls
11492 * A CSS class to apply to the base quick tip element (defaults to '').
11496 * @cfg {Number} width
11497 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
11498 * minWidth or maxWidth.
11503 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
11504 * or display QuickTips in a page.
11507 tm = Roo.QuickTips;
11508 cfg = tm.tagConfig;
11510 if(!Roo.isReady){ // allow calling of init() before onReady
11511 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11514 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11515 el.fxDefaults = {stopFx: true};
11516 // maximum custom styling
11517 //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>');
11518 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>');
11519 tipTitle = el.child('h3');
11520 tipTitle.enableDisplayMode("block");
11521 tipBody = el.child('div.x-tip-bd');
11522 tipBodyText = el.child('div.x-tip-bd-inner');
11523 //bdLeft = el.child('div.x-tip-bd-left');
11524 //bdRight = el.child('div.x-tip-bd-right');
11525 close = el.child('div.x-tip-close');
11526 close.enableDisplayMode("block");
11527 close.on("click", hide);
11528 var d = Roo.get(document);
11529 d.on("mousedown", onDown);
11530 d.on("mouseover", onOver);
11531 d.on("mouseout", onOut);
11532 d.on("mousemove", onMove);
11533 esc = d.addKeyListener(27, hide);
11536 dd = el.initDD("default", null, {
11537 onDrag : function(){
11541 dd.setHandleElId(tipTitle.id);
11550 * Configures a new quick tip instance and assigns it to a target element. The following config options
11553 Property Type Description
11554 ---------- --------------------- ------------------------------------------------------------------------
11555 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
11557 * @param {Object} config The config object
11559 register : function(config){
11560 var cs = config instanceof Array ? config : arguments;
11561 for(var i = 0, len = cs.length; i < len; i++) {
11563 var target = c.target;
11565 if(target instanceof Array){
11566 for(var j = 0, jlen = target.length; j < jlen; j++){
11567 tagEls[target[j]] = c;
11570 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11577 * Removes this quick tip from its element and destroys it.
11578 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11580 unregister : function(el){
11581 delete tagEls[Roo.id(el)];
11585 * Enable this quick tip.
11587 enable : function(){
11588 if(inited && disabled){
11590 if(locks.length < 1){
11597 * Disable this quick tip.
11599 disable : function(){
11601 clearTimeout(showProc);
11602 clearTimeout(hideProc);
11603 clearTimeout(dismissProc);
11611 * Returns true if the quick tip is enabled, else false.
11613 isEnabled : function(){
11619 namespace : "roo", // was ext?? this may break..
11620 alt_namespace : "ext",
11621 attribute : "qtip",
11631 // backwards compat
11632 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11634 * Ext JS Library 1.1.1
11635 * Copyright(c) 2006-2007, Ext JS, LLC.
11637 * Originally Released Under LGPL - original licence link has changed is not relivant.
11640 * <script type="text/javascript">
11645 * @class Roo.tree.TreePanel
11646 * @extends Roo.data.Tree
11648 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11649 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11650 * @cfg {Boolean} enableDD true to enable drag and drop
11651 * @cfg {Boolean} enableDrag true to enable just drag
11652 * @cfg {Boolean} enableDrop true to enable just drop
11653 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11654 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11655 * @cfg {String} ddGroup The DD group this TreePanel belongs to
11656 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11657 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11658 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11659 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11660 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11661 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11662 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11663 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11664 * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11665 * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11666 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11667 * @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>
11668 * @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>
11671 * @param {String/HTMLElement/Element} el The container element
11672 * @param {Object} config
11674 Roo.tree.TreePanel = function(el, config){
11676 var loader = false;
11678 root = config.root;
11679 delete config.root;
11681 if (config.loader) {
11682 loader = config.loader;
11683 delete config.loader;
11686 Roo.apply(this, config);
11687 Roo.tree.TreePanel.superclass.constructor.call(this);
11688 this.el = Roo.get(el);
11689 this.el.addClass('x-tree');
11690 //console.log(root);
11692 this.setRootNode( Roo.factory(root, Roo.tree));
11695 this.loader = Roo.factory(loader, Roo.tree);
11698 * Read-only. The id of the container element becomes this TreePanel's id.
11700 this.id = this.el.id;
11703 * @event beforeload
11704 * Fires before a node is loaded, return false to cancel
11705 * @param {Node} node The node being loaded
11707 "beforeload" : true,
11710 * Fires when a node is loaded
11711 * @param {Node} node The node that was loaded
11715 * @event textchange
11716 * Fires when the text for a node is changed
11717 * @param {Node} node The node
11718 * @param {String} text The new text
11719 * @param {String} oldText The old text
11721 "textchange" : true,
11723 * @event beforeexpand
11724 * Fires before a node is expanded, return false to cancel.
11725 * @param {Node} node The node
11726 * @param {Boolean} deep
11727 * @param {Boolean} anim
11729 "beforeexpand" : true,
11731 * @event beforecollapse
11732 * Fires before a node is collapsed, return false to cancel.
11733 * @param {Node} node The node
11734 * @param {Boolean} deep
11735 * @param {Boolean} anim
11737 "beforecollapse" : true,
11740 * Fires when a node is expanded
11741 * @param {Node} node The node
11745 * @event disabledchange
11746 * Fires when the disabled status of a node changes
11747 * @param {Node} node The node
11748 * @param {Boolean} disabled
11750 "disabledchange" : true,
11753 * Fires when a node is collapsed
11754 * @param {Node} node The node
11758 * @event beforeclick
11759 * Fires before click processing on a node. Return false to cancel the default action.
11760 * @param {Node} node The node
11761 * @param {Roo.EventObject} e The event object
11763 "beforeclick":true,
11765 * @event checkchange
11766 * Fires when a node with a checkbox's checked property changes
11767 * @param {Node} this This node
11768 * @param {Boolean} checked
11770 "checkchange":true,
11773 * Fires when a node is clicked
11774 * @param {Node} node The node
11775 * @param {Roo.EventObject} e The event object
11780 * Fires when a node is double clicked
11781 * @param {Node} node The node
11782 * @param {Roo.EventObject} e The event object
11786 * @event contextmenu
11787 * Fires when a node is right clicked
11788 * @param {Node} node The node
11789 * @param {Roo.EventObject} e The event object
11791 "contextmenu":true,
11793 * @event beforechildrenrendered
11794 * Fires right before the child nodes for a node are rendered
11795 * @param {Node} node The node
11797 "beforechildrenrendered":true,
11800 * Fires when a node starts being dragged
11801 * @param {Roo.tree.TreePanel} this
11802 * @param {Roo.tree.TreeNode} node
11803 * @param {event} e The raw browser event
11805 "startdrag" : true,
11808 * Fires when a drag operation is complete
11809 * @param {Roo.tree.TreePanel} this
11810 * @param {Roo.tree.TreeNode} node
11811 * @param {event} e The raw browser event
11816 * Fires when a dragged node is dropped on a valid DD target
11817 * @param {Roo.tree.TreePanel} this
11818 * @param {Roo.tree.TreeNode} node
11819 * @param {DD} dd The dd it was dropped on
11820 * @param {event} e The raw browser event
11824 * @event beforenodedrop
11825 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11826 * passed to handlers has the following properties:<br />
11827 * <ul style="padding:5px;padding-left:16px;">
11828 * <li>tree - The TreePanel</li>
11829 * <li>target - The node being targeted for the drop</li>
11830 * <li>data - The drag data from the drag source</li>
11831 * <li>point - The point of the drop - append, above or below</li>
11832 * <li>source - The drag source</li>
11833 * <li>rawEvent - Raw mouse event</li>
11834 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11835 * to be inserted by setting them on this object.</li>
11836 * <li>cancel - Set this to true to cancel the drop.</li>
11838 * @param {Object} dropEvent
11840 "beforenodedrop" : true,
11843 * Fires after a DD object is dropped on a node in this tree. The dropEvent
11844 * passed to handlers has the following properties:<br />
11845 * <ul style="padding:5px;padding-left:16px;">
11846 * <li>tree - The TreePanel</li>
11847 * <li>target - The node being targeted for the drop</li>
11848 * <li>data - The drag data from the drag source</li>
11849 * <li>point - The point of the drop - append, above or below</li>
11850 * <li>source - The drag source</li>
11851 * <li>rawEvent - Raw mouse event</li>
11852 * <li>dropNode - Dropped node(s).</li>
11854 * @param {Object} dropEvent
11858 * @event nodedragover
11859 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11860 * passed to handlers has the following properties:<br />
11861 * <ul style="padding:5px;padding-left:16px;">
11862 * <li>tree - The TreePanel</li>
11863 * <li>target - The node being targeted for the drop</li>
11864 * <li>data - The drag data from the drag source</li>
11865 * <li>point - The point of the drop - append, above or below</li>
11866 * <li>source - The drag source</li>
11867 * <li>rawEvent - Raw mouse event</li>
11868 * <li>dropNode - Drop node(s) provided by the source.</li>
11869 * <li>cancel - Set this to true to signal drop not allowed.</li>
11871 * @param {Object} dragOverEvent
11873 "nodedragover" : true,
11875 * @event appendnode
11876 * Fires when append node to the tree
11877 * @param {Roo.tree.TreePanel} this
11878 * @param {Roo.tree.TreeNode} node
11879 * @param {Number} index The index of the newly appended node
11881 "appendnode" : true
11884 if(this.singleExpand){
11885 this.on("beforeexpand", this.restrictExpand, this);
11888 this.editor.tree = this;
11889 this.editor = Roo.factory(this.editor, Roo.tree);
11892 if (this.selModel) {
11893 this.selModel = Roo.factory(this.selModel, Roo.tree);
11897 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11898 rootVisible : true,
11899 animate: Roo.enableFx,
11902 hlDrop : Roo.enableFx,
11906 rendererTip: false,
11908 restrictExpand : function(node){
11909 var p = node.parentNode;
11911 if(p.expandedChild && p.expandedChild.parentNode == p){
11912 p.expandedChild.collapse();
11914 p.expandedChild = node;
11918 // private override
11919 setRootNode : function(node){
11920 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11921 if(!this.rootVisible){
11922 node.ui = new Roo.tree.RootTreeNodeUI(node);
11928 * Returns the container element for this TreePanel
11930 getEl : function(){
11935 * Returns the default TreeLoader for this TreePanel
11937 getLoader : function(){
11938 return this.loader;
11944 expandAll : function(){
11945 this.root.expand(true);
11949 * Collapse all nodes
11951 collapseAll : function(){
11952 this.root.collapse(true);
11956 * Returns the selection model used by this TreePanel
11958 getSelectionModel : function(){
11959 if(!this.selModel){
11960 this.selModel = new Roo.tree.DefaultSelectionModel();
11962 return this.selModel;
11966 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11967 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11968 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11971 getChecked : function(a, startNode){
11972 startNode = startNode || this.root;
11974 var f = function(){
11975 if(this.attributes.checked){
11976 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11979 startNode.cascade(f);
11984 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11985 * @param {String} path
11986 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11987 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11988 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11990 expandPath : function(path, attr, callback){
11991 attr = attr || "id";
11992 var keys = path.split(this.pathSeparator);
11993 var curNode = this.root;
11994 if(curNode.attributes[attr] != keys[1]){ // invalid root
11996 callback(false, null);
12001 var f = function(){
12002 if(++index == keys.length){
12004 callback(true, curNode);
12008 var c = curNode.findChild(attr, keys[index]);
12011 callback(false, curNode);
12016 c.expand(false, false, f);
12018 curNode.expand(false, false, f);
12022 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
12023 * @param {String} path
12024 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
12025 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
12026 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
12028 selectPath : function(path, attr, callback){
12029 attr = attr || "id";
12030 var keys = path.split(this.pathSeparator);
12031 var v = keys.pop();
12032 if(keys.length > 0){
12033 var f = function(success, node){
12034 if(success && node){
12035 var n = node.findChild(attr, v);
12041 }else if(callback){
12042 callback(false, n);
12046 callback(false, n);
12050 this.expandPath(keys.join(this.pathSeparator), attr, f);
12052 this.root.select();
12054 callback(true, this.root);
12059 getTreeEl : function(){
12064 * Trigger rendering of this TreePanel
12066 render : function(){
12067 if (this.innerCt) {
12068 return this; // stop it rendering more than once!!
12071 this.innerCt = this.el.createChild({tag:"ul",
12072 cls:"x-tree-root-ct " +
12073 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12075 if(this.containerScroll){
12076 Roo.dd.ScrollManager.register(this.el);
12078 if((this.enableDD || this.enableDrop) && !this.dropZone){
12080 * The dropZone used by this tree if drop is enabled
12081 * @type Roo.tree.TreeDropZone
12083 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12084 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12087 if((this.enableDD || this.enableDrag) && !this.dragZone){
12089 * The dragZone used by this tree if drag is enabled
12090 * @type Roo.tree.TreeDragZone
12092 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12093 ddGroup: this.ddGroup || "TreeDD",
12094 scroll: this.ddScroll
12097 this.getSelectionModel().init(this);
12099 Roo.log("ROOT not set in tree");
12102 this.root.render();
12103 if(!this.rootVisible){
12104 this.root.renderChildren();
12110 * Ext JS Library 1.1.1
12111 * Copyright(c) 2006-2007, Ext JS, LLC.
12113 * Originally Released Under LGPL - original licence link has changed is not relivant.
12116 * <script type="text/javascript">
12121 * @class Roo.tree.DefaultSelectionModel
12122 * @extends Roo.util.Observable
12123 * The default single selection for a TreePanel.
12124 * @param {Object} cfg Configuration
12126 Roo.tree.DefaultSelectionModel = function(cfg){
12127 this.selNode = null;
12133 * @event selectionchange
12134 * Fires when the selected node changes
12135 * @param {DefaultSelectionModel} this
12136 * @param {TreeNode} node the new selection
12138 "selectionchange" : true,
12141 * @event beforeselect
12142 * Fires before the selected node changes, return false to cancel the change
12143 * @param {DefaultSelectionModel} this
12144 * @param {TreeNode} node the new selection
12145 * @param {TreeNode} node the old selection
12147 "beforeselect" : true
12150 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12153 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12154 init : function(tree){
12156 tree.getTreeEl().on("keydown", this.onKeyDown, this);
12157 tree.on("click", this.onNodeClick, this);
12160 onNodeClick : function(node, e){
12161 if (e.ctrlKey && this.selNode == node) {
12162 this.unselect(node);
12170 * @param {TreeNode} node The node to select
12171 * @return {TreeNode} The selected node
12173 select : function(node){
12174 var last = this.selNode;
12175 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12177 last.ui.onSelectedChange(false);
12179 this.selNode = node;
12180 node.ui.onSelectedChange(true);
12181 this.fireEvent("selectionchange", this, node, last);
12188 * @param {TreeNode} node The node to unselect
12190 unselect : function(node){
12191 if(this.selNode == node){
12192 this.clearSelections();
12197 * Clear all selections
12199 clearSelections : function(){
12200 var n = this.selNode;
12202 n.ui.onSelectedChange(false);
12203 this.selNode = null;
12204 this.fireEvent("selectionchange", this, null);
12210 * Get the selected node
12211 * @return {TreeNode} The selected node
12213 getSelectedNode : function(){
12214 return this.selNode;
12218 * Returns true if the node is selected
12219 * @param {TreeNode} node The node to check
12220 * @return {Boolean}
12222 isSelected : function(node){
12223 return this.selNode == node;
12227 * Selects the node above the selected node in the tree, intelligently walking the nodes
12228 * @return TreeNode The new selection
12230 selectPrevious : function(){
12231 var s = this.selNode || this.lastSelNode;
12235 var ps = s.previousSibling;
12237 if(!ps.isExpanded() || ps.childNodes.length < 1){
12238 return this.select(ps);
12240 var lc = ps.lastChild;
12241 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12244 return this.select(lc);
12246 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12247 return this.select(s.parentNode);
12253 * Selects the node above the selected node in the tree, intelligently walking the nodes
12254 * @return TreeNode The new selection
12256 selectNext : function(){
12257 var s = this.selNode || this.lastSelNode;
12261 if(s.firstChild && s.isExpanded()){
12262 return this.select(s.firstChild);
12263 }else if(s.nextSibling){
12264 return this.select(s.nextSibling);
12265 }else if(s.parentNode){
12267 s.parentNode.bubble(function(){
12268 if(this.nextSibling){
12269 newS = this.getOwnerTree().selModel.select(this.nextSibling);
12278 onKeyDown : function(e){
12279 var s = this.selNode || this.lastSelNode;
12280 // undesirable, but required
12285 var k = e.getKey();
12293 this.selectPrevious();
12296 e.preventDefault();
12297 if(s.hasChildNodes()){
12298 if(!s.isExpanded()){
12300 }else if(s.firstChild){
12301 this.select(s.firstChild, e);
12306 e.preventDefault();
12307 if(s.hasChildNodes() && s.isExpanded()){
12309 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12310 this.select(s.parentNode, e);
12318 * @class Roo.tree.MultiSelectionModel
12319 * @extends Roo.util.Observable
12320 * Multi selection for a TreePanel.
12321 * @param {Object} cfg Configuration
12323 Roo.tree.MultiSelectionModel = function(){
12324 this.selNodes = [];
12328 * @event selectionchange
12329 * Fires when the selected nodes change
12330 * @param {MultiSelectionModel} this
12331 * @param {Array} nodes Array of the selected nodes
12333 "selectionchange" : true
12335 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12339 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12340 init : function(tree){
12342 tree.getTreeEl().on("keydown", this.onKeyDown, this);
12343 tree.on("click", this.onNodeClick, this);
12346 onNodeClick : function(node, e){
12347 this.select(node, e, e.ctrlKey);
12352 * @param {TreeNode} node The node to select
12353 * @param {EventObject} e (optional) An event associated with the selection
12354 * @param {Boolean} keepExisting True to retain existing selections
12355 * @return {TreeNode} The selected node
12357 select : function(node, e, keepExisting){
12358 if(keepExisting !== true){
12359 this.clearSelections(true);
12361 if(this.isSelected(node)){
12362 this.lastSelNode = node;
12365 this.selNodes.push(node);
12366 this.selMap[node.id] = node;
12367 this.lastSelNode = node;
12368 node.ui.onSelectedChange(true);
12369 this.fireEvent("selectionchange", this, this.selNodes);
12375 * @param {TreeNode} node The node to unselect
12377 unselect : function(node){
12378 if(this.selMap[node.id]){
12379 node.ui.onSelectedChange(false);
12380 var sn = this.selNodes;
12383 index = sn.indexOf(node);
12385 for(var i = 0, len = sn.length; i < len; i++){
12393 this.selNodes.splice(index, 1);
12395 delete this.selMap[node.id];
12396 this.fireEvent("selectionchange", this, this.selNodes);
12401 * Clear all selections
12403 clearSelections : function(suppressEvent){
12404 var sn = this.selNodes;
12406 for(var i = 0, len = sn.length; i < len; i++){
12407 sn[i].ui.onSelectedChange(false);
12409 this.selNodes = [];
12411 if(suppressEvent !== true){
12412 this.fireEvent("selectionchange", this, this.selNodes);
12418 * Returns true if the node is selected
12419 * @param {TreeNode} node The node to check
12420 * @return {Boolean}
12422 isSelected : function(node){
12423 return this.selMap[node.id] ? true : false;
12427 * Returns an array of the selected nodes
12430 getSelectedNodes : function(){
12431 return this.selNodes;
12434 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12436 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12438 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12441 * Ext JS Library 1.1.1
12442 * Copyright(c) 2006-2007, Ext JS, LLC.
12444 * Originally Released Under LGPL - original licence link has changed is not relivant.
12447 * <script type="text/javascript">
12451 * @class Roo.tree.TreeNode
12452 * @extends Roo.data.Node
12453 * @cfg {String} text The text for this node
12454 * @cfg {Boolean} expanded true to start the node expanded
12455 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12456 * @cfg {Boolean} allowDrop false if this node cannot be drop on
12457 * @cfg {Boolean} disabled true to start the node disabled
12458 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12459 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
12460 * @cfg {String} cls A css class to be added to the node
12461 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12462 * @cfg {String} href URL of the link used for the node (defaults to #)
12463 * @cfg {String} hrefTarget target frame for the link
12464 * @cfg {String} qtip An Ext QuickTip for the node
12465 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12466 * @cfg {Boolean} singleClickExpand True for single click expand on this node
12467 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12468 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12469 * (defaults to undefined with no checkbox rendered)
12471 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12473 Roo.tree.TreeNode = function(attributes){
12474 attributes = attributes || {};
12475 if(typeof attributes == "string"){
12476 attributes = {text: attributes};
12478 this.childrenRendered = false;
12479 this.rendered = false;
12480 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12481 this.expanded = attributes.expanded === true;
12482 this.isTarget = attributes.isTarget !== false;
12483 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12484 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12487 * Read-only. The text for this node. To change it use setText().
12490 this.text = attributes.text;
12492 * True if this node is disabled.
12495 this.disabled = attributes.disabled === true;
12499 * @event textchange
12500 * Fires when the text for this node is changed
12501 * @param {Node} this This node
12502 * @param {String} text The new text
12503 * @param {String} oldText The old text
12505 "textchange" : true,
12507 * @event beforeexpand
12508 * Fires before this node is expanded, return false to cancel.
12509 * @param {Node} this This node
12510 * @param {Boolean} deep
12511 * @param {Boolean} anim
12513 "beforeexpand" : true,
12515 * @event beforecollapse
12516 * Fires before this node is collapsed, return false to cancel.
12517 * @param {Node} this This node
12518 * @param {Boolean} deep
12519 * @param {Boolean} anim
12521 "beforecollapse" : true,
12524 * Fires when this node is expanded
12525 * @param {Node} this This node
12529 * @event disabledchange
12530 * Fires when the disabled status of this node changes
12531 * @param {Node} this This node
12532 * @param {Boolean} disabled
12534 "disabledchange" : true,
12537 * Fires when this node is collapsed
12538 * @param {Node} this This node
12542 * @event beforeclick
12543 * Fires before click processing. Return false to cancel the default action.
12544 * @param {Node} this This node
12545 * @param {Roo.EventObject} e The event object
12547 "beforeclick":true,
12549 * @event checkchange
12550 * Fires when a node with a checkbox's checked property changes
12551 * @param {Node} this This node
12552 * @param {Boolean} checked
12554 "checkchange":true,
12557 * Fires when this node is clicked
12558 * @param {Node} this This node
12559 * @param {Roo.EventObject} e The event object
12564 * Fires when this node is double clicked
12565 * @param {Node} this This node
12566 * @param {Roo.EventObject} e The event object
12570 * @event contextmenu
12571 * Fires when this node is right clicked
12572 * @param {Node} this This node
12573 * @param {Roo.EventObject} e The event object
12575 "contextmenu":true,
12577 * @event beforechildrenrendered
12578 * Fires right before the child nodes for this node are rendered
12579 * @param {Node} this This node
12581 "beforechildrenrendered":true
12584 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12587 * Read-only. The UI for this node
12590 this.ui = new uiClass(this);
12592 // finally support items[]
12593 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12598 Roo.each(this.attributes.items, function(c) {
12599 this.appendChild(Roo.factory(c,Roo.Tree));
12601 delete this.attributes.items;
12606 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12607 preventHScroll: true,
12609 * Returns true if this node is expanded
12610 * @return {Boolean}
12612 isExpanded : function(){
12613 return this.expanded;
12617 * Returns the UI object for this node
12618 * @return {TreeNodeUI}
12620 getUI : function(){
12624 // private override
12625 setFirstChild : function(node){
12626 var of = this.firstChild;
12627 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12628 if(this.childrenRendered && of && node != of){
12629 of.renderIndent(true, true);
12632 this.renderIndent(true, true);
12636 // private override
12637 setLastChild : function(node){
12638 var ol = this.lastChild;
12639 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12640 if(this.childrenRendered && ol && node != ol){
12641 ol.renderIndent(true, true);
12644 this.renderIndent(true, true);
12648 // these methods are overridden to provide lazy rendering support
12649 // private override
12650 appendChild : function()
12652 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12653 if(node && this.childrenRendered){
12656 this.ui.updateExpandIcon();
12660 // private override
12661 removeChild : function(node){
12662 this.ownerTree.getSelectionModel().unselect(node);
12663 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12664 // if it's been rendered remove dom node
12665 if(this.childrenRendered){
12668 if(this.childNodes.length < 1){
12669 this.collapse(false, false);
12671 this.ui.updateExpandIcon();
12673 if(!this.firstChild) {
12674 this.childrenRendered = false;
12679 // private override
12680 insertBefore : function(node, refNode){
12681 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12682 if(newNode && refNode && this.childrenRendered){
12685 this.ui.updateExpandIcon();
12690 * Sets the text for this node
12691 * @param {String} text
12693 setText : function(text){
12694 var oldText = this.text;
12696 this.attributes.text = text;
12697 if(this.rendered){ // event without subscribing
12698 this.ui.onTextChange(this, text, oldText);
12700 this.fireEvent("textchange", this, text, oldText);
12704 * Triggers selection of this node
12706 select : function(){
12707 this.getOwnerTree().getSelectionModel().select(this);
12711 * Triggers deselection of this node
12713 unselect : function(){
12714 this.getOwnerTree().getSelectionModel().unselect(this);
12718 * Returns true if this node is selected
12719 * @return {Boolean}
12721 isSelected : function(){
12722 return this.getOwnerTree().getSelectionModel().isSelected(this);
12726 * Expand this node.
12727 * @param {Boolean} deep (optional) True to expand all children as well
12728 * @param {Boolean} anim (optional) false to cancel the default animation
12729 * @param {Function} callback (optional) A callback to be called when
12730 * expanding this node completes (does not wait for deep expand to complete).
12731 * Called with 1 parameter, this node.
12733 expand : function(deep, anim, callback){
12734 if(!this.expanded){
12735 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12738 if(!this.childrenRendered){
12739 this.renderChildren();
12741 this.expanded = true;
12743 if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12744 this.ui.animExpand(function(){
12745 this.fireEvent("expand", this);
12746 if(typeof callback == "function"){
12750 this.expandChildNodes(true);
12752 }.createDelegate(this));
12756 this.fireEvent("expand", this);
12757 if(typeof callback == "function"){
12762 if(typeof callback == "function"){
12767 this.expandChildNodes(true);
12771 isHiddenRoot : function(){
12772 return this.isRoot && !this.getOwnerTree().rootVisible;
12776 * Collapse this node.
12777 * @param {Boolean} deep (optional) True to collapse all children as well
12778 * @param {Boolean} anim (optional) false to cancel the default animation
12780 collapse : function(deep, anim){
12781 if(this.expanded && !this.isHiddenRoot()){
12782 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12785 this.expanded = false;
12786 if((this.getOwnerTree().animate && anim !== false) || anim){
12787 this.ui.animCollapse(function(){
12788 this.fireEvent("collapse", this);
12790 this.collapseChildNodes(true);
12792 }.createDelegate(this));
12795 this.ui.collapse();
12796 this.fireEvent("collapse", this);
12800 var cs = this.childNodes;
12801 for(var i = 0, len = cs.length; i < len; i++) {
12802 cs[i].collapse(true, false);
12808 delayedExpand : function(delay){
12809 if(!this.expandProcId){
12810 this.expandProcId = this.expand.defer(delay, this);
12815 cancelExpand : function(){
12816 if(this.expandProcId){
12817 clearTimeout(this.expandProcId);
12819 this.expandProcId = false;
12823 * Toggles expanded/collapsed state of the node
12825 toggle : function(){
12834 * Ensures all parent nodes are expanded
12836 ensureVisible : function(callback){
12837 var tree = this.getOwnerTree();
12838 tree.expandPath(this.parentNode.getPath(), false, function(){
12839 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12840 Roo.callback(callback);
12841 }.createDelegate(this));
12845 * Expand all child nodes
12846 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12848 expandChildNodes : function(deep){
12849 var cs = this.childNodes;
12850 for(var i = 0, len = cs.length; i < len; i++) {
12851 cs[i].expand(deep);
12856 * Collapse all child nodes
12857 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12859 collapseChildNodes : function(deep){
12860 var cs = this.childNodes;
12861 for(var i = 0, len = cs.length; i < len; i++) {
12862 cs[i].collapse(deep);
12867 * Disables this node
12869 disable : function(){
12870 this.disabled = true;
12872 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12873 this.ui.onDisableChange(this, true);
12875 this.fireEvent("disabledchange", this, true);
12879 * Enables this node
12881 enable : function(){
12882 this.disabled = false;
12883 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12884 this.ui.onDisableChange(this, false);
12886 this.fireEvent("disabledchange", this, false);
12890 renderChildren : function(suppressEvent){
12891 if(suppressEvent !== false){
12892 this.fireEvent("beforechildrenrendered", this);
12894 var cs = this.childNodes;
12895 for(var i = 0, len = cs.length; i < len; i++){
12896 cs[i].render(true);
12898 this.childrenRendered = true;
12902 sort : function(fn, scope){
12903 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12904 if(this.childrenRendered){
12905 var cs = this.childNodes;
12906 for(var i = 0, len = cs.length; i < len; i++){
12907 cs[i].render(true);
12913 render : function(bulkRender){
12914 this.ui.render(bulkRender);
12915 if(!this.rendered){
12916 this.rendered = true;
12918 this.expanded = false;
12919 this.expand(false, false);
12925 renderIndent : function(deep, refresh){
12927 this.ui.childIndent = null;
12929 this.ui.renderIndent();
12930 if(deep === true && this.childrenRendered){
12931 var cs = this.childNodes;
12932 for(var i = 0, len = cs.length; i < len; i++){
12933 cs[i].renderIndent(true, refresh);
12939 * Ext JS Library 1.1.1
12940 * Copyright(c) 2006-2007, Ext JS, LLC.
12942 * Originally Released Under LGPL - original licence link has changed is not relivant.
12945 * <script type="text/javascript">
12949 * @class Roo.tree.AsyncTreeNode
12950 * @extends Roo.tree.TreeNode
12951 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12953 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12955 Roo.tree.AsyncTreeNode = function(config){
12956 this.loaded = false;
12957 this.loading = false;
12958 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12960 * @event beforeload
12961 * Fires before this node is loaded, return false to cancel
12962 * @param {Node} this This node
12964 this.addEvents({'beforeload':true, 'load': true});
12967 * Fires when this node is loaded
12968 * @param {Node} this This node
12971 * The loader used by this node (defaults to using the tree's defined loader)
12976 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12977 expand : function(deep, anim, callback){
12978 if(this.loading){ // if an async load is already running, waiting til it's done
12980 var f = function(){
12981 if(!this.loading){ // done loading
12982 clearInterval(timer);
12983 this.expand(deep, anim, callback);
12985 }.createDelegate(this);
12986 timer = setInterval(f, 200);
12990 if(this.fireEvent("beforeload", this) === false){
12993 this.loading = true;
12994 this.ui.beforeLoad(this);
12995 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12997 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
13001 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
13005 * Returns true if this node is currently loading
13006 * @return {Boolean}
13008 isLoading : function(){
13009 return this.loading;
13012 loadComplete : function(deep, anim, callback){
13013 this.loading = false;
13014 this.loaded = true;
13015 this.ui.afterLoad(this);
13016 this.fireEvent("load", this);
13017 this.expand(deep, anim, callback);
13021 * Returns true if this node has been loaded
13022 * @return {Boolean}
13024 isLoaded : function(){
13025 return this.loaded;
13028 hasChildNodes : function(){
13029 if(!this.isLeaf() && !this.loaded){
13032 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
13037 * Trigger a reload for this node
13038 * @param {Function} callback
13040 reload : function(callback){
13041 this.collapse(false, false);
13042 while(this.firstChild){
13043 this.removeChild(this.firstChild);
13045 this.childrenRendered = false;
13046 this.loaded = false;
13047 if(this.isHiddenRoot()){
13048 this.expanded = false;
13050 this.expand(false, false, callback);
13054 * Ext JS Library 1.1.1
13055 * Copyright(c) 2006-2007, Ext JS, LLC.
13057 * Originally Released Under LGPL - original licence link has changed is not relivant.
13060 * <script type="text/javascript">
13064 * @class Roo.tree.TreeNodeUI
13066 * @param {Object} node The node to render
13067 * The TreeNode UI implementation is separate from the
13068 * tree implementation. Unless you are customizing the tree UI,
13069 * you should never have to use this directly.
13071 Roo.tree.TreeNodeUI = function(node){
13073 this.rendered = false;
13074 this.animating = false;
13075 this.emptyIcon = Roo.BLANK_IMAGE_URL;
13078 Roo.tree.TreeNodeUI.prototype = {
13079 removeChild : function(node){
13081 this.ctNode.removeChild(node.ui.getEl());
13085 beforeLoad : function(){
13086 this.addClass("x-tree-node-loading");
13089 afterLoad : function(){
13090 this.removeClass("x-tree-node-loading");
13093 onTextChange : function(node, text, oldText){
13095 this.textNode.innerHTML = text;
13099 onDisableChange : function(node, state){
13100 this.disabled = state;
13102 this.addClass("x-tree-node-disabled");
13104 this.removeClass("x-tree-node-disabled");
13108 onSelectedChange : function(state){
13111 this.addClass("x-tree-selected");
13114 this.removeClass("x-tree-selected");
13118 onMove : function(tree, node, oldParent, newParent, index, refNode){
13119 this.childIndent = null;
13121 var targetNode = newParent.ui.getContainer();
13122 if(!targetNode){//target not rendered
13123 this.holder = document.createElement("div");
13124 this.holder.appendChild(this.wrap);
13127 var insertBefore = refNode ? refNode.ui.getEl() : null;
13129 targetNode.insertBefore(this.wrap, insertBefore);
13131 targetNode.appendChild(this.wrap);
13133 this.node.renderIndent(true);
13137 addClass : function(cls){
13139 Roo.fly(this.elNode).addClass(cls);
13143 removeClass : function(cls){
13145 Roo.fly(this.elNode).removeClass(cls);
13149 remove : function(){
13151 this.holder = document.createElement("div");
13152 this.holder.appendChild(this.wrap);
13156 fireEvent : function(){
13157 return this.node.fireEvent.apply(this.node, arguments);
13160 initEvents : function(){
13161 this.node.on("move", this.onMove, this);
13162 var E = Roo.EventManager;
13163 var a = this.anchor;
13165 var el = Roo.fly(a, '_treeui');
13167 if(Roo.isOpera){ // opera render bug ignores the CSS
13168 el.setStyle("text-decoration", "none");
13171 el.on("click", this.onClick, this);
13172 el.on("dblclick", this.onDblClick, this);
13175 Roo.EventManager.on(this.checkbox,
13176 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13179 el.on("contextmenu", this.onContextMenu, this);
13181 var icon = Roo.fly(this.iconNode);
13182 icon.on("click", this.onClick, this);
13183 icon.on("dblclick", this.onDblClick, this);
13184 icon.on("contextmenu", this.onContextMenu, this);
13185 E.on(this.ecNode, "click", this.ecClick, this, true);
13187 if(this.node.disabled){
13188 this.addClass("x-tree-node-disabled");
13190 if(this.node.hidden){
13191 this.addClass("x-tree-node-disabled");
13193 var ot = this.node.getOwnerTree();
13194 var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
13195 if(dd && (!this.node.isRoot || ot.rootVisible)){
13196 Roo.dd.Registry.register(this.elNode, {
13198 handles: this.getDDHandles(),
13204 getDDHandles : function(){
13205 return [this.iconNode, this.textNode];
13210 this.wrap.style.display = "none";
13216 this.wrap.style.display = "";
13220 onContextMenu : function(e){
13221 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13222 e.preventDefault();
13224 this.fireEvent("contextmenu", this.node, e);
13228 onClick : function(e){
13233 if(this.fireEvent("beforeclick", this.node, e) !== false){
13234 if(!this.disabled && this.node.attributes.href){
13235 this.fireEvent("click", this.node, e);
13238 e.preventDefault();
13243 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13244 this.node.toggle();
13247 this.fireEvent("click", this.node, e);
13253 onDblClick : function(e){
13254 e.preventDefault();
13259 this.toggleCheck();
13261 if(!this.animating && this.node.hasChildNodes()){
13262 this.node.toggle();
13264 this.fireEvent("dblclick", this.node, e);
13267 onCheckChange : function(){
13268 var checked = this.checkbox.checked;
13269 this.node.attributes.checked = checked;
13270 this.fireEvent('checkchange', this.node, checked);
13273 ecClick : function(e){
13274 if(!this.animating && this.node.hasChildNodes()){
13275 this.node.toggle();
13279 startDrop : function(){
13280 this.dropping = true;
13283 // delayed drop so the click event doesn't get fired on a drop
13284 endDrop : function(){
13285 setTimeout(function(){
13286 this.dropping = false;
13287 }.createDelegate(this), 50);
13290 expand : function(){
13291 this.updateExpandIcon();
13292 this.ctNode.style.display = "";
13295 focus : function(){
13296 if(!this.node.preventHScroll){
13297 try{this.anchor.focus();
13299 }else if(!Roo.isIE){
13301 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13302 var l = noscroll.scrollLeft;
13303 this.anchor.focus();
13304 noscroll.scrollLeft = l;
13309 toggleCheck : function(value){
13310 var cb = this.checkbox;
13312 cb.checked = (value === undefined ? !cb.checked : value);
13318 this.anchor.blur();
13322 animExpand : function(callback){
13323 var ct = Roo.get(this.ctNode);
13325 if(!this.node.hasChildNodes()){
13326 this.updateExpandIcon();
13327 this.ctNode.style.display = "";
13328 Roo.callback(callback);
13331 this.animating = true;
13332 this.updateExpandIcon();
13335 callback : function(){
13336 this.animating = false;
13337 Roo.callback(callback);
13340 duration: this.node.ownerTree.duration || .25
13344 highlight : function(){
13345 var tree = this.node.getOwnerTree();
13346 Roo.fly(this.wrap).highlight(
13347 tree.hlColor || "C3DAF9",
13348 {endColor: tree.hlBaseColor}
13352 collapse : function(){
13353 this.updateExpandIcon();
13354 this.ctNode.style.display = "none";
13357 animCollapse : function(callback){
13358 var ct = Roo.get(this.ctNode);
13359 ct.enableDisplayMode('block');
13362 this.animating = true;
13363 this.updateExpandIcon();
13366 callback : function(){
13367 this.animating = false;
13368 Roo.callback(callback);
13371 duration: this.node.ownerTree.duration || .25
13375 getContainer : function(){
13376 return this.ctNode;
13379 getEl : function(){
13383 appendDDGhost : function(ghostNode){
13384 ghostNode.appendChild(this.elNode.cloneNode(true));
13387 getDDRepairXY : function(){
13388 return Roo.lib.Dom.getXY(this.iconNode);
13391 onRender : function(){
13395 render : function(bulkRender){
13396 var n = this.node, a = n.attributes;
13397 var targetNode = n.parentNode ?
13398 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13400 if(!this.rendered){
13401 this.rendered = true;
13403 this.renderElements(n, a, targetNode, bulkRender);
13406 if(this.textNode.setAttributeNS){
13407 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13409 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13412 this.textNode.setAttribute("ext:qtip", a.qtip);
13414 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13417 }else if(a.qtipCfg){
13418 a.qtipCfg.target = Roo.id(this.textNode);
13419 Roo.QuickTips.register(a.qtipCfg);
13422 if(!this.node.expanded){
13423 this.updateExpandIcon();
13426 if(bulkRender === true) {
13427 targetNode.appendChild(this.wrap);
13432 renderElements : function(n, a, targetNode, bulkRender)
13434 // add some indent caching, this helps performance when rendering a large tree
13435 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13436 var t = n.getOwnerTree();
13437 var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13438 if (typeof(n.attributes.html) != 'undefined') {
13439 txt = n.attributes.html;
13441 var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13442 var cb = typeof a.checked == 'boolean';
13443 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13444 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13445 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13446 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13447 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13448 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13449 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13450 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
13451 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13452 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13455 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13456 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13457 n.nextSibling.ui.getEl(), buf.join(""));
13459 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13462 this.elNode = this.wrap.childNodes[0];
13463 this.ctNode = this.wrap.childNodes[1];
13464 var cs = this.elNode.childNodes;
13465 this.indentNode = cs[0];
13466 this.ecNode = cs[1];
13467 this.iconNode = cs[2];
13470 this.checkbox = cs[3];
13473 this.anchor = cs[index];
13474 this.textNode = cs[index].firstChild;
13477 getAnchor : function(){
13478 return this.anchor;
13481 getTextEl : function(){
13482 return this.textNode;
13485 getIconEl : function(){
13486 return this.iconNode;
13489 isChecked : function(){
13490 return this.checkbox ? this.checkbox.checked : false;
13493 updateExpandIcon : function(){
13495 var n = this.node, c1, c2;
13496 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13497 var hasChild = n.hasChildNodes();
13501 c1 = "x-tree-node-collapsed";
13502 c2 = "x-tree-node-expanded";
13505 c1 = "x-tree-node-expanded";
13506 c2 = "x-tree-node-collapsed";
13509 this.removeClass("x-tree-node-leaf");
13510 this.wasLeaf = false;
13512 if(this.c1 != c1 || this.c2 != c2){
13513 Roo.fly(this.elNode).replaceClass(c1, c2);
13514 this.c1 = c1; this.c2 = c2;
13517 // this changes non-leafs into leafs if they have no children.
13518 // it's not very rational behaviour..
13520 if(!this.wasLeaf && this.node.leaf){
13521 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13524 this.wasLeaf = true;
13527 var ecc = "x-tree-ec-icon "+cls;
13528 if(this.ecc != ecc){
13529 this.ecNode.className = ecc;
13535 getChildIndent : function(){
13536 if(!this.childIndent){
13540 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13542 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13544 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13549 this.childIndent = buf.join("");
13551 return this.childIndent;
13554 renderIndent : function(){
13557 var p = this.node.parentNode;
13559 indent = p.ui.getChildIndent();
13561 if(this.indentMarkup != indent){ // don't rerender if not required
13562 this.indentNode.innerHTML = indent;
13563 this.indentMarkup = indent;
13565 this.updateExpandIcon();
13570 Roo.tree.RootTreeNodeUI = function(){
13571 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13573 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13574 render : function(){
13575 if(!this.rendered){
13576 var targetNode = this.node.ownerTree.innerCt.dom;
13577 this.node.expanded = true;
13578 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13579 this.wrap = this.ctNode = targetNode.firstChild;
13582 collapse : function(){
13584 expand : function(){
13588 * Ext JS Library 1.1.1
13589 * Copyright(c) 2006-2007, Ext JS, LLC.
13591 * Originally Released Under LGPL - original licence link has changed is not relivant.
13594 * <script type="text/javascript">
13597 * @class Roo.tree.TreeLoader
13598 * @extends Roo.util.Observable
13599 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13600 * nodes from a specified URL. The response must be a javascript Array definition
13601 * who's elements are node definition objects. eg:
13606 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13607 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13614 * The old style respose with just an array is still supported, but not recommended.
13617 * A server request is sent, and child nodes are loaded only when a node is expanded.
13618 * The loading node's id is passed to the server under the parameter name "node" to
13619 * enable the server to produce the correct child nodes.
13621 * To pass extra parameters, an event handler may be attached to the "beforeload"
13622 * event, and the parameters specified in the TreeLoader's baseParams property:
13624 myTreeLoader.on("beforeload", function(treeLoader, node) {
13625 this.baseParams.category = node.attributes.category;
13630 * This would pass an HTTP parameter called "category" to the server containing
13631 * the value of the Node's "category" attribute.
13633 * Creates a new Treeloader.
13634 * @param {Object} config A config object containing config properties.
13636 Roo.tree.TreeLoader = function(config){
13637 this.baseParams = {};
13638 this.requestMethod = "POST";
13639 Roo.apply(this, config);
13644 * @event beforeload
13645 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13646 * @param {Object} This TreeLoader object.
13647 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13648 * @param {Object} callback The callback function specified in the {@link #load} call.
13653 * Fires when the node has been successfuly loaded.
13654 * @param {Object} This TreeLoader object.
13655 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13656 * @param {Object} response The response object containing the data from the server.
13660 * @event loadexception
13661 * Fires if the network request failed.
13662 * @param {Object} This TreeLoader object.
13663 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13664 * @param {Object} response The response object containing the data from the server.
13666 loadexception : true,
13669 * Fires before a node is created, enabling you to return custom Node types
13670 * @param {Object} This TreeLoader object.
13671 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13676 Roo.tree.TreeLoader.superclass.constructor.call(this);
13679 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13681 * @cfg {String} dataUrl The URL from which to request a Json string which
13682 * specifies an array of node definition object representing the child nodes
13686 * @cfg {String} requestMethod either GET or POST
13687 * defaults to POST (due to BC)
13691 * @cfg {Object} baseParams (optional) An object containing properties which
13692 * specify HTTP parameters to be passed to each request for child nodes.
13695 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13696 * created by this loader. If the attributes sent by the server have an attribute in this object,
13697 * they take priority.
13700 * @cfg {Object} uiProviders (optional) An object containing properties which
13702 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13703 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13704 * <i>uiProvider</i> attribute of a returned child node is a string rather
13705 * than a reference to a TreeNodeUI implementation, this that string value
13706 * is used as a property name in the uiProviders object. You can define the provider named
13707 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13712 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13713 * child nodes before loading.
13715 clearOnLoad : true,
13718 * @cfg {String} root (optional) Default to false. Use this to read data from an object
13719 * property on loading, rather than expecting an array. (eg. more compatible to a standard
13720 * Grid query { data : [ .....] }
13725 * @cfg {String} queryParam (optional)
13726 * Name of the query as it will be passed on the querystring (defaults to 'node')
13727 * eg. the request will be ?node=[id]
13734 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13735 * This is called automatically when a node is expanded, but may be used to reload
13736 * a node (or append new children if the {@link #clearOnLoad} option is false.)
13737 * @param {Roo.tree.TreeNode} node
13738 * @param {Function} callback
13740 load : function(node, callback){
13741 if(this.clearOnLoad){
13742 while(node.firstChild){
13743 node.removeChild(node.firstChild);
13746 if(node.attributes.children){ // preloaded json children
13747 var cs = node.attributes.children;
13748 for(var i = 0, len = cs.length; i < len; i++){
13749 node.appendChild(this.createNode(cs[i]));
13751 if(typeof callback == "function"){
13754 }else if(this.dataUrl){
13755 this.requestData(node, callback);
13759 getParams: function(node){
13760 var buf = [], bp = this.baseParams;
13761 for(var key in bp){
13762 if(typeof bp[key] != "function"){
13763 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13766 var n = this.queryParam === false ? 'node' : this.queryParam;
13767 buf.push(n + "=", encodeURIComponent(node.id));
13768 return buf.join("");
13771 requestData : function(node, callback){
13772 if(this.fireEvent("beforeload", this, node, callback) !== false){
13773 this.transId = Roo.Ajax.request({
13774 method:this.requestMethod,
13775 url: this.dataUrl||this.url,
13776 success: this.handleResponse,
13777 failure: this.handleFailure,
13779 argument: {callback: callback, node: node},
13780 params: this.getParams(node)
13783 // if the load is cancelled, make sure we notify
13784 // the node that we are done
13785 if(typeof callback == "function"){
13791 isLoading : function(){
13792 return this.transId ? true : false;
13795 abort : function(){
13796 if(this.isLoading()){
13797 Roo.Ajax.abort(this.transId);
13802 createNode : function(attr)
13804 // apply baseAttrs, nice idea Corey!
13805 if(this.baseAttrs){
13806 Roo.applyIf(attr, this.baseAttrs);
13808 if(this.applyLoader !== false){
13809 attr.loader = this;
13811 // uiProvider = depreciated..
13813 if(typeof(attr.uiProvider) == 'string'){
13814 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
13815 /** eval:var:attr */ eval(attr.uiProvider);
13817 if(typeof(this.uiProviders['default']) != 'undefined') {
13818 attr.uiProvider = this.uiProviders['default'];
13821 this.fireEvent('create', this, attr);
13823 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13825 new Roo.tree.TreeNode(attr) :
13826 new Roo.tree.AsyncTreeNode(attr));
13829 processResponse : function(response, node, callback)
13831 var json = response.responseText;
13834 var o = Roo.decode(json);
13836 if (this.root === false && typeof(o.success) != undefined) {
13837 this.root = 'data'; // the default behaviour for list like data..
13840 if (this.root !== false && !o.success) {
13841 // it's a failure condition.
13842 var a = response.argument;
13843 this.fireEvent("loadexception", this, a.node, response);
13844 Roo.log("Load failed - should have a handler really");
13850 if (this.root !== false) {
13854 for(var i = 0, len = o.length; i < len; i++){
13855 var n = this.createNode(o[i]);
13857 node.appendChild(n);
13860 if(typeof callback == "function"){
13861 callback(this, node);
13864 this.handleFailure(response);
13868 handleResponse : function(response){
13869 this.transId = false;
13870 var a = response.argument;
13871 this.processResponse(response, a.node, a.callback);
13872 this.fireEvent("load", this, a.node, response);
13875 handleFailure : function(response)
13877 // should handle failure better..
13878 this.transId = false;
13879 var a = response.argument;
13880 this.fireEvent("loadexception", this, a.node, response);
13881 if(typeof a.callback == "function"){
13882 a.callback(this, a.node);
13887 * Ext JS Library 1.1.1
13888 * Copyright(c) 2006-2007, Ext JS, LLC.
13890 * Originally Released Under LGPL - original licence link has changed is not relivant.
13893 * <script type="text/javascript">
13897 * @class Roo.tree.TreeFilter
13898 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13899 * @param {TreePanel} tree
13900 * @param {Object} config (optional)
13902 Roo.tree.TreeFilter = function(tree, config){
13904 this.filtered = {};
13905 Roo.apply(this, config);
13908 Roo.tree.TreeFilter.prototype = {
13915 * Filter the data by a specific attribute.
13916 * @param {String/RegExp} value Either string that the attribute value
13917 * should start with or a RegExp to test against the attribute
13918 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13919 * @param {TreeNode} startNode (optional) The node to start the filter at.
13921 filter : function(value, attr, startNode){
13922 attr = attr || "text";
13924 if(typeof value == "string"){
13925 var vlen = value.length;
13926 // auto clear empty filter
13927 if(vlen == 0 && this.clearBlank){
13931 value = value.toLowerCase();
13933 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13935 }else if(value.exec){ // regex?
13937 return value.test(n.attributes[attr]);
13940 throw 'Illegal filter type, must be string or regex';
13942 this.filterBy(f, null, startNode);
13946 * Filter by a function. The passed function will be called with each
13947 * node in the tree (or from the startNode). If the function returns true, the node is kept
13948 * otherwise it is filtered. If a node is filtered, its children are also filtered.
13949 * @param {Function} fn The filter function
13950 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13952 filterBy : function(fn, scope, startNode){
13953 startNode = startNode || this.tree.root;
13954 if(this.autoClear){
13957 var af = this.filtered, rv = this.reverse;
13958 var f = function(n){
13959 if(n == startNode){
13965 var m = fn.call(scope || n, n);
13973 startNode.cascade(f);
13976 if(typeof id != "function"){
13978 if(n && n.parentNode){
13979 n.parentNode.removeChild(n);
13987 * Clears the current filter. Note: with the "remove" option
13988 * set a filter cannot be cleared.
13990 clear : function(){
13992 var af = this.filtered;
13994 if(typeof id != "function"){
14001 this.filtered = {};
14006 * Ext JS Library 1.1.1
14007 * Copyright(c) 2006-2007, Ext JS, LLC.
14009 * Originally Released Under LGPL - original licence link has changed is not relivant.
14012 * <script type="text/javascript">
14017 * @class Roo.tree.TreeSorter
14018 * Provides sorting of nodes in a TreePanel
14020 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
14021 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
14022 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
14023 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
14024 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
14025 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
14027 * @param {TreePanel} tree
14028 * @param {Object} config
14030 Roo.tree.TreeSorter = function(tree, config){
14031 Roo.apply(this, config);
14032 tree.on("beforechildrenrendered", this.doSort, this);
14033 tree.on("append", this.updateSort, this);
14034 tree.on("insert", this.updateSort, this);
14036 var dsc = this.dir && this.dir.toLowerCase() == "desc";
14037 var p = this.property || "text";
14038 var sortType = this.sortType;
14039 var fs = this.folderSort;
14040 var cs = this.caseSensitive === true;
14041 var leafAttr = this.leafAttr || 'leaf';
14043 this.sortFn = function(n1, n2){
14045 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
14048 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
14052 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
14053 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
14055 return dsc ? +1 : -1;
14057 return dsc ? -1 : +1;
14064 Roo.tree.TreeSorter.prototype = {
14065 doSort : function(node){
14066 node.sort(this.sortFn);
14069 compareNodes : function(n1, n2){
14070 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14073 updateSort : function(tree, node){
14074 if(node.childrenRendered){
14075 this.doSort.defer(1, this, [node]);
14080 * Ext JS Library 1.1.1
14081 * Copyright(c) 2006-2007, Ext JS, LLC.
14083 * Originally Released Under LGPL - original licence link has changed is not relivant.
14086 * <script type="text/javascript">
14089 if(Roo.dd.DropZone){
14091 Roo.tree.TreeDropZone = function(tree, config){
14092 this.allowParentInsert = false;
14093 this.allowContainerDrop = false;
14094 this.appendOnly = false;
14095 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14097 this.lastInsertClass = "x-tree-no-status";
14098 this.dragOverData = {};
14101 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14102 ddGroup : "TreeDD",
14105 expandDelay : 1000,
14107 expandNode : function(node){
14108 if(node.hasChildNodes() && !node.isExpanded()){
14109 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14113 queueExpand : function(node){
14114 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14117 cancelExpand : function(){
14118 if(this.expandProcId){
14119 clearTimeout(this.expandProcId);
14120 this.expandProcId = false;
14124 isValidDropPoint : function(n, pt, dd, e, data){
14125 if(!n || !data){ return false; }
14126 var targetNode = n.node;
14127 var dropNode = data.node;
14128 // default drop rules
14129 if(!(targetNode && targetNode.isTarget && pt)){
14132 if(pt == "append" && targetNode.allowChildren === false){
14135 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14138 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14141 // reuse the object
14142 var overEvent = this.dragOverData;
14143 overEvent.tree = this.tree;
14144 overEvent.target = targetNode;
14145 overEvent.data = data;
14146 overEvent.point = pt;
14147 overEvent.source = dd;
14148 overEvent.rawEvent = e;
14149 overEvent.dropNode = dropNode;
14150 overEvent.cancel = false;
14151 var result = this.tree.fireEvent("nodedragover", overEvent);
14152 return overEvent.cancel === false && result !== false;
14155 getDropPoint : function(e, n, dd)
14159 return tn.allowChildren !== false ? "append" : false; // always append for root
14161 var dragEl = n.ddel;
14162 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14163 var y = Roo.lib.Event.getPageY(e);
14164 //var noAppend = tn.allowChildren === false || tn.isLeaf();
14166 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14167 var noAppend = tn.allowChildren === false;
14168 if(this.appendOnly || tn.parentNode.allowChildren === false){
14169 return noAppend ? false : "append";
14171 var noBelow = false;
14172 if(!this.allowParentInsert){
14173 noBelow = tn.hasChildNodes() && tn.isExpanded();
14175 var q = (b - t) / (noAppend ? 2 : 3);
14176 if(y >= t && y < (t + q)){
14178 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14185 onNodeEnter : function(n, dd, e, data)
14187 this.cancelExpand();
14190 onNodeOver : function(n, dd, e, data)
14193 var pt = this.getDropPoint(e, n, dd);
14196 // auto node expand check
14197 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14198 this.queueExpand(node);
14199 }else if(pt != "append"){
14200 this.cancelExpand();
14203 // set the insert point style on the target node
14204 var returnCls = this.dropNotAllowed;
14205 if(this.isValidDropPoint(n, pt, dd, e, data)){
14210 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14211 cls = "x-tree-drag-insert-above";
14212 }else if(pt == "below"){
14213 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14214 cls = "x-tree-drag-insert-below";
14216 returnCls = "x-tree-drop-ok-append";
14217 cls = "x-tree-drag-append";
14219 if(this.lastInsertClass != cls){
14220 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14221 this.lastInsertClass = cls;
14228 onNodeOut : function(n, dd, e, data){
14230 this.cancelExpand();
14231 this.removeDropIndicators(n);
14234 onNodeDrop : function(n, dd, e, data){
14235 var point = this.getDropPoint(e, n, dd);
14236 var targetNode = n.node;
14237 targetNode.ui.startDrop();
14238 if(!this.isValidDropPoint(n, point, dd, e, data)){
14239 targetNode.ui.endDrop();
14242 // first try to find the drop node
14243 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14246 target: targetNode,
14251 dropNode: dropNode,
14254 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14255 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14256 targetNode.ui.endDrop();
14259 // allow target changing
14260 targetNode = dropEvent.target;
14261 if(point == "append" && !targetNode.isExpanded()){
14262 targetNode.expand(false, null, function(){
14263 this.completeDrop(dropEvent);
14264 }.createDelegate(this));
14266 this.completeDrop(dropEvent);
14271 completeDrop : function(de){
14272 var ns = de.dropNode, p = de.point, t = de.target;
14273 if(!(ns instanceof Array)){
14277 for(var i = 0, len = ns.length; i < len; i++){
14280 t.parentNode.insertBefore(n, t);
14281 }else if(p == "below"){
14282 t.parentNode.insertBefore(n, t.nextSibling);
14288 if(this.tree.hlDrop){
14292 this.tree.fireEvent("nodedrop", de);
14295 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14296 if(this.tree.hlDrop){
14297 dropNode.ui.focus();
14298 dropNode.ui.highlight();
14300 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14303 getTree : function(){
14307 removeDropIndicators : function(n){
14310 Roo.fly(el).removeClass([
14311 "x-tree-drag-insert-above",
14312 "x-tree-drag-insert-below",
14313 "x-tree-drag-append"]);
14314 this.lastInsertClass = "_noclass";
14318 beforeDragDrop : function(target, e, id){
14319 this.cancelExpand();
14323 afterRepair : function(data){
14324 if(data && Roo.enableFx){
14325 data.node.ui.highlight();
14335 * Ext JS Library 1.1.1
14336 * Copyright(c) 2006-2007, Ext JS, LLC.
14338 * Originally Released Under LGPL - original licence link has changed is not relivant.
14341 * <script type="text/javascript">
14345 if(Roo.dd.DragZone){
14346 Roo.tree.TreeDragZone = function(tree, config){
14347 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14351 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14352 ddGroup : "TreeDD",
14354 onBeforeDrag : function(data, e){
14356 return n && n.draggable && !n.disabled;
14360 onInitDrag : function(e){
14361 var data = this.dragData;
14362 this.tree.getSelectionModel().select(data.node);
14363 this.proxy.update("");
14364 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14365 this.tree.fireEvent("startdrag", this.tree, data.node, e);
14368 getRepairXY : function(e, data){
14369 return data.node.ui.getDDRepairXY();
14372 onEndDrag : function(data, e){
14373 this.tree.fireEvent("enddrag", this.tree, data.node, e);
14378 onValidDrop : function(dd, e, id){
14379 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14383 beforeInvalidDrop : function(e, id){
14384 // this scrolls the original position back into view
14385 var sm = this.tree.getSelectionModel();
14386 sm.clearSelections();
14387 sm.select(this.dragData.node);
14392 * Ext JS Library 1.1.1
14393 * Copyright(c) 2006-2007, Ext JS, LLC.
14395 * Originally Released Under LGPL - original licence link has changed is not relivant.
14398 * <script type="text/javascript">
14401 * @class Roo.tree.TreeEditor
14402 * @extends Roo.Editor
14403 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
14404 * as the editor field.
14406 * @param {Object} config (used to be the tree panel.)
14407 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14409 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14410 * @cfg {Roo.form.TextField|Object} field The field configuration
14414 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14417 if (oldconfig) { // old style..
14418 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14421 tree = config.tree;
14422 config.field = config.field || {};
14423 config.field.xtype = 'TextField';
14424 field = Roo.factory(config.field, Roo.form);
14426 config = config || {};
14431 * @event beforenodeedit
14432 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
14433 * false from the handler of this event.
14434 * @param {Editor} this
14435 * @param {Roo.tree.Node} node
14437 "beforenodeedit" : true
14441 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14445 tree.on('beforeclick', this.beforeNodeClick, this);
14446 tree.getTreeEl().on('mousedown', this.hide, this);
14447 this.on('complete', this.updateNode, this);
14448 this.on('beforestartedit', this.fitToTree, this);
14449 this.on('startedit', this.bindScroll, this, {delay:10});
14450 this.on('specialkey', this.onSpecialKey, this);
14453 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14455 * @cfg {String} alignment
14456 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14462 * @cfg {Boolean} hideEl
14463 * True to hide the bound element while the editor is displayed (defaults to false)
14467 * @cfg {String} cls
14468 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14470 cls: "x-small-editor x-tree-editor",
14472 * @cfg {Boolean} shim
14473 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14479 * @cfg {Number} maxWidth
14480 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
14481 * the containing tree element's size, it will be automatically limited for you to the container width, taking
14482 * scroll and client offsets into account prior to each edit.
14489 fitToTree : function(ed, el){
14490 var td = this.tree.getTreeEl().dom, nd = el.dom;
14491 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
14492 td.scrollLeft = nd.offsetLeft;
14496 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14497 this.setSize(w, '');
14499 return this.fireEvent('beforenodeedit', this, this.editNode);
14504 triggerEdit : function(node){
14505 this.completeEdit();
14506 this.editNode = node;
14507 this.startEdit(node.ui.textNode, node.text);
14511 bindScroll : function(){
14512 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14516 beforeNodeClick : function(node, e){
14517 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14518 this.lastClick = new Date();
14519 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14521 this.triggerEdit(node);
14528 updateNode : function(ed, value){
14529 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14530 this.editNode.setText(value);
14534 onHide : function(){
14535 Roo.tree.TreeEditor.superclass.onHide.call(this);
14537 this.editNode.ui.focus();
14542 onSpecialKey : function(field, e){
14543 var k = e.getKey();
14547 }else if(k == e.ENTER && !e.hasModifier()){
14549 this.completeEdit();
14552 });//<Script type="text/javascript">
14555 * Ext JS Library 1.1.1
14556 * Copyright(c) 2006-2007, Ext JS, LLC.
14558 * Originally Released Under LGPL - original licence link has changed is not relivant.
14561 * <script type="text/javascript">
14565 * Not documented??? - probably should be...
14568 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14569 //focus: Roo.emptyFn, // prevent odd scrolling behavior
14571 renderElements : function(n, a, targetNode, bulkRender){
14572 //consel.log("renderElements?");
14573 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14575 var t = n.getOwnerTree();
14576 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14578 var cols = t.columns;
14579 var bw = t.borderWidth;
14581 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14582 var cb = typeof a.checked == "boolean";
14583 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14584 var colcls = 'x-t-' + tid + '-c0';
14586 '<li class="x-tree-node">',
14589 '<div class="x-tree-node-el ', a.cls,'">',
14591 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14594 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14595 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
14596 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14597 (a.icon ? ' x-tree-node-inline-icon' : ''),
14598 (a.iconCls ? ' '+a.iconCls : ''),
14599 '" unselectable="on" />',
14600 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
14601 (a.checked ? 'checked="checked" />' : ' />')) : ''),
14603 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14604 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14605 '<span unselectable="on" qtip="' + tx + '">',
14609 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14610 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14612 for(var i = 1, len = cols.length; i < len; i++){
14614 colcls = 'x-t-' + tid + '-c' +i;
14615 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14616 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14617 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14623 '<div class="x-clear"></div></div>',
14624 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14627 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14628 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14629 n.nextSibling.ui.getEl(), buf.join(""));
14631 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14633 var el = this.wrap.firstChild;
14635 this.elNode = el.firstChild;
14636 this.ranchor = el.childNodes[1];
14637 this.ctNode = this.wrap.childNodes[1];
14638 var cs = el.firstChild.childNodes;
14639 this.indentNode = cs[0];
14640 this.ecNode = cs[1];
14641 this.iconNode = cs[2];
14644 this.checkbox = cs[3];
14647 this.anchor = cs[index];
14649 this.textNode = cs[index].firstChild;
14651 //el.on("click", this.onClick, this);
14652 //el.on("dblclick", this.onDblClick, this);
14655 // console.log(this);
14657 initEvents : function(){
14658 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14661 var a = this.ranchor;
14663 var el = Roo.get(a);
14665 if(Roo.isOpera){ // opera render bug ignores the CSS
14666 el.setStyle("text-decoration", "none");
14669 el.on("click", this.onClick, this);
14670 el.on("dblclick", this.onDblClick, this);
14671 el.on("contextmenu", this.onContextMenu, this);
14675 /*onSelectedChange : function(state){
14678 this.addClass("x-tree-selected");
14681 this.removeClass("x-tree-selected");
14684 addClass : function(cls){
14686 Roo.fly(this.elRow).addClass(cls);
14692 removeClass : function(cls){
14694 Roo.fly(this.elRow).removeClass(cls);
14700 });//<Script type="text/javascript">
14704 * Ext JS Library 1.1.1
14705 * Copyright(c) 2006-2007, Ext JS, LLC.
14707 * Originally Released Under LGPL - original licence link has changed is not relivant.
14710 * <script type="text/javascript">
14715 * @class Roo.tree.ColumnTree
14716 * @extends Roo.data.TreePanel
14717 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
14718 * @cfg {int} borderWidth compined right/left border allowance
14720 * @param {String/HTMLElement/Element} el The container element
14721 * @param {Object} config
14723 Roo.tree.ColumnTree = function(el, config)
14725 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14729 * Fire this event on a container when it resizes
14730 * @param {int} w Width
14731 * @param {int} h Height
14735 this.on('resize', this.onResize, this);
14738 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14742 borderWidth: Roo.isBorderBox ? 0 : 2,
14745 render : function(){
14746 // add the header.....
14748 Roo.tree.ColumnTree.superclass.render.apply(this);
14750 this.el.addClass('x-column-tree');
14752 this.headers = this.el.createChild(
14753 {cls:'x-tree-headers'},this.innerCt.dom);
14755 var cols = this.columns, c;
14756 var totalWidth = 0;
14758 var len = cols.length;
14759 for(var i = 0; i < len; i++){
14761 totalWidth += c.width;
14762 this.headEls.push(this.headers.createChild({
14763 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14765 cls:'x-tree-hd-text',
14768 style:'width:'+(c.width-this.borderWidth)+'px;'
14771 this.headers.createChild({cls:'x-clear'});
14772 // prevent floats from wrapping when clipped
14773 this.headers.setWidth(totalWidth);
14774 //this.innerCt.setWidth(totalWidth);
14775 this.innerCt.setStyle({ overflow: 'auto' });
14776 this.onResize(this.width, this.height);
14780 onResize : function(w,h)
14785 this.innerCt.setWidth(this.width);
14786 this.innerCt.setHeight(this.height-20);
14789 var cols = this.columns, c;
14790 var totalWidth = 0;
14792 var len = cols.length;
14793 for(var i = 0; i < len; i++){
14795 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14796 // it's the expander..
14797 expEl = this.headEls[i];
14800 totalWidth += c.width;
14804 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
14806 this.headers.setWidth(w-20);
14815 * Ext JS Library 1.1.1
14816 * Copyright(c) 2006-2007, Ext JS, LLC.
14818 * Originally Released Under LGPL - original licence link has changed is not relivant.
14821 * <script type="text/javascript">
14825 * @class Roo.menu.Menu
14826 * @extends Roo.util.Observable
14827 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
14828 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14830 * Creates a new Menu
14831 * @param {Object} config Configuration options
14833 Roo.menu.Menu = function(config){
14835 Roo.menu.Menu.superclass.constructor.call(this, config);
14837 this.id = this.id || Roo.id();
14840 * @event beforeshow
14841 * Fires before this menu is displayed
14842 * @param {Roo.menu.Menu} this
14846 * @event beforehide
14847 * Fires before this menu is hidden
14848 * @param {Roo.menu.Menu} this
14853 * Fires after this menu is displayed
14854 * @param {Roo.menu.Menu} this
14859 * Fires after this menu is hidden
14860 * @param {Roo.menu.Menu} this
14865 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14866 * @param {Roo.menu.Menu} this
14867 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14868 * @param {Roo.EventObject} e
14873 * Fires when the mouse is hovering over this menu
14874 * @param {Roo.menu.Menu} this
14875 * @param {Roo.EventObject} e
14876 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14881 * Fires when the mouse exits this menu
14882 * @param {Roo.menu.Menu} this
14883 * @param {Roo.EventObject} e
14884 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14889 * Fires when a menu item contained in this menu is clicked
14890 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14891 * @param {Roo.EventObject} e
14895 if (this.registerMenu) {
14896 Roo.menu.MenuMgr.register(this);
14899 var mis = this.items;
14900 this.items = new Roo.util.MixedCollection();
14902 this.add.apply(this, mis);
14906 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14908 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14912 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14913 * for bottom-right shadow (defaults to "sides")
14917 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14918 * this menu (defaults to "tl-tr?")
14920 subMenuAlign : "tl-tr?",
14922 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14923 * relative to its element of origin (defaults to "tl-bl?")
14925 defaultAlign : "tl-bl?",
14927 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14929 allowOtherMenus : false,
14931 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14933 registerMenu : true,
14938 render : function(){
14942 var el = this.el = new Roo.Layer({
14944 shadow:this.shadow,
14946 parentEl: this.parentEl || document.body,
14950 this.keyNav = new Roo.menu.MenuNav(this);
14953 el.addClass("x-menu-plain");
14956 el.addClass(this.cls);
14958 // generic focus element
14959 this.focusEl = el.createChild({
14960 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14962 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14963 //disabling touch- as it's causing issues ..
14964 //ul.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
14965 ul.on('click' , this.onClick, this);
14968 ul.on("mouseover", this.onMouseOver, this);
14969 ul.on("mouseout", this.onMouseOut, this);
14970 this.items.each(function(item){
14975 var li = document.createElement("li");
14976 li.className = "x-menu-list-item";
14977 ul.dom.appendChild(li);
14978 item.render(li, this);
14985 autoWidth : function(){
14986 var el = this.el, ul = this.ul;
14990 var w = this.width;
14993 }else if(Roo.isIE){
14994 el.setWidth(this.minWidth);
14995 var t = el.dom.offsetWidth; // force recalc
14996 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
15001 delayAutoWidth : function(){
15004 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
15006 this.awTask.delay(20);
15011 findTargetItem : function(e){
15012 var t = e.getTarget(".x-menu-list-item", this.ul, true);
15013 if(t && t.menuItemId){
15014 return this.items.get(t.menuItemId);
15019 onClick : function(e){
15020 Roo.log("menu.onClick");
15021 var t = this.findTargetItem(e);
15026 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
15027 if(t == this.activeItem && t.shouldDeactivate(e)){
15028 this.activeItem.deactivate();
15029 delete this.activeItem;
15033 this.setActiveItem(t, true);
15041 this.fireEvent("click", this, t, e);
15045 setActiveItem : function(item, autoExpand){
15046 if(item != this.activeItem){
15047 if(this.activeItem){
15048 this.activeItem.deactivate();
15050 this.activeItem = item;
15051 item.activate(autoExpand);
15052 }else if(autoExpand){
15058 tryActivate : function(start, step){
15059 var items = this.items;
15060 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
15061 var item = items.get(i);
15062 if(!item.disabled && item.canActivate){
15063 this.setActiveItem(item, false);
15071 onMouseOver : function(e){
15073 if(t = this.findTargetItem(e)){
15074 if(t.canActivate && !t.disabled){
15075 this.setActiveItem(t, true);
15078 this.fireEvent("mouseover", this, e, t);
15082 onMouseOut : function(e){
15084 if(t = this.findTargetItem(e)){
15085 if(t == this.activeItem && t.shouldDeactivate(e)){
15086 this.activeItem.deactivate();
15087 delete this.activeItem;
15090 this.fireEvent("mouseout", this, e, t);
15094 * Read-only. Returns true if the menu is currently displayed, else false.
15097 isVisible : function(){
15098 return this.el && !this.hidden;
15102 * Displays this menu relative to another element
15103 * @param {String/HTMLElement/Roo.Element} element The element to align to
15104 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15105 * the element (defaults to this.defaultAlign)
15106 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15108 show : function(el, pos, parentMenu){
15109 this.parentMenu = parentMenu;
15113 this.fireEvent("beforeshow", this);
15114 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15118 * Displays this menu at a specific xy position
15119 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15120 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15122 showAt : function(xy, parentMenu, /* private: */_e){
15123 this.parentMenu = parentMenu;
15128 this.fireEvent("beforeshow", this);
15129 xy = this.el.adjustForConstraints(xy);
15133 this.hidden = false;
15135 this.fireEvent("show", this);
15138 focus : function(){
15140 this.doFocus.defer(50, this);
15144 doFocus : function(){
15146 this.focusEl.focus();
15151 * Hides this menu and optionally all parent menus
15152 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15154 hide : function(deep){
15155 if(this.el && this.isVisible()){
15156 this.fireEvent("beforehide", this);
15157 if(this.activeItem){
15158 this.activeItem.deactivate();
15159 this.activeItem = null;
15162 this.hidden = true;
15163 this.fireEvent("hide", this);
15165 if(deep === true && this.parentMenu){
15166 this.parentMenu.hide(true);
15171 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15172 * Any of the following are valid:
15174 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15175 * <li>An HTMLElement object which will be converted to a menu item</li>
15176 * <li>A menu item config object that will be created as a new menu item</li>
15177 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15178 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15183 var menu = new Roo.menu.Menu();
15185 // Create a menu item to add by reference
15186 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15188 // Add a bunch of items at once using different methods.
15189 // Only the last item added will be returned.
15190 var item = menu.add(
15191 menuItem, // add existing item by ref
15192 'Dynamic Item', // new TextItem
15193 '-', // new separator
15194 { text: 'Config Item' } // new item by config
15197 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15198 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15201 var a = arguments, l = a.length, item;
15202 for(var i = 0; i < l; i++){
15204 if ((typeof(el) == "object") && el.xtype && el.xns) {
15205 el = Roo.factory(el, Roo.menu);
15208 if(el.render){ // some kind of Item
15209 item = this.addItem(el);
15210 }else if(typeof el == "string"){ // string
15211 if(el == "separator" || el == "-"){
15212 item = this.addSeparator();
15214 item = this.addText(el);
15216 }else if(el.tagName || el.el){ // element
15217 item = this.addElement(el);
15218 }else if(typeof el == "object"){ // must be menu item config?
15219 item = this.addMenuItem(el);
15226 * Returns this menu's underlying {@link Roo.Element} object
15227 * @return {Roo.Element} The element
15229 getEl : function(){
15237 * Adds a separator bar to the menu
15238 * @return {Roo.menu.Item} The menu item that was added
15240 addSeparator : function(){
15241 return this.addItem(new Roo.menu.Separator());
15245 * Adds an {@link Roo.Element} object to the menu
15246 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15247 * @return {Roo.menu.Item} The menu item that was added
15249 addElement : function(el){
15250 return this.addItem(new Roo.menu.BaseItem(el));
15254 * Adds an existing object based on {@link Roo.menu.Item} to the menu
15255 * @param {Roo.menu.Item} item The menu item to add
15256 * @return {Roo.menu.Item} The menu item that was added
15258 addItem : function(item){
15259 this.items.add(item);
15261 var li = document.createElement("li");
15262 li.className = "x-menu-list-item";
15263 this.ul.dom.appendChild(li);
15264 item.render(li, this);
15265 this.delayAutoWidth();
15271 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15272 * @param {Object} config A MenuItem config object
15273 * @return {Roo.menu.Item} The menu item that was added
15275 addMenuItem : function(config){
15276 if(!(config instanceof Roo.menu.Item)){
15277 if(typeof config.checked == "boolean"){ // must be check menu item config?
15278 config = new Roo.menu.CheckItem(config);
15280 config = new Roo.menu.Item(config);
15283 return this.addItem(config);
15287 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15288 * @param {String} text The text to display in the menu item
15289 * @return {Roo.menu.Item} The menu item that was added
15291 addText : function(text){
15292 return this.addItem(new Roo.menu.TextItem({ text : text }));
15296 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15297 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15298 * @param {Roo.menu.Item} item The menu item to add
15299 * @return {Roo.menu.Item} The menu item that was added
15301 insert : function(index, item){
15302 this.items.insert(index, item);
15304 var li = document.createElement("li");
15305 li.className = "x-menu-list-item";
15306 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15307 item.render(li, this);
15308 this.delayAutoWidth();
15314 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15315 * @param {Roo.menu.Item} item The menu item to remove
15317 remove : function(item){
15318 this.items.removeKey(item.id);
15323 * Removes and destroys all items in the menu
15325 removeAll : function(){
15327 while(f = this.items.first()){
15333 // MenuNav is a private utility class used internally by the Menu
15334 Roo.menu.MenuNav = function(menu){
15335 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15336 this.scope = this.menu = menu;
15339 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15340 doRelay : function(e, h){
15341 var k = e.getKey();
15342 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15343 this.menu.tryActivate(0, 1);
15346 return h.call(this.scope || this, e, this.menu);
15349 up : function(e, m){
15350 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15351 m.tryActivate(m.items.length-1, -1);
15355 down : function(e, m){
15356 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15357 m.tryActivate(0, 1);
15361 right : function(e, m){
15363 m.activeItem.expandMenu(true);
15367 left : function(e, m){
15369 if(m.parentMenu && m.parentMenu.activeItem){
15370 m.parentMenu.activeItem.activate();
15374 enter : function(e, m){
15376 e.stopPropagation();
15377 m.activeItem.onClick(e);
15378 m.fireEvent("click", this, m.activeItem);
15384 * Ext JS Library 1.1.1
15385 * Copyright(c) 2006-2007, Ext JS, LLC.
15387 * Originally Released Under LGPL - original licence link has changed is not relivant.
15390 * <script type="text/javascript">
15394 * @class Roo.menu.MenuMgr
15395 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15398 Roo.menu.MenuMgr = function(){
15399 var menus, active, groups = {}, attached = false, lastShow = new Date();
15401 // private - called when first menu is created
15404 active = new Roo.util.MixedCollection();
15405 Roo.get(document).addKeyListener(27, function(){
15406 if(active.length > 0){
15413 function hideAll(){
15414 if(active && active.length > 0){
15415 var c = active.clone();
15416 c.each(function(m){
15423 function onHide(m){
15425 if(active.length < 1){
15426 Roo.get(document).un("mousedown", onMouseDown);
15432 function onShow(m){
15433 var last = active.last();
15434 lastShow = new Date();
15437 Roo.get(document).on("mousedown", onMouseDown);
15441 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15442 m.parentMenu.activeChild = m;
15443 }else if(last && last.isVisible()){
15444 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15449 function onBeforeHide(m){
15451 m.activeChild.hide();
15453 if(m.autoHideTimer){
15454 clearTimeout(m.autoHideTimer);
15455 delete m.autoHideTimer;
15460 function onBeforeShow(m){
15461 var pm = m.parentMenu;
15462 if(!pm && !m.allowOtherMenus){
15464 }else if(pm && pm.activeChild && active != m){
15465 pm.activeChild.hide();
15470 function onMouseDown(e){
15471 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15477 function onBeforeCheck(mi, state){
15479 var g = groups[mi.group];
15480 for(var i = 0, l = g.length; i < l; i++){
15482 g[i].setChecked(false);
15491 * Hides all menus that are currently visible
15493 hideAll : function(){
15498 register : function(menu){
15502 menus[menu.id] = menu;
15503 menu.on("beforehide", onBeforeHide);
15504 menu.on("hide", onHide);
15505 menu.on("beforeshow", onBeforeShow);
15506 menu.on("show", onShow);
15507 var g = menu.group;
15508 if(g && menu.events["checkchange"]){
15512 groups[g].push(menu);
15513 menu.on("checkchange", onCheck);
15518 * Returns a {@link Roo.menu.Menu} object
15519 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15520 * be used to generate and return a new Menu instance.
15522 get : function(menu){
15523 if(typeof menu == "string"){ // menu id
15524 return menus[menu];
15525 }else if(menu.events){ // menu instance
15527 }else if(typeof menu.length == 'number'){ // array of menu items?
15528 return new Roo.menu.Menu({items:menu});
15529 }else{ // otherwise, must be a config
15530 return new Roo.menu.Menu(menu);
15535 unregister : function(menu){
15536 delete menus[menu.id];
15537 menu.un("beforehide", onBeforeHide);
15538 menu.un("hide", onHide);
15539 menu.un("beforeshow", onBeforeShow);
15540 menu.un("show", onShow);
15541 var g = menu.group;
15542 if(g && menu.events["checkchange"]){
15543 groups[g].remove(menu);
15544 menu.un("checkchange", onCheck);
15549 registerCheckable : function(menuItem){
15550 var g = menuItem.group;
15555 groups[g].push(menuItem);
15556 menuItem.on("beforecheckchange", onBeforeCheck);
15561 unregisterCheckable : function(menuItem){
15562 var g = menuItem.group;
15564 groups[g].remove(menuItem);
15565 menuItem.un("beforecheckchange", onBeforeCheck);
15571 * Ext JS Library 1.1.1
15572 * Copyright(c) 2006-2007, Ext JS, LLC.
15574 * Originally Released Under LGPL - original licence link has changed is not relivant.
15577 * <script type="text/javascript">
15582 * @class Roo.menu.BaseItem
15583 * @extends Roo.Component
15584 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
15585 * management and base configuration options shared by all menu components.
15587 * Creates a new BaseItem
15588 * @param {Object} config Configuration options
15590 Roo.menu.BaseItem = function(config){
15591 Roo.menu.BaseItem.superclass.constructor.call(this, config);
15596 * Fires when this item is clicked
15597 * @param {Roo.menu.BaseItem} this
15598 * @param {Roo.EventObject} e
15603 * Fires when this item is activated
15604 * @param {Roo.menu.BaseItem} this
15608 * @event deactivate
15609 * Fires when this item is deactivated
15610 * @param {Roo.menu.BaseItem} this
15616 this.on("click", this.handler, this.scope, true);
15620 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15622 * @cfg {Function} handler
15623 * A function that will handle the click event of this menu item (defaults to undefined)
15626 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15628 canActivate : false,
15631 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15636 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15638 activeClass : "x-menu-item-active",
15640 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15642 hideOnClick : true,
15644 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15649 ctype: "Roo.menu.BaseItem",
15652 actionMode : "container",
15655 render : function(container, parentMenu){
15656 this.parentMenu = parentMenu;
15657 Roo.menu.BaseItem.superclass.render.call(this, container);
15658 this.container.menuItemId = this.id;
15662 onRender : function(container, position){
15663 this.el = Roo.get(this.el);
15664 container.dom.appendChild(this.el.dom);
15668 onClick : function(e){
15669 if(!this.disabled && this.fireEvent("click", this, e) !== false
15670 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15671 this.handleClick(e);
15678 activate : function(){
15682 var li = this.container;
15683 li.addClass(this.activeClass);
15684 this.region = li.getRegion().adjust(2, 2, -2, -2);
15685 this.fireEvent("activate", this);
15690 deactivate : function(){
15691 this.container.removeClass(this.activeClass);
15692 this.fireEvent("deactivate", this);
15696 shouldDeactivate : function(e){
15697 return !this.region || !this.region.contains(e.getPoint());
15701 handleClick : function(e){
15702 if(this.hideOnClick){
15703 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15708 expandMenu : function(autoActivate){
15713 hideMenu : function(){
15718 * Ext JS Library 1.1.1
15719 * Copyright(c) 2006-2007, Ext JS, LLC.
15721 * Originally Released Under LGPL - original licence link has changed is not relivant.
15724 * <script type="text/javascript">
15728 * @class Roo.menu.Adapter
15729 * @extends Roo.menu.BaseItem
15730 * 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.
15731 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15733 * Creates a new Adapter
15734 * @param {Object} config Configuration options
15736 Roo.menu.Adapter = function(component, config){
15737 Roo.menu.Adapter.superclass.constructor.call(this, config);
15738 this.component = component;
15740 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15742 canActivate : true,
15745 onRender : function(container, position){
15746 this.component.render(container);
15747 this.el = this.component.getEl();
15751 activate : function(){
15755 this.component.focus();
15756 this.fireEvent("activate", this);
15761 deactivate : function(){
15762 this.fireEvent("deactivate", this);
15766 disable : function(){
15767 this.component.disable();
15768 Roo.menu.Adapter.superclass.disable.call(this);
15772 enable : function(){
15773 this.component.enable();
15774 Roo.menu.Adapter.superclass.enable.call(this);
15778 * Ext JS Library 1.1.1
15779 * Copyright(c) 2006-2007, Ext JS, LLC.
15781 * Originally Released Under LGPL - original licence link has changed is not relivant.
15784 * <script type="text/javascript">
15788 * @class Roo.menu.TextItem
15789 * @extends Roo.menu.BaseItem
15790 * Adds a static text string to a menu, usually used as either a heading or group separator.
15791 * Note: old style constructor with text is still supported.
15794 * Creates a new TextItem
15795 * @param {Object} cfg Configuration
15797 Roo.menu.TextItem = function(cfg){
15798 if (typeof(cfg) == 'string') {
15801 Roo.apply(this,cfg);
15804 Roo.menu.TextItem.superclass.constructor.call(this);
15807 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15809 * @cfg {Boolean} text Text to show on item.
15814 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15816 hideOnClick : false,
15818 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15820 itemCls : "x-menu-text",
15823 onRender : function(){
15824 var s = document.createElement("span");
15825 s.className = this.itemCls;
15826 s.innerHTML = this.text;
15828 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15832 * Ext JS Library 1.1.1
15833 * Copyright(c) 2006-2007, Ext JS, LLC.
15835 * Originally Released Under LGPL - original licence link has changed is not relivant.
15838 * <script type="text/javascript">
15842 * @class Roo.menu.Separator
15843 * @extends Roo.menu.BaseItem
15844 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15845 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15847 * @param {Object} config Configuration options
15849 Roo.menu.Separator = function(config){
15850 Roo.menu.Separator.superclass.constructor.call(this, config);
15853 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15855 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15857 itemCls : "x-menu-sep",
15859 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15861 hideOnClick : false,
15864 onRender : function(li){
15865 var s = document.createElement("span");
15866 s.className = this.itemCls;
15867 s.innerHTML = " ";
15869 li.addClass("x-menu-sep-li");
15870 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15874 * Ext JS Library 1.1.1
15875 * Copyright(c) 2006-2007, Ext JS, LLC.
15877 * Originally Released Under LGPL - original licence link has changed is not relivant.
15880 * <script type="text/javascript">
15883 * @class Roo.menu.Item
15884 * @extends Roo.menu.BaseItem
15885 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15886 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15887 * activation and click handling.
15889 * Creates a new Item
15890 * @param {Object} config Configuration options
15892 Roo.menu.Item = function(config){
15893 Roo.menu.Item.superclass.constructor.call(this, config);
15895 this.menu = Roo.menu.MenuMgr.get(this.menu);
15898 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15901 * @cfg {String} text
15902 * The text to show on the menu item.
15906 * @cfg {String} HTML to render in menu
15907 * The text to show on the menu item (HTML version).
15911 * @cfg {String} icon
15912 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15916 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15918 itemCls : "x-menu-item",
15920 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15922 canActivate : true,
15924 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15927 // doc'd in BaseItem
15931 ctype: "Roo.menu.Item",
15934 onRender : function(container, position){
15935 var el = document.createElement("a");
15936 el.hideFocus = true;
15937 el.unselectable = "on";
15938 el.href = this.href || "#";
15939 if(this.hrefTarget){
15940 el.target = this.hrefTarget;
15942 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
15944 var html = this.html.length ? this.html : String.format('{0}',this.text);
15946 el.innerHTML = String.format(
15947 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15948 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15950 Roo.menu.Item.superclass.onRender.call(this, container, position);
15954 * Sets the text to display in this menu item
15955 * @param {String} text The text to display
15956 * @param {Boolean} isHTML true to indicate text is pure html.
15958 setText : function(text, isHTML){
15966 var html = this.html.length ? this.html : String.format('{0}',this.text);
15968 this.el.update(String.format(
15969 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15970 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15971 this.parentMenu.autoWidth();
15976 handleClick : function(e){
15977 if(!this.href){ // if no link defined, stop the event automatically
15980 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15984 activate : function(autoExpand){
15985 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15995 shouldDeactivate : function(e){
15996 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15997 if(this.menu && this.menu.isVisible()){
15998 return !this.menu.getEl().getRegion().contains(e.getPoint());
16006 deactivate : function(){
16007 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
16012 expandMenu : function(autoActivate){
16013 if(!this.disabled && this.menu){
16014 clearTimeout(this.hideTimer);
16015 delete this.hideTimer;
16016 if(!this.menu.isVisible() && !this.showTimer){
16017 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
16018 }else if (this.menu.isVisible() && autoActivate){
16019 this.menu.tryActivate(0, 1);
16025 deferExpand : function(autoActivate){
16026 delete this.showTimer;
16027 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
16029 this.menu.tryActivate(0, 1);
16034 hideMenu : function(){
16035 clearTimeout(this.showTimer);
16036 delete this.showTimer;
16037 if(!this.hideTimer && this.menu && this.menu.isVisible()){
16038 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
16043 deferHide : function(){
16044 delete this.hideTimer;
16049 * Ext JS Library 1.1.1
16050 * Copyright(c) 2006-2007, Ext JS, LLC.
16052 * Originally Released Under LGPL - original licence link has changed is not relivant.
16055 * <script type="text/javascript">
16059 * @class Roo.menu.CheckItem
16060 * @extends Roo.menu.Item
16061 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
16063 * Creates a new CheckItem
16064 * @param {Object} config Configuration options
16066 Roo.menu.CheckItem = function(config){
16067 Roo.menu.CheckItem.superclass.constructor.call(this, config);
16070 * @event beforecheckchange
16071 * Fires before the checked value is set, providing an opportunity to cancel if needed
16072 * @param {Roo.menu.CheckItem} this
16073 * @param {Boolean} checked The new checked value that will be set
16075 "beforecheckchange" : true,
16077 * @event checkchange
16078 * Fires after the checked value has been set
16079 * @param {Roo.menu.CheckItem} this
16080 * @param {Boolean} checked The checked value that was set
16082 "checkchange" : true
16084 if(this.checkHandler){
16085 this.on('checkchange', this.checkHandler, this.scope);
16088 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16090 * @cfg {String} group
16091 * All check items with the same group name will automatically be grouped into a single-select
16092 * radio button group (defaults to '')
16095 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16097 itemCls : "x-menu-item x-menu-check-item",
16099 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16101 groupClass : "x-menu-group-item",
16104 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
16105 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16106 * initialized with checked = true will be rendered as checked.
16111 ctype: "Roo.menu.CheckItem",
16114 onRender : function(c){
16115 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16117 this.el.addClass(this.groupClass);
16119 Roo.menu.MenuMgr.registerCheckable(this);
16121 this.checked = false;
16122 this.setChecked(true, true);
16127 destroy : function(){
16129 Roo.menu.MenuMgr.unregisterCheckable(this);
16131 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16135 * Set the checked state of this item
16136 * @param {Boolean} checked The new checked value
16137 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16139 setChecked : function(state, suppressEvent){
16140 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16141 if(this.container){
16142 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16144 this.checked = state;
16145 if(suppressEvent !== true){
16146 this.fireEvent("checkchange", this, state);
16152 handleClick : function(e){
16153 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16154 this.setChecked(!this.checked);
16156 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16160 * Ext JS Library 1.1.1
16161 * Copyright(c) 2006-2007, Ext JS, LLC.
16163 * Originally Released Under LGPL - original licence link has changed is not relivant.
16166 * <script type="text/javascript">
16170 * @class Roo.menu.DateItem
16171 * @extends Roo.menu.Adapter
16172 * A menu item that wraps the {@link Roo.DatPicker} component.
16174 * Creates a new DateItem
16175 * @param {Object} config Configuration options
16177 Roo.menu.DateItem = function(config){
16178 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16179 /** The Roo.DatePicker object @type Roo.DatePicker */
16180 this.picker = this.component;
16181 this.addEvents({select: true});
16183 this.picker.on("render", function(picker){
16184 picker.getEl().swallowEvent("click");
16185 picker.container.addClass("x-menu-date-item");
16188 this.picker.on("select", this.onSelect, this);
16191 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16193 onSelect : function(picker, date){
16194 this.fireEvent("select", this, date, picker);
16195 Roo.menu.DateItem.superclass.handleClick.call(this);
16199 * Ext JS Library 1.1.1
16200 * Copyright(c) 2006-2007, Ext JS, LLC.
16202 * Originally Released Under LGPL - original licence link has changed is not relivant.
16205 * <script type="text/javascript">
16209 * @class Roo.menu.ColorItem
16210 * @extends Roo.menu.Adapter
16211 * A menu item that wraps the {@link Roo.ColorPalette} component.
16213 * Creates a new ColorItem
16214 * @param {Object} config Configuration options
16216 Roo.menu.ColorItem = function(config){
16217 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16218 /** The Roo.ColorPalette object @type Roo.ColorPalette */
16219 this.palette = this.component;
16220 this.relayEvents(this.palette, ["select"]);
16221 if(this.selectHandler){
16222 this.on('select', this.selectHandler, this.scope);
16225 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16227 * Ext JS Library 1.1.1
16228 * Copyright(c) 2006-2007, Ext JS, LLC.
16230 * Originally Released Under LGPL - original licence link has changed is not relivant.
16233 * <script type="text/javascript">
16238 * @class Roo.menu.DateMenu
16239 * @extends Roo.menu.Menu
16240 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16242 * Creates a new DateMenu
16243 * @param {Object} config Configuration options
16245 Roo.menu.DateMenu = function(config){
16246 Roo.menu.DateMenu.superclass.constructor.call(this, config);
16248 var di = new Roo.menu.DateItem(config);
16251 * The {@link Roo.DatePicker} instance for this DateMenu
16254 this.picker = di.picker;
16257 * @param {DatePicker} picker
16258 * @param {Date} date
16260 this.relayEvents(di, ["select"]);
16261 this.on('beforeshow', function(){
16263 this.picker.hideMonthPicker(false);
16267 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16271 * Ext JS Library 1.1.1
16272 * Copyright(c) 2006-2007, Ext JS, LLC.
16274 * Originally Released Under LGPL - original licence link has changed is not relivant.
16277 * <script type="text/javascript">
16282 * @class Roo.menu.ColorMenu
16283 * @extends Roo.menu.Menu
16284 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16286 * Creates a new ColorMenu
16287 * @param {Object} config Configuration options
16289 Roo.menu.ColorMenu = function(config){
16290 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16292 var ci = new Roo.menu.ColorItem(config);
16295 * The {@link Roo.ColorPalette} instance for this ColorMenu
16296 * @type ColorPalette
16298 this.palette = ci.palette;
16301 * @param {ColorPalette} palette
16302 * @param {String} color
16304 this.relayEvents(ci, ["select"]);
16306 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16308 * Ext JS Library 1.1.1
16309 * Copyright(c) 2006-2007, Ext JS, LLC.
16311 * Originally Released Under LGPL - original licence link has changed is not relivant.
16314 * <script type="text/javascript">
16318 * @class Roo.form.TextItem
16319 * @extends Roo.BoxComponent
16320 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16322 * Creates a new TextItem
16323 * @param {Object} config Configuration options
16325 Roo.form.TextItem = function(config){
16326 Roo.form.TextItem.superclass.constructor.call(this, config);
16329 Roo.extend(Roo.form.TextItem, Roo.BoxComponent, {
16332 * @cfg {String} tag the tag for this item (default div)
16336 * @cfg {String} html the content for this item
16340 getAutoCreate : function()
16353 onRender : function(ct, position)
16355 Roo.form.TextItem.superclass.onRender.call(this, ct, position);
16358 var cfg = this.getAutoCreate();
16360 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16362 if (!cfg.name.length) {
16365 this.el = ct.createChild(cfg, position);
16371 * Ext JS Library 1.1.1
16372 * Copyright(c) 2006-2007, Ext JS, LLC.
16374 * Originally Released Under LGPL - original licence link has changed is not relivant.
16377 * <script type="text/javascript">
16381 * @class Roo.form.Field
16382 * @extends Roo.BoxComponent
16383 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16385 * Creates a new Field
16386 * @param {Object} config Configuration options
16388 Roo.form.Field = function(config){
16389 Roo.form.Field.superclass.constructor.call(this, config);
16392 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
16394 * @cfg {String} fieldLabel Label to use when rendering a form.
16397 * @cfg {String} qtip Mouse over tip
16401 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16403 invalidClass : "x-form-invalid",
16405 * @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")
16407 invalidText : "The value in this field is invalid",
16409 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16411 focusClass : "x-form-focus",
16413 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16414 automatic validation (defaults to "keyup").
16416 validationEvent : "keyup",
16418 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16420 validateOnBlur : true,
16422 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16424 validationDelay : 250,
16426 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16427 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16429 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16431 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16433 fieldClass : "x-form-field",
16435 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
16438 ----------- ----------------------------------------------------------------------
16439 qtip Display a quick tip when the user hovers over the field
16440 title Display a default browser title attribute popup
16441 under Add a block div beneath the field containing the error text
16442 side Add an error icon to the right of the field with a popup on hover
16443 [element id] Add the error text directly to the innerHTML of the specified element
16446 msgTarget : 'qtip',
16448 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16453 * @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.
16458 * @cfg {Boolean} disabled True to disable the field (defaults to false).
16463 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16465 inputType : undefined,
16468 * @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).
16470 tabIndex : undefined,
16473 isFormField : true,
16478 * @property {Roo.Element} fieldEl
16479 * Element Containing the rendered Field (with label etc.)
16482 * @cfg {Mixed} value A value to initialize this field with.
16487 * @cfg {String} name The field's HTML name attribute.
16490 * @cfg {String} cls A CSS class to apply to the field's underlying element.
16493 loadedValue : false,
16497 initComponent : function(){
16498 Roo.form.Field.superclass.initComponent.call(this);
16502 * Fires when this field receives input focus.
16503 * @param {Roo.form.Field} this
16508 * Fires when this field loses input focus.
16509 * @param {Roo.form.Field} this
16513 * @event specialkey
16514 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
16515 * {@link Roo.EventObject#getKey} to determine which key was pressed.
16516 * @param {Roo.form.Field} this
16517 * @param {Roo.EventObject} e The event object
16522 * Fires just before the field blurs if the field value has changed.
16523 * @param {Roo.form.Field} this
16524 * @param {Mixed} newValue The new value
16525 * @param {Mixed} oldValue The original value
16530 * Fires after the field has been marked as invalid.
16531 * @param {Roo.form.Field} this
16532 * @param {String} msg The validation message
16537 * Fires after the field has been validated with no errors.
16538 * @param {Roo.form.Field} this
16543 * Fires after the key up
16544 * @param {Roo.form.Field} this
16545 * @param {Roo.EventObject} e The event Object
16552 * Returns the name attribute of the field if available
16553 * @return {String} name The field name
16555 getName: function(){
16556 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16560 onRender : function(ct, position){
16561 Roo.form.Field.superclass.onRender.call(this, ct, position);
16563 var cfg = this.getAutoCreate();
16565 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16567 if (!cfg.name.length) {
16570 if(this.inputType){
16571 cfg.type = this.inputType;
16573 this.el = ct.createChild(cfg, position);
16575 var type = this.el.dom.type;
16577 if(type == 'password'){
16580 this.el.addClass('x-form-'+type);
16583 this.el.dom.readOnly = true;
16585 if(this.tabIndex !== undefined){
16586 this.el.dom.setAttribute('tabIndex', this.tabIndex);
16589 this.el.addClass([this.fieldClass, this.cls]);
16594 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16595 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16596 * @return {Roo.form.Field} this
16598 applyTo : function(target){
16599 this.allowDomMove = false;
16600 this.el = Roo.get(target);
16601 this.render(this.el.dom.parentNode);
16606 initValue : function(){
16607 if(this.value !== undefined){
16608 this.setValue(this.value);
16609 }else if(this.el.dom.value.length > 0){
16610 this.setValue(this.el.dom.value);
16615 * Returns true if this field has been changed since it was originally loaded and is not disabled.
16616 * DEPRICATED - it never worked well - use hasChanged/resetHasChanged.
16618 isDirty : function() {
16619 if(this.disabled) {
16622 return String(this.getValue()) !== String(this.originalValue);
16626 * stores the current value in loadedValue
16628 resetHasChanged : function()
16630 this.loadedValue = String(this.getValue());
16633 * checks the current value against the 'loaded' value.
16634 * Note - will return false if 'resetHasChanged' has not been called first.
16636 hasChanged : function()
16638 if(this.disabled || this.readOnly) {
16641 return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16647 afterRender : function(){
16648 Roo.form.Field.superclass.afterRender.call(this);
16653 fireKey : function(e){
16654 //Roo.log('field ' + e.getKey());
16655 if(e.isNavKeyPress()){
16656 this.fireEvent("specialkey", this, e);
16661 * Resets the current field value to the originally loaded value and clears any validation messages
16663 reset : function(){
16664 this.setValue(this.resetValue);
16665 this.originalValue = this.getValue();
16666 this.clearInvalid();
16670 initEvents : function(){
16671 // safari killled keypress - so keydown is now used..
16672 this.el.on("keydown" , this.fireKey, this);
16673 this.el.on("focus", this.onFocus, this);
16674 this.el.on("blur", this.onBlur, this);
16675 this.el.relayEvent('keyup', this);
16677 // reference to original value for reset
16678 this.originalValue = this.getValue();
16679 this.resetValue = this.getValue();
16683 onFocus : function(){
16684 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16685 this.el.addClass(this.focusClass);
16687 if(!this.hasFocus){
16688 this.hasFocus = true;
16689 this.startValue = this.getValue();
16690 this.fireEvent("focus", this);
16694 beforeBlur : Roo.emptyFn,
16697 onBlur : function(){
16699 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16700 this.el.removeClass(this.focusClass);
16702 this.hasFocus = false;
16703 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16706 var v = this.getValue();
16707 if(String(v) !== String(this.startValue)){
16708 this.fireEvent('change', this, v, this.startValue);
16710 this.fireEvent("blur", this);
16714 * Returns whether or not the field value is currently valid
16715 * @param {Boolean} preventMark True to disable marking the field invalid
16716 * @return {Boolean} True if the value is valid, else false
16718 isValid : function(preventMark){
16722 var restore = this.preventMark;
16723 this.preventMark = preventMark === true;
16724 var v = this.validateValue(this.processValue(this.getRawValue()));
16725 this.preventMark = restore;
16730 * Validates the field value
16731 * @return {Boolean} True if the value is valid, else false
16733 validate : function(){
16734 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16735 this.clearInvalid();
16741 processValue : function(value){
16746 // Subclasses should provide the validation implementation by overriding this
16747 validateValue : function(value){
16752 * Mark this field as invalid
16753 * @param {String} msg The validation message
16755 markInvalid : function(msg){
16756 if(!this.rendered || this.preventMark){ // not rendered
16760 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16762 obj.el.addClass(this.invalidClass);
16763 msg = msg || this.invalidText;
16764 switch(this.msgTarget){
16766 obj.el.dom.qtip = msg;
16767 obj.el.dom.qclass = 'x-form-invalid-tip';
16768 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16769 Roo.QuickTips.enable();
16773 this.el.dom.title = msg;
16777 var elp = this.el.findParent('.x-form-element', 5, true);
16778 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16779 this.errorEl.setWidth(elp.getWidth(true)-20);
16781 this.errorEl.update(msg);
16782 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16785 if(!this.errorIcon){
16786 var elp = this.el.findParent('.x-form-element', 5, true);
16787 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16789 this.alignErrorIcon();
16790 this.errorIcon.dom.qtip = msg;
16791 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16792 this.errorIcon.show();
16793 this.on('resize', this.alignErrorIcon, this);
16796 var t = Roo.getDom(this.msgTarget);
16798 t.style.display = this.msgDisplay;
16801 this.fireEvent('invalid', this, msg);
16805 alignErrorIcon : function(){
16806 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16810 * Clear any invalid styles/messages for this field
16812 clearInvalid : function(){
16813 if(!this.rendered || this.preventMark){ // not rendered
16816 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16818 obj.el.removeClass(this.invalidClass);
16819 switch(this.msgTarget){
16821 obj.el.dom.qtip = '';
16824 this.el.dom.title = '';
16828 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16832 if(this.errorIcon){
16833 this.errorIcon.dom.qtip = '';
16834 this.errorIcon.hide();
16835 this.un('resize', this.alignErrorIcon, this);
16839 var t = Roo.getDom(this.msgTarget);
16841 t.style.display = 'none';
16844 this.fireEvent('valid', this);
16848 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
16849 * @return {Mixed} value The field value
16851 getRawValue : function(){
16852 var v = this.el.getValue();
16858 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16859 * @return {Mixed} value The field value
16861 getValue : function(){
16862 var v = this.el.getValue();
16868 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
16869 * @param {Mixed} value The value to set
16871 setRawValue : function(v){
16872 return this.el.dom.value = (v === null || v === undefined ? '' : v);
16876 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
16877 * @param {Mixed} value The value to set
16879 setValue : function(v){
16882 this.el.dom.value = (v === null || v === undefined ? '' : v);
16887 adjustSize : function(w, h){
16888 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16889 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16893 adjustWidth : function(tag, w){
16894 tag = tag.toLowerCase();
16895 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16896 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16897 if(tag == 'input'){
16900 if(tag == 'textarea'){
16903 }else if(Roo.isOpera){
16904 if(tag == 'input'){
16907 if(tag == 'textarea'){
16917 // anything other than normal should be considered experimental
16918 Roo.form.Field.msgFx = {
16920 show: function(msgEl, f){
16921 msgEl.setDisplayed('block');
16924 hide : function(msgEl, f){
16925 msgEl.setDisplayed(false).update('');
16930 show: function(msgEl, f){
16931 msgEl.slideIn('t', {stopFx:true});
16934 hide : function(msgEl, f){
16935 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16940 show: function(msgEl, f){
16941 msgEl.fixDisplay();
16942 msgEl.alignTo(f.el, 'tl-tr');
16943 msgEl.slideIn('l', {stopFx:true});
16946 hide : function(msgEl, f){
16947 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16952 * Ext JS Library 1.1.1
16953 * Copyright(c) 2006-2007, Ext JS, LLC.
16955 * Originally Released Under LGPL - original licence link has changed is not relivant.
16958 * <script type="text/javascript">
16963 * @class Roo.form.TextField
16964 * @extends Roo.form.Field
16965 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
16966 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16968 * Creates a new TextField
16969 * @param {Object} config Configuration options
16971 Roo.form.TextField = function(config){
16972 Roo.form.TextField.superclass.constructor.call(this, config);
16976 * Fires when the autosize function is triggered. The field may or may not have actually changed size
16977 * according to the default logic, but this event provides a hook for the developer to apply additional
16978 * logic at runtime to resize the field if needed.
16979 * @param {Roo.form.Field} this This text field
16980 * @param {Number} width The new field width
16986 Roo.extend(Roo.form.TextField, Roo.form.Field, {
16988 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16992 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16996 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
17000 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
17004 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
17008 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
17010 disableKeyFilter : false,
17012 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
17016 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
17020 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
17022 maxLength : Number.MAX_VALUE,
17024 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
17026 minLengthText : "The minimum length for this field is {0}",
17028 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
17030 maxLengthText : "The maximum length for this field is {0}",
17032 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
17034 selectOnFocus : false,
17036 * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space
17038 allowLeadingSpace : false,
17040 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
17042 blankText : "This field is required",
17044 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
17045 * If available, this function will be called only after the basic validators all return true, and will be passed the
17046 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
17050 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
17051 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
17052 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
17056 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
17060 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
17066 initEvents : function()
17068 if (this.emptyText) {
17069 this.el.attr('placeholder', this.emptyText);
17072 Roo.form.TextField.superclass.initEvents.call(this);
17073 if(this.validationEvent == 'keyup'){
17074 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
17075 this.el.on('keyup', this.filterValidation, this);
17077 else if(this.validationEvent !== false){
17078 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
17081 if(this.selectOnFocus){
17082 this.on("focus", this.preFocus, this);
17084 if (!this.allowLeadingSpace) {
17085 this.on('blur', this.cleanLeadingSpace, this);
17088 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
17089 this.el.on("keypress", this.filterKeys, this);
17092 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
17093 this.el.on("click", this.autoSize, this);
17095 if(this.el.is('input[type=password]') && Roo.isSafari){
17096 this.el.on('keydown', this.SafariOnKeyDown, this);
17100 processValue : function(value){
17101 if(this.stripCharsRe){
17102 var newValue = value.replace(this.stripCharsRe, '');
17103 if(newValue !== value){
17104 this.setRawValue(newValue);
17111 filterValidation : function(e){
17112 if(!e.isNavKeyPress()){
17113 this.validationTask.delay(this.validationDelay);
17118 onKeyUp : function(e){
17119 if(!e.isNavKeyPress()){
17123 // private - clean the leading white space
17124 cleanLeadingSpace : function(e)
17126 if ( this.inputType == 'file') {
17130 this.setValue((this.getValue() + '').replace(/^\s+/,''));
17133 * Resets the current field value to the originally-loaded value and clears any validation messages.
17136 reset : function(){
17137 Roo.form.TextField.superclass.reset.call(this);
17141 preFocus : function(){
17143 if(this.selectOnFocus){
17144 this.el.dom.select();
17150 filterKeys : function(e){
17151 var k = e.getKey();
17152 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17155 var c = e.getCharCode(), cc = String.fromCharCode(c);
17156 if(Roo.isIE && (e.isSpecialKey() || !cc)){
17159 if(!this.maskRe.test(cc)){
17164 setValue : function(v){
17166 Roo.form.TextField.superclass.setValue.apply(this, arguments);
17172 * Validates a value according to the field's validation rules and marks the field as invalid
17173 * if the validation fails
17174 * @param {Mixed} value The value to validate
17175 * @return {Boolean} True if the value is valid, else false
17177 validateValue : function(value){
17178 if(value.length < 1) { // if it's blank
17179 if(this.allowBlank){
17180 this.clearInvalid();
17183 this.markInvalid(this.blankText);
17187 if(value.length < this.minLength){
17188 this.markInvalid(String.format(this.minLengthText, this.minLength));
17191 if(value.length > this.maxLength){
17192 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17196 var vt = Roo.form.VTypes;
17197 if(!vt[this.vtype](value, this)){
17198 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17202 if(typeof this.validator == "function"){
17203 var msg = this.validator(value);
17205 this.markInvalid(msg);
17209 if(this.regex && !this.regex.test(value)){
17210 this.markInvalid(this.regexText);
17217 * Selects text in this field
17218 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17219 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17221 selectText : function(start, end){
17222 var v = this.getRawValue();
17224 start = start === undefined ? 0 : start;
17225 end = end === undefined ? v.length : end;
17226 var d = this.el.dom;
17227 if(d.setSelectionRange){
17228 d.setSelectionRange(start, end);
17229 }else if(d.createTextRange){
17230 var range = d.createTextRange();
17231 range.moveStart("character", start);
17232 range.moveEnd("character", v.length-end);
17239 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17240 * This only takes effect if grow = true, and fires the autosize event.
17242 autoSize : function(){
17243 if(!this.grow || !this.rendered){
17247 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17250 var v = el.dom.value;
17251 var d = document.createElement('div');
17252 d.appendChild(document.createTextNode(v));
17256 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17257 this.el.setWidth(w);
17258 this.fireEvent("autosize", this, w);
17262 SafariOnKeyDown : function(event)
17264 // this is a workaround for a password hang bug on chrome/ webkit.
17266 var isSelectAll = false;
17268 if(this.el.dom.selectionEnd > 0){
17269 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17271 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17272 event.preventDefault();
17277 if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17279 event.preventDefault();
17280 // this is very hacky as keydown always get's upper case.
17282 var cc = String.fromCharCode(event.getCharCode());
17285 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
17293 * Ext JS Library 1.1.1
17294 * Copyright(c) 2006-2007, Ext JS, LLC.
17296 * Originally Released Under LGPL - original licence link has changed is not relivant.
17299 * <script type="text/javascript">
17303 * @class Roo.form.Hidden
17304 * @extends Roo.form.TextField
17305 * Simple Hidden element used on forms
17307 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17310 * Creates a new Hidden form element.
17311 * @param {Object} config Configuration options
17316 // easy hidden field...
17317 Roo.form.Hidden = function(config){
17318 Roo.form.Hidden.superclass.constructor.call(this, config);
17321 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17323 inputType: 'hidden',
17326 labelSeparator: '',
17328 itemCls : 'x-form-item-display-none'
17336 * Ext JS Library 1.1.1
17337 * Copyright(c) 2006-2007, Ext JS, LLC.
17339 * Originally Released Under LGPL - original licence link has changed is not relivant.
17342 * <script type="text/javascript">
17346 * @class Roo.form.TriggerField
17347 * @extends Roo.form.TextField
17348 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17349 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17350 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17351 * for which you can provide a custom implementation. For example:
17353 var trigger = new Roo.form.TriggerField();
17354 trigger.onTriggerClick = myTriggerFn;
17355 trigger.applyTo('my-field');
17358 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17359 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17360 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
17361 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17363 * Create a new TriggerField.
17364 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17365 * to the base TextField)
17367 Roo.form.TriggerField = function(config){
17368 this.mimicing = false;
17369 Roo.form.TriggerField.superclass.constructor.call(this, config);
17372 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
17374 * @cfg {String} triggerClass A CSS class to apply to the trigger
17377 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17378 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17380 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17382 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17386 /** @cfg {Boolean} grow @hide */
17387 /** @cfg {Number} growMin @hide */
17388 /** @cfg {Number} growMax @hide */
17394 autoSize: Roo.emptyFn,
17398 deferHeight : true,
17401 actionMode : 'wrap',
17403 onResize : function(w, h){
17404 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17405 if(typeof w == 'number'){
17406 var x = w - this.trigger.getWidth();
17407 this.el.setWidth(this.adjustWidth('input', x));
17408 this.trigger.setStyle('left', x+'px');
17413 adjustSize : Roo.BoxComponent.prototype.adjustSize,
17416 getResizeEl : function(){
17421 getPositionEl : function(){
17426 alignErrorIcon : function(){
17427 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17431 onRender : function(ct, position){
17432 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17433 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17434 this.trigger = this.wrap.createChild(this.triggerConfig ||
17435 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17436 if(this.hideTrigger){
17437 this.trigger.setDisplayed(false);
17439 this.initTrigger();
17441 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17446 initTrigger : function(){
17447 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17448 this.trigger.addClassOnOver('x-form-trigger-over');
17449 this.trigger.addClassOnClick('x-form-trigger-click');
17453 onDestroy : function(){
17455 this.trigger.removeAllListeners();
17456 this.trigger.remove();
17459 this.wrap.remove();
17461 Roo.form.TriggerField.superclass.onDestroy.call(this);
17465 onFocus : function(){
17466 Roo.form.TriggerField.superclass.onFocus.call(this);
17467 if(!this.mimicing){
17468 this.wrap.addClass('x-trigger-wrap-focus');
17469 this.mimicing = true;
17470 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17471 if(this.monitorTab){
17472 this.el.on("keydown", this.checkTab, this);
17478 checkTab : function(e){
17479 if(e.getKey() == e.TAB){
17480 this.triggerBlur();
17485 onBlur : function(){
17490 mimicBlur : function(e, t){
17491 if(!this.wrap.contains(t) && this.validateBlur()){
17492 this.triggerBlur();
17497 triggerBlur : function(){
17498 this.mimicing = false;
17499 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17500 if(this.monitorTab){
17501 this.el.un("keydown", this.checkTab, this);
17503 this.wrap.removeClass('x-trigger-wrap-focus');
17504 Roo.form.TriggerField.superclass.onBlur.call(this);
17508 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17509 validateBlur : function(e, t){
17514 onDisable : function(){
17515 Roo.form.TriggerField.superclass.onDisable.call(this);
17517 this.wrap.addClass('x-item-disabled');
17522 onEnable : function(){
17523 Roo.form.TriggerField.superclass.onEnable.call(this);
17525 this.wrap.removeClass('x-item-disabled');
17530 onShow : function(){
17531 var ae = this.getActionEl();
17534 ae.dom.style.display = '';
17535 ae.dom.style.visibility = 'visible';
17541 onHide : function(){
17542 var ae = this.getActionEl();
17543 ae.dom.style.display = 'none';
17547 * The function that should handle the trigger's click event. This method does nothing by default until overridden
17548 * by an implementing function.
17550 * @param {EventObject} e
17552 onTriggerClick : Roo.emptyFn
17555 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
17556 // to be extended by an implementing class. For an example of implementing this class, see the custom
17557 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17558 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17559 initComponent : function(){
17560 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17562 this.triggerConfig = {
17563 tag:'span', cls:'x-form-twin-triggers', cn:[
17564 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17565 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17569 getTrigger : function(index){
17570 return this.triggers[index];
17573 initTrigger : function(){
17574 var ts = this.trigger.select('.x-form-trigger', true);
17575 this.wrap.setStyle('overflow', 'hidden');
17576 var triggerField = this;
17577 ts.each(function(t, all, index){
17578 t.hide = function(){
17579 var w = triggerField.wrap.getWidth();
17580 this.dom.style.display = 'none';
17581 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17583 t.show = function(){
17584 var w = triggerField.wrap.getWidth();
17585 this.dom.style.display = '';
17586 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17588 var triggerIndex = 'Trigger'+(index+1);
17590 if(this['hide'+triggerIndex]){
17591 t.dom.style.display = 'none';
17593 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17594 t.addClassOnOver('x-form-trigger-over');
17595 t.addClassOnClick('x-form-trigger-click');
17597 this.triggers = ts.elements;
17600 onTrigger1Click : Roo.emptyFn,
17601 onTrigger2Click : Roo.emptyFn
17604 * Ext JS Library 1.1.1
17605 * Copyright(c) 2006-2007, Ext JS, LLC.
17607 * Originally Released Under LGPL - original licence link has changed is not relivant.
17610 * <script type="text/javascript">
17614 * @class Roo.form.TextArea
17615 * @extends Roo.form.TextField
17616 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
17617 * support for auto-sizing.
17619 * Creates a new TextArea
17620 * @param {Object} config Configuration options
17622 Roo.form.TextArea = function(config){
17623 Roo.form.TextArea.superclass.constructor.call(this, config);
17624 // these are provided exchanges for backwards compat
17625 // minHeight/maxHeight were replaced by growMin/growMax to be
17626 // compatible with TextField growing config values
17627 if(this.minHeight !== undefined){
17628 this.growMin = this.minHeight;
17630 if(this.maxHeight !== undefined){
17631 this.growMax = this.maxHeight;
17635 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
17637 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17641 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17645 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17646 * in the field (equivalent to setting overflow: hidden, defaults to false)
17648 preventScrollbars: false,
17650 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17651 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17655 onRender : function(ct, position){
17657 this.defaultAutoCreate = {
17659 style:"width:300px;height:60px;",
17660 autocomplete: "new-password"
17663 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17665 this.textSizeEl = Roo.DomHelper.append(document.body, {
17666 tag: "pre", cls: "x-form-grow-sizer"
17668 if(this.preventScrollbars){
17669 this.el.setStyle("overflow", "hidden");
17671 this.el.setHeight(this.growMin);
17675 onDestroy : function(){
17676 if(this.textSizeEl){
17677 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17679 Roo.form.TextArea.superclass.onDestroy.call(this);
17683 onKeyUp : function(e){
17684 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17690 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17691 * This only takes effect if grow = true, and fires the autosize event if the height changes.
17693 autoSize : function(){
17694 if(!this.grow || !this.textSizeEl){
17698 var v = el.dom.value;
17699 var ts = this.textSizeEl;
17702 ts.appendChild(document.createTextNode(v));
17705 Roo.fly(ts).setWidth(this.el.getWidth());
17707 v = "  ";
17710 v = v.replace(/\n/g, '<p> </p>');
17712 v += " \n ";
17715 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17716 if(h != this.lastHeight){
17717 this.lastHeight = h;
17718 this.el.setHeight(h);
17719 this.fireEvent("autosize", this, h);
17724 * Ext JS Library 1.1.1
17725 * Copyright(c) 2006-2007, Ext JS, LLC.
17727 * Originally Released Under LGPL - original licence link has changed is not relivant.
17730 * <script type="text/javascript">
17735 * @class Roo.form.NumberField
17736 * @extends Roo.form.TextField
17737 * Numeric text field that provides automatic keystroke filtering and numeric validation.
17739 * Creates a new NumberField
17740 * @param {Object} config Configuration options
17742 Roo.form.NumberField = function(config){
17743 Roo.form.NumberField.superclass.constructor.call(this, config);
17746 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
17748 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17750 fieldClass: "x-form-field x-form-num-field",
17752 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17754 allowDecimals : true,
17756 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17758 decimalSeparator : ".",
17760 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17762 decimalPrecision : 2,
17764 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17766 allowNegative : true,
17768 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17770 minValue : Number.NEGATIVE_INFINITY,
17772 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17774 maxValue : Number.MAX_VALUE,
17776 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17778 minText : "The minimum value for this field is {0}",
17780 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17782 maxText : "The maximum value for this field is {0}",
17784 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
17785 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17787 nanText : "{0} is not a valid number",
17790 initEvents : function(){
17791 Roo.form.NumberField.superclass.initEvents.call(this);
17792 var allowed = "0123456789";
17793 if(this.allowDecimals){
17794 allowed += this.decimalSeparator;
17796 if(this.allowNegative){
17799 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17800 var keyPress = function(e){
17801 var k = e.getKey();
17802 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17805 var c = e.getCharCode();
17806 if(allowed.indexOf(String.fromCharCode(c)) === -1){
17810 this.el.on("keypress", keyPress, this);
17814 validateValue : function(value){
17815 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17818 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17821 var num = this.parseValue(value);
17823 this.markInvalid(String.format(this.nanText, value));
17826 if(num < this.minValue){
17827 this.markInvalid(String.format(this.minText, this.minValue));
17830 if(num > this.maxValue){
17831 this.markInvalid(String.format(this.maxText, this.maxValue));
17837 getValue : function(){
17838 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17842 parseValue : function(value){
17843 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17844 return isNaN(value) ? '' : value;
17848 fixPrecision : function(value){
17849 var nan = isNaN(value);
17850 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17851 return nan ? '' : value;
17853 return parseFloat(value).toFixed(this.decimalPrecision);
17856 setValue : function(v){
17857 v = this.fixPrecision(v);
17858 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17862 decimalPrecisionFcn : function(v){
17863 return Math.floor(v);
17866 beforeBlur : function(){
17867 var v = this.parseValue(this.getRawValue());
17874 * Ext JS Library 1.1.1
17875 * Copyright(c) 2006-2007, Ext JS, LLC.
17877 * Originally Released Under LGPL - original licence link has changed is not relivant.
17880 * <script type="text/javascript">
17884 * @class Roo.form.DateField
17885 * @extends Roo.form.TriggerField
17886 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17888 * Create a new DateField
17889 * @param {Object} config
17891 Roo.form.DateField = function(config)
17893 Roo.form.DateField.superclass.constructor.call(this, config);
17899 * Fires when a date is selected
17900 * @param {Roo.form.DateField} combo This combo box
17901 * @param {Date} date The date selected
17908 if(typeof this.minValue == "string") {
17909 this.minValue = this.parseDate(this.minValue);
17911 if(typeof this.maxValue == "string") {
17912 this.maxValue = this.parseDate(this.maxValue);
17914 this.ddMatch = null;
17915 if(this.disabledDates){
17916 var dd = this.disabledDates;
17918 for(var i = 0; i < dd.length; i++){
17920 if(i != dd.length-1) {
17924 this.ddMatch = new RegExp(re + ")");
17928 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
17930 * @cfg {String} format
17931 * The default date format string which can be overriden for localization support. The format must be
17932 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17936 * @cfg {String} altFormats
17937 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17938 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17940 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17942 * @cfg {Array} disabledDays
17943 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17945 disabledDays : null,
17947 * @cfg {String} disabledDaysText
17948 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17950 disabledDaysText : "Disabled",
17952 * @cfg {Array} disabledDates
17953 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17954 * expression so they are very powerful. Some examples:
17956 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17957 * <li>["03/08", "09/16"] would disable those days for every year</li>
17958 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17959 * <li>["03/../2006"] would disable every day in March 2006</li>
17960 * <li>["^03"] would disable every day in every March</li>
17962 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17963 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17965 disabledDates : null,
17967 * @cfg {String} disabledDatesText
17968 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17970 disabledDatesText : "Disabled",
17972 * @cfg {Date/String} minValue
17973 * The minimum allowed date. Can be either a Javascript date object or a string date in a
17974 * valid format (defaults to null).
17978 * @cfg {Date/String} maxValue
17979 * The maximum allowed date. Can be either a Javascript date object or a string date in a
17980 * valid format (defaults to null).
17984 * @cfg {String} minText
17985 * The error text to display when the date in the cell is before minValue (defaults to
17986 * 'The date in this field must be after {minValue}').
17988 minText : "The date in this field must be equal to or after {0}",
17990 * @cfg {String} maxText
17991 * The error text to display when the date in the cell is after maxValue (defaults to
17992 * 'The date in this field must be before {maxValue}').
17994 maxText : "The date in this field must be equal to or before {0}",
17996 * @cfg {String} invalidText
17997 * The error text to display when the date in the field is invalid (defaults to
17998 * '{value} is not a valid date - it must be in the format {format}').
18000 invalidText : "{0} is not a valid date - it must be in the format {1}",
18002 * @cfg {String} triggerClass
18003 * An additional CSS class used to style the trigger button. The trigger will always get the
18004 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18005 * which displays a calendar icon).
18007 triggerClass : 'x-form-date-trigger',
18011 * @cfg {Boolean} useIso
18012 * if enabled, then the date field will use a hidden field to store the
18013 * real value as iso formated date. default (false)
18017 * @cfg {String/Object} autoCreate
18018 * A DomHelper element spec, or true for a default element spec (defaults to
18019 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18022 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
18025 hiddenField: false,
18027 onRender : function(ct, position)
18029 Roo.form.DateField.superclass.onRender.call(this, ct, position);
18031 //this.el.dom.removeAttribute('name');
18032 Roo.log("Changing name?");
18033 this.el.dom.setAttribute('name', this.name + '____hidden___' );
18034 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18036 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18037 // prevent input submission
18038 this.hiddenName = this.name;
18045 validateValue : function(value)
18047 value = this.formatDate(value);
18048 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
18049 Roo.log('super failed');
18052 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18055 var svalue = value;
18056 value = this.parseDate(value);
18058 Roo.log('parse date failed' + svalue);
18059 this.markInvalid(String.format(this.invalidText, svalue, this.format));
18062 var time = value.getTime();
18063 if(this.minValue && time < this.minValue.getTime()){
18064 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18067 if(this.maxValue && time > this.maxValue.getTime()){
18068 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18071 if(this.disabledDays){
18072 var day = value.getDay();
18073 for(var i = 0; i < this.disabledDays.length; i++) {
18074 if(day === this.disabledDays[i]){
18075 this.markInvalid(this.disabledDaysText);
18080 var fvalue = this.formatDate(value);
18081 if(this.ddMatch && this.ddMatch.test(fvalue)){
18082 this.markInvalid(String.format(this.disabledDatesText, fvalue));
18089 // Provides logic to override the default TriggerField.validateBlur which just returns true
18090 validateBlur : function(){
18091 return !this.menu || !this.menu.isVisible();
18094 getName: function()
18096 // returns hidden if it's set..
18097 if (!this.rendered) {return ''};
18098 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
18103 * Returns the current date value of the date field.
18104 * @return {Date} The date value
18106 getValue : function(){
18108 return this.hiddenField ?
18109 this.hiddenField.value :
18110 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
18114 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
18115 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
18116 * (the default format used is "m/d/y").
18119 //All of these calls set the same date value (May 4, 2006)
18121 //Pass a date object:
18122 var dt = new Date('5/4/06');
18123 dateField.setValue(dt);
18125 //Pass a date string (default format):
18126 dateField.setValue('5/4/06');
18128 //Pass a date string (custom format):
18129 dateField.format = 'Y-m-d';
18130 dateField.setValue('2006-5-4');
18132 * @param {String/Date} date The date or valid date string
18134 setValue : function(date){
18135 if (this.hiddenField) {
18136 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18138 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18139 // make sure the value field is always stored as a date..
18140 this.value = this.parseDate(date);
18146 parseDate : function(value){
18147 if(!value || value instanceof Date){
18150 var v = Date.parseDate(value, this.format);
18151 if (!v && this.useIso) {
18152 v = Date.parseDate(value, 'Y-m-d');
18154 if(!v && this.altFormats){
18155 if(!this.altFormatsArray){
18156 this.altFormatsArray = this.altFormats.split("|");
18158 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18159 v = Date.parseDate(value, this.altFormatsArray[i]);
18166 formatDate : function(date, fmt){
18167 return (!date || !(date instanceof Date)) ?
18168 date : date.dateFormat(fmt || this.format);
18173 select: function(m, d){
18176 this.fireEvent('select', this, d);
18178 show : function(){ // retain focus styling
18182 this.focus.defer(10, this);
18183 var ml = this.menuListeners;
18184 this.menu.un("select", ml.select, this);
18185 this.menu.un("show", ml.show, this);
18186 this.menu.un("hide", ml.hide, this);
18191 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18192 onTriggerClick : function(){
18196 if(this.menu == null){
18197 this.menu = new Roo.menu.DateMenu();
18199 Roo.apply(this.menu.picker, {
18200 showClear: this.allowBlank,
18201 minDate : this.minValue,
18202 maxDate : this.maxValue,
18203 disabledDatesRE : this.ddMatch,
18204 disabledDatesText : this.disabledDatesText,
18205 disabledDays : this.disabledDays,
18206 disabledDaysText : this.disabledDaysText,
18207 format : this.useIso ? 'Y-m-d' : this.format,
18208 minText : String.format(this.minText, this.formatDate(this.minValue)),
18209 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18211 this.menu.on(Roo.apply({}, this.menuListeners, {
18214 this.menu.picker.setValue(this.getValue() || new Date());
18215 this.menu.show(this.el, "tl-bl?");
18218 beforeBlur : function(){
18219 var v = this.parseDate(this.getRawValue());
18229 isDirty : function() {
18230 if(this.disabled) {
18234 if(typeof(this.startValue) === 'undefined'){
18238 return String(this.getValue()) !== String(this.startValue);
18242 cleanLeadingSpace : function(e)
18249 * Ext JS Library 1.1.1
18250 * Copyright(c) 2006-2007, Ext JS, LLC.
18252 * Originally Released Under LGPL - original licence link has changed is not relivant.
18255 * <script type="text/javascript">
18259 * @class Roo.form.MonthField
18260 * @extends Roo.form.TriggerField
18261 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18263 * Create a new MonthField
18264 * @param {Object} config
18266 Roo.form.MonthField = function(config){
18268 Roo.form.MonthField.superclass.constructor.call(this, config);
18274 * Fires when a date is selected
18275 * @param {Roo.form.MonthFieeld} combo This combo box
18276 * @param {Date} date The date selected
18283 if(typeof this.minValue == "string") {
18284 this.minValue = this.parseDate(this.minValue);
18286 if(typeof this.maxValue == "string") {
18287 this.maxValue = this.parseDate(this.maxValue);
18289 this.ddMatch = null;
18290 if(this.disabledDates){
18291 var dd = this.disabledDates;
18293 for(var i = 0; i < dd.length; i++){
18295 if(i != dd.length-1) {
18299 this.ddMatch = new RegExp(re + ")");
18303 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
18305 * @cfg {String} format
18306 * The default date format string which can be overriden for localization support. The format must be
18307 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18311 * @cfg {String} altFormats
18312 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18313 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18315 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18317 * @cfg {Array} disabledDays
18318 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18320 disabledDays : [0,1,2,3,4,5,6],
18322 * @cfg {String} disabledDaysText
18323 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18325 disabledDaysText : "Disabled",
18327 * @cfg {Array} disabledDates
18328 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18329 * expression so they are very powerful. Some examples:
18331 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18332 * <li>["03/08", "09/16"] would disable those days for every year</li>
18333 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18334 * <li>["03/../2006"] would disable every day in March 2006</li>
18335 * <li>["^03"] would disable every day in every March</li>
18337 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18338 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18340 disabledDates : null,
18342 * @cfg {String} disabledDatesText
18343 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18345 disabledDatesText : "Disabled",
18347 * @cfg {Date/String} minValue
18348 * The minimum allowed date. Can be either a Javascript date object or a string date in a
18349 * valid format (defaults to null).
18353 * @cfg {Date/String} maxValue
18354 * The maximum allowed date. Can be either a Javascript date object or a string date in a
18355 * valid format (defaults to null).
18359 * @cfg {String} minText
18360 * The error text to display when the date in the cell is before minValue (defaults to
18361 * 'The date in this field must be after {minValue}').
18363 minText : "The date in this field must be equal to or after {0}",
18365 * @cfg {String} maxTextf
18366 * The error text to display when the date in the cell is after maxValue (defaults to
18367 * 'The date in this field must be before {maxValue}').
18369 maxText : "The date in this field must be equal to or before {0}",
18371 * @cfg {String} invalidText
18372 * The error text to display when the date in the field is invalid (defaults to
18373 * '{value} is not a valid date - it must be in the format {format}').
18375 invalidText : "{0} is not a valid date - it must be in the format {1}",
18377 * @cfg {String} triggerClass
18378 * An additional CSS class used to style the trigger button. The trigger will always get the
18379 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18380 * which displays a calendar icon).
18382 triggerClass : 'x-form-date-trigger',
18386 * @cfg {Boolean} useIso
18387 * if enabled, then the date field will use a hidden field to store the
18388 * real value as iso formated date. default (true)
18392 * @cfg {String/Object} autoCreate
18393 * A DomHelper element spec, or true for a default element spec (defaults to
18394 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18397 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18400 hiddenField: false,
18402 hideMonthPicker : false,
18404 onRender : function(ct, position)
18406 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18408 this.el.dom.removeAttribute('name');
18409 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18411 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18412 // prevent input submission
18413 this.hiddenName = this.name;
18420 validateValue : function(value)
18422 value = this.formatDate(value);
18423 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18426 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18429 var svalue = value;
18430 value = this.parseDate(value);
18432 this.markInvalid(String.format(this.invalidText, svalue, this.format));
18435 var time = value.getTime();
18436 if(this.minValue && time < this.minValue.getTime()){
18437 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18440 if(this.maxValue && time > this.maxValue.getTime()){
18441 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18444 /*if(this.disabledDays){
18445 var day = value.getDay();
18446 for(var i = 0; i < this.disabledDays.length; i++) {
18447 if(day === this.disabledDays[i]){
18448 this.markInvalid(this.disabledDaysText);
18454 var fvalue = this.formatDate(value);
18455 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18456 this.markInvalid(String.format(this.disabledDatesText, fvalue));
18464 // Provides logic to override the default TriggerField.validateBlur which just returns true
18465 validateBlur : function(){
18466 return !this.menu || !this.menu.isVisible();
18470 * Returns the current date value of the date field.
18471 * @return {Date} The date value
18473 getValue : function(){
18477 return this.hiddenField ?
18478 this.hiddenField.value :
18479 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18483 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
18484 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18485 * (the default format used is "m/d/y").
18488 //All of these calls set the same date value (May 4, 2006)
18490 //Pass a date object:
18491 var dt = new Date('5/4/06');
18492 monthField.setValue(dt);
18494 //Pass a date string (default format):
18495 monthField.setValue('5/4/06');
18497 //Pass a date string (custom format):
18498 monthField.format = 'Y-m-d';
18499 monthField.setValue('2006-5-4');
18501 * @param {String/Date} date The date or valid date string
18503 setValue : function(date){
18504 Roo.log('month setValue' + date);
18505 // can only be first of month..
18507 var val = this.parseDate(date);
18509 if (this.hiddenField) {
18510 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18512 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18513 this.value = this.parseDate(date);
18517 parseDate : function(value){
18518 if(!value || value instanceof Date){
18519 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18522 var v = Date.parseDate(value, this.format);
18523 if (!v && this.useIso) {
18524 v = Date.parseDate(value, 'Y-m-d');
18528 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18532 if(!v && this.altFormats){
18533 if(!this.altFormatsArray){
18534 this.altFormatsArray = this.altFormats.split("|");
18536 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18537 v = Date.parseDate(value, this.altFormatsArray[i]);
18544 formatDate : function(date, fmt){
18545 return (!date || !(date instanceof Date)) ?
18546 date : date.dateFormat(fmt || this.format);
18551 select: function(m, d){
18553 this.fireEvent('select', this, d);
18555 show : function(){ // retain focus styling
18559 this.focus.defer(10, this);
18560 var ml = this.menuListeners;
18561 this.menu.un("select", ml.select, this);
18562 this.menu.un("show", ml.show, this);
18563 this.menu.un("hide", ml.hide, this);
18567 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18568 onTriggerClick : function(){
18572 if(this.menu == null){
18573 this.menu = new Roo.menu.DateMenu();
18577 Roo.apply(this.menu.picker, {
18579 showClear: this.allowBlank,
18580 minDate : this.minValue,
18581 maxDate : this.maxValue,
18582 disabledDatesRE : this.ddMatch,
18583 disabledDatesText : this.disabledDatesText,
18585 format : this.useIso ? 'Y-m-d' : this.format,
18586 minText : String.format(this.minText, this.formatDate(this.minValue)),
18587 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18590 this.menu.on(Roo.apply({}, this.menuListeners, {
18598 // hide month picker get's called when we called by 'before hide';
18600 var ignorehide = true;
18601 p.hideMonthPicker = function(disableAnim){
18605 if(this.monthPicker){
18606 Roo.log("hideMonthPicker called");
18607 if(disableAnim === true){
18608 this.monthPicker.hide();
18610 this.monthPicker.slideOut('t', {duration:.2});
18611 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18612 p.fireEvent("select", this, this.value);
18618 Roo.log('picker set value');
18619 Roo.log(this.getValue());
18620 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18621 m.show(this.el, 'tl-bl?');
18622 ignorehide = false;
18623 // this will trigger hideMonthPicker..
18626 // hidden the day picker
18627 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18633 p.showMonthPicker.defer(100, p);
18639 beforeBlur : function(){
18640 var v = this.parseDate(this.getRawValue());
18646 /** @cfg {Boolean} grow @hide */
18647 /** @cfg {Number} growMin @hide */
18648 /** @cfg {Number} growMax @hide */
18655 * Ext JS Library 1.1.1
18656 * Copyright(c) 2006-2007, Ext JS, LLC.
18658 * Originally Released Under LGPL - original licence link has changed is not relivant.
18661 * <script type="text/javascript">
18666 * @class Roo.form.ComboBox
18667 * @extends Roo.form.TriggerField
18668 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18670 * Create a new ComboBox.
18671 * @param {Object} config Configuration options
18673 Roo.form.ComboBox = function(config){
18674 Roo.form.ComboBox.superclass.constructor.call(this, config);
18678 * Fires when the dropdown list is expanded
18679 * @param {Roo.form.ComboBox} combo This combo box
18684 * Fires when the dropdown list is collapsed
18685 * @param {Roo.form.ComboBox} combo This combo box
18689 * @event beforeselect
18690 * Fires before a list item is selected. Return false to cancel the selection.
18691 * @param {Roo.form.ComboBox} combo This combo box
18692 * @param {Roo.data.Record} record The data record returned from the underlying store
18693 * @param {Number} index The index of the selected item in the dropdown list
18695 'beforeselect' : true,
18698 * Fires when a list item is selected
18699 * @param {Roo.form.ComboBox} combo This combo box
18700 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18701 * @param {Number} index The index of the selected item in the dropdown list
18705 * @event beforequery
18706 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18707 * The event object passed has these properties:
18708 * @param {Roo.form.ComboBox} combo This combo box
18709 * @param {String} query The query
18710 * @param {Boolean} forceAll true to force "all" query
18711 * @param {Boolean} cancel true to cancel the query
18712 * @param {Object} e The query event object
18714 'beforequery': true,
18717 * Fires when the 'add' icon is pressed (add a listener to enable add button)
18718 * @param {Roo.form.ComboBox} combo This combo box
18723 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18724 * @param {Roo.form.ComboBox} combo This combo box
18725 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18731 if(this.transform){
18732 this.allowDomMove = false;
18733 var s = Roo.getDom(this.transform);
18734 if(!this.hiddenName){
18735 this.hiddenName = s.name;
18738 this.mode = 'local';
18739 var d = [], opts = s.options;
18740 for(var i = 0, len = opts.length;i < len; i++){
18742 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18744 this.value = value;
18746 d.push([value, o.text]);
18748 this.store = new Roo.data.SimpleStore({
18750 fields: ['value', 'text'],
18753 this.valueField = 'value';
18754 this.displayField = 'text';
18756 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18757 if(!this.lazyRender){
18758 this.target = true;
18759 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18760 s.parentNode.removeChild(s); // remove it
18761 this.render(this.el.parentNode);
18763 s.parentNode.removeChild(s); // remove it
18768 this.store = Roo.factory(this.store, Roo.data);
18771 this.selectedIndex = -1;
18772 if(this.mode == 'local'){
18773 if(config.queryDelay === undefined){
18774 this.queryDelay = 10;
18776 if(config.minChars === undefined){
18782 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18784 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18787 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18788 * rendering into an Roo.Editor, defaults to false)
18791 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18792 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18795 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18798 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18799 * the dropdown list (defaults to undefined, with no header element)
18803 * @cfg {String/Roo.Template} tpl The template to use to render the output
18807 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18809 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18811 listWidth: undefined,
18813 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18814 * mode = 'remote' or 'text' if mode = 'local')
18816 displayField: undefined,
18818 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18819 * mode = 'remote' or 'value' if mode = 'local').
18820 * Note: use of a valueField requires the user make a selection
18821 * in order for a value to be mapped.
18823 valueField: undefined,
18827 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18828 * field's data value (defaults to the underlying DOM element's name)
18830 hiddenName: undefined,
18832 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18836 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18838 selectedClass: 'x-combo-selected',
18840 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
18841 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18842 * which displays a downward arrow icon).
18844 triggerClass : 'x-form-arrow-trigger',
18846 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18850 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18851 * anchor positions (defaults to 'tl-bl')
18853 listAlign: 'tl-bl?',
18855 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18859 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
18860 * query specified by the allQuery config option (defaults to 'query')
18862 triggerAction: 'query',
18864 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18865 * (defaults to 4, does not apply if editable = false)
18869 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18870 * delay (typeAheadDelay) if it matches a known value (defaults to false)
18874 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18875 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18879 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18880 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
18884 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
18885 * when editable = true (defaults to false)
18887 selectOnFocus:false,
18889 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18891 queryParam: 'query',
18893 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
18894 * when mode = 'remote' (defaults to 'Loading...')
18896 loadingText: 'Loading...',
18898 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18902 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18906 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18907 * traditional select (defaults to true)
18911 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18915 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18919 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18920 * listWidth has a higher value)
18924 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18925 * allow the user to set arbitrary text into the field (defaults to false)
18927 forceSelection:false,
18929 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18930 * if typeAhead = true (defaults to 250)
18932 typeAheadDelay : 250,
18934 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18935 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18937 valueNotFoundText : undefined,
18939 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18941 blockFocus : false,
18944 * @cfg {Boolean} disableClear Disable showing of clear button.
18946 disableClear : false,
18948 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
18950 alwaysQuery : false,
18956 // element that contains real text value.. (when hidden is used..)
18959 onRender : function(ct, position)
18961 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18963 if(this.hiddenName){
18964 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
18966 this.hiddenField.value =
18967 this.hiddenValue !== undefined ? this.hiddenValue :
18968 this.value !== undefined ? this.value : '';
18970 // prevent input submission
18971 this.el.dom.removeAttribute('name');
18977 this.el.dom.setAttribute('autocomplete', 'off');
18980 var cls = 'x-combo-list';
18982 this.list = new Roo.Layer({
18983 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18986 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18987 this.list.setWidth(lw);
18988 this.list.swallowEvent('mousewheel');
18989 this.assetHeight = 0;
18992 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18993 this.assetHeight += this.header.getHeight();
18996 this.innerList = this.list.createChild({cls:cls+'-inner'});
18997 this.innerList.on('mouseover', this.onViewOver, this);
18998 this.innerList.on('mousemove', this.onViewMove, this);
18999 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19001 if(this.allowBlank && !this.pageSize && !this.disableClear){
19002 this.footer = this.list.createChild({cls:cls+'-ft'});
19003 this.pageTb = new Roo.Toolbar(this.footer);
19007 this.footer = this.list.createChild({cls:cls+'-ft'});
19008 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
19009 {pageSize: this.pageSize});
19013 if (this.pageTb && this.allowBlank && !this.disableClear) {
19015 this.pageTb.add(new Roo.Toolbar.Fill(), {
19016 cls: 'x-btn-icon x-btn-clear',
19018 handler: function()
19021 _this.clearValue();
19022 _this.onSelect(false, -1);
19027 this.assetHeight += this.footer.getHeight();
19032 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
19035 this.view = new Roo.View(this.innerList, this.tpl, {
19038 selectedClass: this.selectedClass
19041 this.view.on('click', this.onViewClick, this);
19043 this.store.on('beforeload', this.onBeforeLoad, this);
19044 this.store.on('load', this.onLoad, this);
19045 this.store.on('loadexception', this.onLoadException, this);
19047 if(this.resizable){
19048 this.resizer = new Roo.Resizable(this.list, {
19049 pinned:true, handles:'se'
19051 this.resizer.on('resize', function(r, w, h){
19052 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
19053 this.listWidth = w;
19054 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
19055 this.restrictHeight();
19057 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
19059 if(!this.editable){
19060 this.editable = true;
19061 this.setEditable(false);
19065 if (typeof(this.events.add.listeners) != 'undefined') {
19067 this.addicon = this.wrap.createChild(
19068 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
19070 this.addicon.on('click', function(e) {
19071 this.fireEvent('add', this);
19074 if (typeof(this.events.edit.listeners) != 'undefined') {
19076 this.editicon = this.wrap.createChild(
19077 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
19078 if (this.addicon) {
19079 this.editicon.setStyle('margin-left', '40px');
19081 this.editicon.on('click', function(e) {
19083 // we fire even if inothing is selected..
19084 this.fireEvent('edit', this, this.lastData );
19094 initEvents : function(){
19095 Roo.form.ComboBox.superclass.initEvents.call(this);
19097 this.keyNav = new Roo.KeyNav(this.el, {
19098 "up" : function(e){
19099 this.inKeyMode = true;
19103 "down" : function(e){
19104 if(!this.isExpanded()){
19105 this.onTriggerClick();
19107 this.inKeyMode = true;
19112 "enter" : function(e){
19113 this.onViewClick();
19117 "esc" : function(e){
19121 "tab" : function(e){
19122 this.onViewClick(false);
19123 this.fireEvent("specialkey", this, e);
19129 doRelay : function(foo, bar, hname){
19130 if(hname == 'down' || this.scope.isExpanded()){
19131 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
19138 this.queryDelay = Math.max(this.queryDelay || 10,
19139 this.mode == 'local' ? 10 : 250);
19140 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
19141 if(this.typeAhead){
19142 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
19144 if(this.editable !== false){
19145 this.el.on("keyup", this.onKeyUp, this);
19147 if(this.forceSelection){
19148 this.on('blur', this.doForce, this);
19152 onDestroy : function(){
19154 this.view.setStore(null);
19155 this.view.el.removeAllListeners();
19156 this.view.el.remove();
19157 this.view.purgeListeners();
19160 this.list.destroy();
19163 this.store.un('beforeload', this.onBeforeLoad, this);
19164 this.store.un('load', this.onLoad, this);
19165 this.store.un('loadexception', this.onLoadException, this);
19167 Roo.form.ComboBox.superclass.onDestroy.call(this);
19171 fireKey : function(e){
19172 if(e.isNavKeyPress() && !this.list.isVisible()){
19173 this.fireEvent("specialkey", this, e);
19178 onResize: function(w, h){
19179 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19181 if(typeof w != 'number'){
19182 // we do not handle it!?!?
19185 var tw = this.trigger.getWidth();
19186 tw += this.addicon ? this.addicon.getWidth() : 0;
19187 tw += this.editicon ? this.editicon.getWidth() : 0;
19189 this.el.setWidth( this.adjustWidth('input', x));
19191 this.trigger.setStyle('left', x+'px');
19193 if(this.list && this.listWidth === undefined){
19194 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19195 this.list.setWidth(lw);
19196 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19204 * Allow or prevent the user from directly editing the field text. If false is passed,
19205 * the user will only be able to select from the items defined in the dropdown list. This method
19206 * is the runtime equivalent of setting the 'editable' config option at config time.
19207 * @param {Boolean} value True to allow the user to directly edit the field text
19209 setEditable : function(value){
19210 if(value == this.editable){
19213 this.editable = value;
19215 this.el.dom.setAttribute('readOnly', true);
19216 this.el.on('mousedown', this.onTriggerClick, this);
19217 this.el.addClass('x-combo-noedit');
19219 this.el.dom.setAttribute('readOnly', false);
19220 this.el.un('mousedown', this.onTriggerClick, this);
19221 this.el.removeClass('x-combo-noedit');
19226 onBeforeLoad : function(){
19227 if(!this.hasFocus){
19230 this.innerList.update(this.loadingText ?
19231 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19232 this.restrictHeight();
19233 this.selectedIndex = -1;
19237 onLoad : function(){
19238 if(!this.hasFocus){
19241 if(this.store.getCount() > 0){
19243 this.restrictHeight();
19244 if(this.lastQuery == this.allQuery){
19246 this.el.dom.select();
19248 if(!this.selectByValue(this.value, true)){
19249 this.select(0, true);
19253 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19254 this.taTask.delay(this.typeAheadDelay);
19258 this.onEmptyResults();
19263 onLoadException : function()
19266 Roo.log(this.store.reader.jsonData);
19267 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19268 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19274 onTypeAhead : function(){
19275 if(this.store.getCount() > 0){
19276 var r = this.store.getAt(0);
19277 var newValue = r.data[this.displayField];
19278 var len = newValue.length;
19279 var selStart = this.getRawValue().length;
19280 if(selStart != len){
19281 this.setRawValue(newValue);
19282 this.selectText(selStart, newValue.length);
19288 onSelect : function(record, index){
19289 if(this.fireEvent('beforeselect', this, record, index) !== false){
19290 this.setFromData(index > -1 ? record.data : false);
19292 this.fireEvent('select', this, record, index);
19297 * Returns the currently selected field value or empty string if no value is set.
19298 * @return {String} value The selected value
19300 getValue : function(){
19301 if(this.valueField){
19302 return typeof this.value != 'undefined' ? this.value : '';
19304 return Roo.form.ComboBox.superclass.getValue.call(this);
19308 * Clears any text/value currently set in the field
19310 clearValue : function(){
19311 if(this.hiddenField){
19312 this.hiddenField.value = '';
19315 this.setRawValue('');
19316 this.lastSelectionText = '';
19321 * Sets the specified value into the field. If the value finds a match, the corresponding record text
19322 * will be displayed in the field. If the value does not match the data value of an existing item,
19323 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19324 * Otherwise the field will be blank (although the value will still be set).
19325 * @param {String} value The value to match
19327 setValue : function(v){
19329 if(this.valueField){
19330 var r = this.findRecord(this.valueField, v);
19332 text = r.data[this.displayField];
19333 }else if(this.valueNotFoundText !== undefined){
19334 text = this.valueNotFoundText;
19337 this.lastSelectionText = text;
19338 if(this.hiddenField){
19339 this.hiddenField.value = v;
19341 Roo.form.ComboBox.superclass.setValue.call(this, text);
19345 * @property {Object} the last set data for the element
19350 * Sets the value of the field based on a object which is related to the record format for the store.
19351 * @param {Object} value the value to set as. or false on reset?
19353 setFromData : function(o){
19354 var dv = ''; // display value
19355 var vv = ''; // value value..
19357 if (this.displayField) {
19358 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19360 // this is an error condition!!!
19361 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
19364 if(this.valueField){
19365 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19367 if(this.hiddenField){
19368 this.hiddenField.value = vv;
19370 this.lastSelectionText = dv;
19371 Roo.form.ComboBox.superclass.setValue.call(this, dv);
19375 // no hidden field.. - we store the value in 'value', but still display
19376 // display field!!!!
19377 this.lastSelectionText = dv;
19378 Roo.form.ComboBox.superclass.setValue.call(this, dv);
19384 reset : function(){
19385 // overridden so that last data is reset..
19386 this.setValue(this.resetValue);
19387 this.originalValue = this.getValue();
19388 this.clearInvalid();
19389 this.lastData = false;
19391 this.view.clearSelections();
19395 findRecord : function(prop, value){
19397 if(this.store.getCount() > 0){
19398 this.store.each(function(r){
19399 if(r.data[prop] == value){
19409 getName: function()
19411 // returns hidden if it's set..
19412 if (!this.rendered) {return ''};
19413 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
19417 onViewMove : function(e, t){
19418 this.inKeyMode = false;
19422 onViewOver : function(e, t){
19423 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19426 var item = this.view.findItemFromChild(t);
19428 var index = this.view.indexOf(item);
19429 this.select(index, false);
19434 onViewClick : function(doFocus)
19436 var index = this.view.getSelectedIndexes()[0];
19437 var r = this.store.getAt(index);
19439 this.onSelect(r, index);
19441 if(doFocus !== false && !this.blockFocus){
19447 restrictHeight : function(){
19448 this.innerList.dom.style.height = '';
19449 var inner = this.innerList.dom;
19450 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19451 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19452 this.list.beginUpdate();
19453 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19454 this.list.alignTo(this.el, this.listAlign);
19455 this.list.endUpdate();
19459 onEmptyResults : function(){
19464 * Returns true if the dropdown list is expanded, else false.
19466 isExpanded : function(){
19467 return this.list.isVisible();
19471 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19472 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19473 * @param {String} value The data value of the item to select
19474 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19475 * selected item if it is not currently in view (defaults to true)
19476 * @return {Boolean} True if the value matched an item in the list, else false
19478 selectByValue : function(v, scrollIntoView){
19479 if(v !== undefined && v !== null){
19480 var r = this.findRecord(this.valueField || this.displayField, v);
19482 this.select(this.store.indexOf(r), scrollIntoView);
19490 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19491 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19492 * @param {Number} index The zero-based index of the list item to select
19493 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19494 * selected item if it is not currently in view (defaults to true)
19496 select : function(index, scrollIntoView){
19497 this.selectedIndex = index;
19498 this.view.select(index);
19499 if(scrollIntoView !== false){
19500 var el = this.view.getNode(index);
19502 this.innerList.scrollChildIntoView(el, false);
19508 selectNext : function(){
19509 var ct = this.store.getCount();
19511 if(this.selectedIndex == -1){
19513 }else if(this.selectedIndex < ct-1){
19514 this.select(this.selectedIndex+1);
19520 selectPrev : function(){
19521 var ct = this.store.getCount();
19523 if(this.selectedIndex == -1){
19525 }else if(this.selectedIndex != 0){
19526 this.select(this.selectedIndex-1);
19532 onKeyUp : function(e){
19533 if(this.editable !== false && !e.isSpecialKey()){
19534 this.lastKey = e.getKey();
19535 this.dqTask.delay(this.queryDelay);
19540 validateBlur : function(){
19541 return !this.list || !this.list.isVisible();
19545 initQuery : function(){
19546 this.doQuery(this.getRawValue());
19550 doForce : function(){
19551 if(this.el.dom.value.length > 0){
19552 this.el.dom.value =
19553 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19559 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
19560 * query allowing the query action to be canceled if needed.
19561 * @param {String} query The SQL query to execute
19562 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19563 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
19564 * saved in the current store (defaults to false)
19566 doQuery : function(q, forceAll){
19567 if(q === undefined || q === null){
19572 forceAll: forceAll,
19576 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19580 forceAll = qe.forceAll;
19581 if(forceAll === true || (q.length >= this.minChars)){
19582 if(this.lastQuery != q || this.alwaysQuery){
19583 this.lastQuery = q;
19584 if(this.mode == 'local'){
19585 this.selectedIndex = -1;
19587 this.store.clearFilter();
19589 this.store.filter(this.displayField, q);
19593 this.store.baseParams[this.queryParam] = q;
19595 params: this.getParams(q)
19600 this.selectedIndex = -1;
19607 getParams : function(q){
19609 //p[this.queryParam] = q;
19612 p.limit = this.pageSize;
19618 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19620 collapse : function(){
19621 if(!this.isExpanded()){
19625 Roo.get(document).un('mousedown', this.collapseIf, this);
19626 Roo.get(document).un('mousewheel', this.collapseIf, this);
19627 if (!this.editable) {
19628 Roo.get(document).un('keydown', this.listKeyPress, this);
19630 this.fireEvent('collapse', this);
19634 collapseIf : function(e){
19635 if(!e.within(this.wrap) && !e.within(this.list)){
19641 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19643 expand : function(){
19644 if(this.isExpanded() || !this.hasFocus){
19647 this.list.alignTo(this.el, this.listAlign);
19649 Roo.get(document).on('mousedown', this.collapseIf, this);
19650 Roo.get(document).on('mousewheel', this.collapseIf, this);
19651 if (!this.editable) {
19652 Roo.get(document).on('keydown', this.listKeyPress, this);
19655 this.fireEvent('expand', this);
19659 // Implements the default empty TriggerField.onTriggerClick function
19660 onTriggerClick : function(){
19664 if(this.isExpanded()){
19666 if (!this.blockFocus) {
19671 this.hasFocus = true;
19672 if(this.triggerAction == 'all') {
19673 this.doQuery(this.allQuery, true);
19675 this.doQuery(this.getRawValue());
19677 if (!this.blockFocus) {
19682 listKeyPress : function(e)
19684 //Roo.log('listkeypress');
19685 // scroll to first matching element based on key pres..
19686 if (e.isSpecialKey()) {
19689 var k = String.fromCharCode(e.getKey()).toUpperCase();
19692 var csel = this.view.getSelectedNodes();
19693 var cselitem = false;
19695 var ix = this.view.indexOf(csel[0]);
19696 cselitem = this.store.getAt(ix);
19697 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19703 this.store.each(function(v) {
19705 // start at existing selection.
19706 if (cselitem.id == v.id) {
19712 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19713 match = this.store.indexOf(v);
19718 if (match === false) {
19719 return true; // no more action?
19722 this.view.select(match);
19723 var sn = Roo.get(this.view.getSelectedNodes()[0]);
19724 sn.scrollIntoView(sn.dom.parentNode, false);
19728 * @cfg {Boolean} grow
19732 * @cfg {Number} growMin
19736 * @cfg {Number} growMax
19744 * Copyright(c) 2010-2012, Roo J Solutions Limited
19751 * @class Roo.form.ComboBoxArray
19752 * @extends Roo.form.TextField
19753 * A facebook style adder... for lists of email / people / countries etc...
19754 * pick multiple items from a combo box, and shows each one.
19756 * Fred [x] Brian [x] [Pick another |v]
19759 * For this to work: it needs various extra information
19760 * - normal combo problay has
19762 * + displayField, valueField
19764 * For our purpose...
19767 * If we change from 'extends' to wrapping...
19774 * Create a new ComboBoxArray.
19775 * @param {Object} config Configuration options
19779 Roo.form.ComboBoxArray = function(config)
19783 * @event beforeremove
19784 * Fires before remove the value from the list
19785 * @param {Roo.form.ComboBoxArray} _self This combo box array
19786 * @param {Roo.form.ComboBoxArray.Item} item removed item
19788 'beforeremove' : true,
19791 * Fires when remove the value from the list
19792 * @param {Roo.form.ComboBoxArray} _self This combo box array
19793 * @param {Roo.form.ComboBoxArray.Item} item removed item
19800 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19802 this.items = new Roo.util.MixedCollection(false);
19804 // construct the child combo...
19814 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19817 * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19822 // behavies liek a hiddne field
19823 inputType: 'hidden',
19825 * @cfg {Number} width The width of the box that displays the selected element
19832 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
19836 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
19838 hiddenName : false,
19841 // private the array of items that are displayed..
19843 // private - the hidden field el.
19845 // private - the filed el..
19848 //validateValue : function() { return true; }, // all values are ok!
19849 //onAddClick: function() { },
19851 onRender : function(ct, position)
19854 // create the standard hidden element
19855 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19858 // give fake names to child combo;
19859 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19860 this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19862 this.combo = Roo.factory(this.combo, Roo.form);
19863 this.combo.onRender(ct, position);
19864 if (typeof(this.combo.width) != 'undefined') {
19865 this.combo.onResize(this.combo.width,0);
19868 this.combo.initEvents();
19870 // assigned so form know we need to do this..
19871 this.store = this.combo.store;
19872 this.valueField = this.combo.valueField;
19873 this.displayField = this.combo.displayField ;
19876 this.combo.wrap.addClass('x-cbarray-grp');
19878 var cbwrap = this.combo.wrap.createChild(
19879 {tag: 'div', cls: 'x-cbarray-cb'},
19884 this.hiddenEl = this.combo.wrap.createChild({
19885 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
19887 this.el = this.combo.wrap.createChild({
19888 tag: 'input', type:'hidden' , name: this.name, value : ''
19890 // this.el.dom.removeAttribute("name");
19893 this.outerWrap = this.combo.wrap;
19894 this.wrap = cbwrap;
19896 this.outerWrap.setWidth(this.width);
19897 this.outerWrap.dom.removeChild(this.el.dom);
19899 this.wrap.dom.appendChild(this.el.dom);
19900 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19901 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19903 this.combo.trigger.setStyle('position','relative');
19904 this.combo.trigger.setStyle('left', '0px');
19905 this.combo.trigger.setStyle('top', '2px');
19907 this.combo.el.setStyle('vertical-align', 'text-bottom');
19909 //this.trigger.setStyle('vertical-align', 'top');
19911 // this should use the code from combo really... on('add' ....)
19915 this.adder = this.outerWrap.createChild(
19916 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
19918 this.adder.on('click', function(e) {
19919 _t.fireEvent('adderclick', this, e);
19923 //this.adder.on('click', this.onAddClick, _t);
19926 this.combo.on('select', function(cb, rec, ix) {
19927 this.addItem(rec.data);
19930 cb.el.dom.value = '';
19931 //cb.lastData = rec.data;
19940 getName: function()
19942 // returns hidden if it's set..
19943 if (!this.rendered) {return ''};
19944 return this.hiddenName ? this.hiddenName : this.name;
19949 onResize: function(w, h){
19952 // not sure if this is needed..
19953 //this.combo.onResize(w,h);
19955 if(typeof w != 'number'){
19956 // we do not handle it!?!?
19959 var tw = this.combo.trigger.getWidth();
19960 tw += this.addicon ? this.addicon.getWidth() : 0;
19961 tw += this.editicon ? this.editicon.getWidth() : 0;
19963 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19965 this.combo.trigger.setStyle('left', '0px');
19967 if(this.list && this.listWidth === undefined){
19968 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19969 this.list.setWidth(lw);
19970 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19977 addItem: function(rec)
19979 var valueField = this.combo.valueField;
19980 var displayField = this.combo.displayField;
19982 if (this.items.indexOfKey(rec[valueField]) > -1) {
19983 //console.log("GOT " + rec.data.id);
19987 var x = new Roo.form.ComboBoxArray.Item({
19988 //id : rec[this.idField],
19990 displayField : displayField ,
19991 tipField : displayField ,
19995 this.items.add(rec[valueField],x);
19996 // add it before the element..
19997 this.updateHiddenEl();
19998 x.render(this.outerWrap, this.wrap.dom);
19999 // add the image handler..
20002 updateHiddenEl : function()
20005 if (!this.hiddenEl) {
20009 var idField = this.combo.valueField;
20011 this.items.each(function(f) {
20012 ar.push(f.data[idField]);
20014 this.hiddenEl.dom.value = ar.join(',');
20020 this.items.clear();
20022 Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
20026 this.el.dom.value = '';
20027 if (this.hiddenEl) {
20028 this.hiddenEl.dom.value = '';
20032 getValue: function()
20034 return this.hiddenEl ? this.hiddenEl.dom.value : '';
20036 setValue: function(v) // not a valid action - must use addItems..
20041 if (this.store.isLocal && (typeof(v) == 'string')) {
20042 // then we can use the store to find the values..
20043 // comma seperated at present.. this needs to allow JSON based encoding..
20044 this.hiddenEl.value = v;
20046 Roo.each(v.split(','), function(k) {
20047 Roo.log("CHECK " + this.valueField + ',' + k);
20048 var li = this.store.query(this.valueField, k);
20053 add[this.valueField] = k;
20054 add[this.displayField] = li.item(0).data[this.displayField];
20060 if (typeof(v) == 'object' ) {
20061 // then let's assume it's an array of objects..
20062 Roo.each(v, function(l) {
20070 setFromData: function(v)
20072 // this recieves an object, if setValues is called.
20074 this.el.dom.value = v[this.displayField];
20075 this.hiddenEl.dom.value = v[this.valueField];
20076 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
20079 var kv = v[this.valueField];
20080 var dv = v[this.displayField];
20081 kv = typeof(kv) != 'string' ? '' : kv;
20082 dv = typeof(dv) != 'string' ? '' : dv;
20085 var keys = kv.split(',');
20086 var display = dv.split(',');
20087 for (var i = 0 ; i < keys.length; i++) {
20090 add[this.valueField] = keys[i];
20091 add[this.displayField] = display[i];
20099 * Validates the combox array value
20100 * @return {Boolean} True if the value is valid, else false
20102 validate : function(){
20103 if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
20104 this.clearInvalid();
20110 validateValue : function(value){
20111 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
20119 isDirty : function() {
20120 if(this.disabled) {
20125 var d = Roo.decode(String(this.originalValue));
20127 return String(this.getValue()) !== String(this.originalValue);
20130 var originalValue = [];
20132 for (var i = 0; i < d.length; i++){
20133 originalValue.push(d[i][this.valueField]);
20136 return String(this.getValue()) !== String(originalValue.join(','));
20145 * @class Roo.form.ComboBoxArray.Item
20146 * @extends Roo.BoxComponent
20147 * A selected item in the list
20148 * Fred [x] Brian [x] [Pick another |v]
20151 * Create a new item.
20152 * @param {Object} config Configuration options
20155 Roo.form.ComboBoxArray.Item = function(config) {
20156 config.id = Roo.id();
20157 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20160 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20163 displayField : false,
20167 defaultAutoCreate : {
20169 cls: 'x-cbarray-item',
20176 src : Roo.BLANK_IMAGE_URL ,
20184 onRender : function(ct, position)
20186 Roo.form.Field.superclass.onRender.call(this, ct, position);
20189 var cfg = this.getAutoCreate();
20190 this.el = ct.createChild(cfg, position);
20193 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20195 this.el.child('div').dom.innerHTML = this.cb.renderer ?
20196 this.cb.renderer(this.data) :
20197 String.format('{0}',this.data[this.displayField]);
20200 this.el.child('div').dom.setAttribute('qtip',
20201 String.format('{0}',this.data[this.tipField])
20204 this.el.child('img').on('click', this.remove, this);
20208 remove : function()
20210 if(this.cb.disabled){
20214 if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20215 this.cb.items.remove(this);
20216 this.el.child('img').un('click', this.remove, this);
20218 this.cb.updateHiddenEl();
20220 this.cb.fireEvent('remove', this.cb, this);
20225 * RooJS Library 1.1.1
20226 * Copyright(c) 2008-2011 Alan Knowles
20233 * @class Roo.form.ComboNested
20234 * @extends Roo.form.ComboBox
20235 * A combobox for that allows selection of nested items in a list,
20250 * Create a new ComboNested
20251 * @param {Object} config Configuration options
20253 Roo.form.ComboNested = function(config){
20254 Roo.form.ComboCheck.superclass.constructor.call(this, config);
20255 // should verify some data...
20257 // hiddenName = required..
20258 // displayField = required
20259 // valudField == required
20260 var req= [ 'hiddenName', 'displayField', 'valueField' ];
20262 Roo.each(req, function(e) {
20263 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
20264 throw "Roo.form.ComboNested : missing value for: " + e;
20271 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
20274 * @config {Number} max Number of columns to show
20279 list : null, // the outermost div..
20280 innerLists : null, // the
20284 onRender : function(ct, position)
20286 Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
20288 if(this.hiddenName){
20289 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
20291 this.hiddenField.value =
20292 this.hiddenValue !== undefined ? this.hiddenValue :
20293 this.value !== undefined ? this.value : '';
20295 // prevent input submission
20296 this.el.dom.removeAttribute('name');
20302 this.el.dom.setAttribute('autocomplete', 'off');
20305 var cls = 'x-combo-list';
20307 this.list = new Roo.Layer({
20308 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
20311 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
20312 this.list.setWidth(lw);
20313 this.list.swallowEvent('mousewheel');
20314 this.assetHeight = 0;
20317 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
20318 this.assetHeight += this.header.getHeight();
20320 this.innerLists = [];
20323 for (var i =0 ; i < this.maxColumns; i++) {
20324 this.onRenderList( cls, i);
20327 // always needs footer, as we are going to have an 'OK' button.
20328 this.footer = this.list.createChild({cls:cls+'-ft'});
20329 this.pageTb = new Roo.Toolbar(this.footer);
20334 handler: function()
20340 if ( this.allowBlank && !this.disableClear) {
20342 this.pageTb.add(new Roo.Toolbar.Fill(), {
20343 cls: 'x-btn-icon x-btn-clear',
20345 handler: function()
20348 _this.clearValue();
20349 _this.onSelect(false, -1);
20354 this.assetHeight += this.footer.getHeight();
20358 onRenderList : function ( cls, i)
20361 var lw = Math.floor(
20362 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20365 this.list.setWidth(lw); // default to '1'
20367 var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20368 //il.on('mouseover', this.onViewOver, this, { list: i });
20369 //il.on('mousemove', this.onViewMove, this, { list: i });
20371 il.setStyle({ 'overflow-x' : 'hidden'});
20374 this.tpl = new Roo.Template({
20375 html : '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20376 isEmpty: function (value, allValues) {
20378 var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20379 return dl ? 'has-children' : 'no-children'
20384 var store = this.store;
20386 store = new Roo.data.SimpleStore({
20387 //fields : this.store.reader.meta.fields,
20388 reader : this.store.reader,
20392 this.stores[i] = store;
20396 var view = this.views[i] = new Roo.View(
20402 selectedClass: this.selectedClass
20405 view.getEl().setWidth(lw);
20406 view.getEl().setStyle({
20407 position: i < 1 ? 'relative' : 'absolute',
20409 left: (i * lw ) + 'px',
20410 display : i > 0 ? 'none' : 'block'
20412 view.on('selectionchange', this.onSelectChange, this, {list : i });
20413 view.on('dblclick', this.onDoubleClick, this, {list : i });
20414 //view.on('click', this.onViewClick, this, { list : i });
20416 store.on('beforeload', this.onBeforeLoad, this);
20417 store.on('load', this.onLoad, this, { list : i});
20418 store.on('loadexception', this.onLoadException, this);
20420 // hide the other vies..
20425 onResize : function() {},
20427 restrictHeight : function()
20430 Roo.each(this.innerLists, function(il,i) {
20431 var el = this.views[i].getEl();
20432 el.dom.style.height = '';
20433 var inner = el.dom;
20434 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
20435 // only adjust heights on other ones..
20438 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20439 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20440 mh = Math.max(el.getHeight(), mh);
20446 this.list.beginUpdate();
20447 this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20448 this.list.alignTo(this.el, this.listAlign);
20449 this.list.endUpdate();
20454 // -- store handlers..
20456 onBeforeLoad : function()
20458 if(!this.hasFocus){
20461 this.innerLists[0].update(this.loadingText ?
20462 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20463 this.restrictHeight();
20464 this.selectedIndex = -1;
20467 onLoad : function(a,b,c,d)
20470 if(!this.hasFocus){
20474 if(this.store.getCount() > 0) {
20476 this.restrictHeight();
20478 this.onEmptyResults();
20481 this.stores[1].loadData([]);
20482 this.stores[2].loadData([]);
20491 onLoadException : function()
20494 Roo.log(this.store.reader.jsonData);
20495 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20496 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20504 onSelectChange : function (view, sels, opts )
20506 var ix = view.getSelectedIndexes();
20509 if (opts.list > this.maxColumns - 2) {
20511 this.setFromData(ix.length ? view.store.getAt(ix[0]).data : {});
20516 this.setFromData({});
20517 var str = this.stores[opts.list+1];
20522 var rec = view.store.getAt(ix[0]);
20523 this.setFromData(rec.data);
20525 var lw = Math.floor(
20526 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20529 this.stores[opts.list+1].loadDataFromChildren( rec );
20530 var dl = this.stores[opts.list+1]. getTotalCount();
20531 this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20532 this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20533 this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20534 this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20536 onDoubleClick : function()
20538 this.collapse(); //??
20543 findRecord : function (prop,value)
20545 return this.findRecordInStore(this.store, prop,value);
20549 findRecordInStore : function(store, prop, value)
20551 var cstore = new Roo.data.SimpleStore({
20552 //fields : this.store.reader.meta.fields, // we need array reader.. for
20553 reader : this.store.reader,
20557 var record = false;
20558 if(store.getCount() > 0){
20559 store.each(function(r){
20560 if(r.data[prop] == value){
20564 if (r.data.cn && r.data.cn.length) {
20565 cstore.loadDataFromChildren( r);
20566 var cret = _this.findRecordInStore(cstore, prop, value);
20567 if (cret !== false) {
20584 * Ext JS Library 1.1.1
20585 * Copyright(c) 2006-2007, Ext JS, LLC.
20587 * Originally Released Under LGPL - original licence link has changed is not relivant.
20590 * <script type="text/javascript">
20593 * @class Roo.form.Checkbox
20594 * @extends Roo.form.Field
20595 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
20597 * Creates a new Checkbox
20598 * @param {Object} config Configuration options
20600 Roo.form.Checkbox = function(config){
20601 Roo.form.Checkbox.superclass.constructor.call(this, config);
20605 * Fires when the checkbox is checked or unchecked.
20606 * @param {Roo.form.Checkbox} this This checkbox
20607 * @param {Boolean} checked The new checked value
20613 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
20615 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20617 focusClass : undefined,
20619 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20621 fieldClass: "x-form-field",
20623 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20627 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20628 * {tag: "input", type: "checkbox", autocomplete: "off"})
20630 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20632 * @cfg {String} boxLabel The text that appears beside the checkbox
20636 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20640 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20642 valueOff: '0', // value when not checked..
20644 actionMode : 'viewEl',
20647 itemCls : 'x-menu-check-item x-form-item',
20648 groupClass : 'x-menu-group-item',
20649 inputType : 'hidden',
20652 inSetChecked: false, // check that we are not calling self...
20654 inputElement: false, // real input element?
20655 basedOn: false, // ????
20657 isFormField: true, // not sure where this is needed!!!!
20659 onResize : function(){
20660 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20661 if(!this.boxLabel){
20662 this.el.alignTo(this.wrap, 'c-c');
20666 initEvents : function(){
20667 Roo.form.Checkbox.superclass.initEvents.call(this);
20668 this.el.on("click", this.onClick, this);
20669 this.el.on("change", this.onClick, this);
20673 getResizeEl : function(){
20677 getPositionEl : function(){
20682 onRender : function(ct, position){
20683 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20685 if(this.inputValue !== undefined){
20686 this.el.dom.value = this.inputValue;
20689 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20690 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20691 var viewEl = this.wrap.createChild({
20692 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20693 this.viewEl = viewEl;
20694 this.wrap.on('click', this.onClick, this);
20696 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20697 this.el.on('propertychange', this.setFromHidden, this); //ie
20702 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20703 // viewEl.on('click', this.onClick, this);
20705 //if(this.checked){
20706 this.setChecked(this.checked);
20708 //this.checked = this.el.dom;
20714 initValue : Roo.emptyFn,
20717 * Returns the checked state of the checkbox.
20718 * @return {Boolean} True if checked, else false
20720 getValue : function(){
20722 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20724 return this.valueOff;
20729 onClick : function(){
20730 if (this.disabled) {
20733 this.setChecked(!this.checked);
20735 //if(this.el.dom.checked != this.checked){
20736 // this.setValue(this.el.dom.checked);
20741 * Sets the checked state of the checkbox.
20742 * On is always based on a string comparison between inputValue and the param.
20743 * @param {Boolean/String} value - the value to set
20744 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20746 setValue : function(v,suppressEvent){
20749 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20750 //if(this.el && this.el.dom){
20751 // this.el.dom.checked = this.checked;
20752 // this.el.dom.defaultChecked = this.checked;
20754 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20755 //this.fireEvent("check", this, this.checked);
20758 setChecked : function(state,suppressEvent)
20760 if (this.inSetChecked) {
20761 this.checked = state;
20767 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20769 this.checked = state;
20770 if(suppressEvent !== true){
20771 this.fireEvent('check', this, state);
20773 this.inSetChecked = true;
20774 this.el.dom.value = state ? this.inputValue : this.valueOff;
20775 this.inSetChecked = false;
20778 // handle setting of hidden value by some other method!!?!?
20779 setFromHidden: function()
20784 //console.log("SET FROM HIDDEN");
20785 //alert('setFrom hidden');
20786 this.setValue(this.el.dom.value);
20789 onDestroy : function()
20792 Roo.get(this.viewEl).remove();
20795 Roo.form.Checkbox.superclass.onDestroy.call(this);
20798 setBoxLabel : function(str)
20800 this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20805 * Ext JS Library 1.1.1
20806 * Copyright(c) 2006-2007, Ext JS, LLC.
20808 * Originally Released Under LGPL - original licence link has changed is not relivant.
20811 * <script type="text/javascript">
20815 * @class Roo.form.Radio
20816 * @extends Roo.form.Checkbox
20817 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
20818 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20820 * Creates a new Radio
20821 * @param {Object} config Configuration options
20823 Roo.form.Radio = function(){
20824 Roo.form.Radio.superclass.constructor.apply(this, arguments);
20826 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20827 inputType: 'radio',
20830 * If this radio is part of a group, it will return the selected value
20833 getGroupValue : function(){
20834 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20838 onRender : function(ct, position){
20839 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20841 if(this.inputValue !== undefined){
20842 this.el.dom.value = this.inputValue;
20845 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20846 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20847 //var viewEl = this.wrap.createChild({
20848 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20849 //this.viewEl = viewEl;
20850 //this.wrap.on('click', this.onClick, this);
20852 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20853 //this.el.on('propertychange', this.setFromHidden, this); //ie
20858 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20859 // viewEl.on('click', this.onClick, this);
20862 this.el.dom.checked = 'checked' ;
20868 });//<script type="text/javascript">
20871 * Based Ext JS Library 1.1.1
20872 * Copyright(c) 2006-2007, Ext JS, LLC.
20878 * @class Roo.HtmlEditorCore
20879 * @extends Roo.Component
20880 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20882 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20885 Roo.HtmlEditorCore = function(config){
20888 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20893 * @event initialize
20894 * Fires when the editor is fully initialized (including the iframe)
20895 * @param {Roo.HtmlEditorCore} this
20900 * Fires when the editor is first receives the focus. Any insertion must wait
20901 * until after this event.
20902 * @param {Roo.HtmlEditorCore} this
20906 * @event beforesync
20907 * Fires before the textarea is updated with content from the editor iframe. Return false
20908 * to cancel the sync.
20909 * @param {Roo.HtmlEditorCore} this
20910 * @param {String} html
20914 * @event beforepush
20915 * Fires before the iframe editor is updated with content from the textarea. Return false
20916 * to cancel the push.
20917 * @param {Roo.HtmlEditorCore} this
20918 * @param {String} html
20923 * Fires when the textarea is updated with content from the editor iframe.
20924 * @param {Roo.HtmlEditorCore} this
20925 * @param {String} html
20930 * Fires when the iframe editor is updated with content from the textarea.
20931 * @param {Roo.HtmlEditorCore} this
20932 * @param {String} html
20937 * @event editorevent
20938 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20939 * @param {Roo.HtmlEditorCore} this
20945 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20947 // defaults : white / black...
20948 this.applyBlacklists();
20955 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20959 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20965 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20970 * @cfg {Number} height (in pixels)
20974 * @cfg {Number} width (in pixels)
20979 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20982 stylesheets: false,
20987 // private properties
20988 validationEvent : false,
20990 initialized : false,
20992 sourceEditMode : false,
20993 onFocus : Roo.emptyFn,
20995 hideMode:'offsets',
20999 // blacklist + whitelisted elements..
21006 * Protected method that will not generally be called directly. It
21007 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21008 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21010 getDocMarkup : function(){
21014 // inherit styels from page...??
21015 if (this.stylesheets === false) {
21017 Roo.get(document.head).select('style').each(function(node) {
21018 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21021 Roo.get(document.head).select('link').each(function(node) {
21022 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21025 } else if (!this.stylesheets.length) {
21027 st = '<style type="text/css">' +
21028 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21031 st = '<style type="text/css">' +
21036 st += '<style type="text/css">' +
21037 'IMG { cursor: pointer } ' +
21040 var cls = 'roo-htmleditor-body';
21042 if(this.bodyCls.length){
21043 cls += ' ' + this.bodyCls;
21046 return '<html><head>' + st +
21047 //<style type="text/css">' +
21048 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21050 ' </head><body class="' + cls + '"></body></html>';
21054 onRender : function(ct, position)
21057 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21058 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21061 this.el.dom.style.border = '0 none';
21062 this.el.dom.setAttribute('tabIndex', -1);
21063 this.el.addClass('x-hidden hide');
21067 if(Roo.isIE){ // fix IE 1px bogus margin
21068 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21072 this.frameId = Roo.id();
21076 var iframe = this.owner.wrap.createChild({
21078 cls: 'form-control', // bootstrap..
21080 name: this.frameId,
21081 frameBorder : 'no',
21082 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21087 this.iframe = iframe.dom;
21089 this.assignDocWin();
21091 this.doc.designMode = 'on';
21094 this.doc.write(this.getDocMarkup());
21098 var task = { // must defer to wait for browser to be ready
21100 //console.log("run task?" + this.doc.readyState);
21101 this.assignDocWin();
21102 if(this.doc.body || this.doc.readyState == 'complete'){
21104 this.doc.designMode="on";
21108 Roo.TaskMgr.stop(task);
21109 this.initEditor.defer(10, this);
21116 Roo.TaskMgr.start(task);
21121 onResize : function(w, h)
21123 Roo.log('resize: ' +w + ',' + h );
21124 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21128 if(typeof w == 'number'){
21130 this.iframe.style.width = w + 'px';
21132 if(typeof h == 'number'){
21134 this.iframe.style.height = h + 'px';
21136 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21143 * Toggles the editor between standard and source edit mode.
21144 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21146 toggleSourceEdit : function(sourceEditMode){
21148 this.sourceEditMode = sourceEditMode === true;
21150 if(this.sourceEditMode){
21152 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21155 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21156 //this.iframe.className = '';
21159 //this.setSize(this.owner.wrap.getSize());
21160 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21167 * Protected method that will not generally be called directly. If you need/want
21168 * custom HTML cleanup, this is the method you should override.
21169 * @param {String} html The HTML to be cleaned
21170 * return {String} The cleaned HTML
21172 cleanHtml : function(html){
21173 html = String(html);
21174 if(html.length > 5){
21175 if(Roo.isSafari){ // strip safari nonsense
21176 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21179 if(html == ' '){
21186 * HTML Editor -> Textarea
21187 * Protected method that will not generally be called directly. Syncs the contents
21188 * of the editor iframe with the textarea.
21190 syncValue : function(){
21191 if(this.initialized){
21192 var bd = (this.doc.body || this.doc.documentElement);
21193 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21194 var html = bd.innerHTML;
21196 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21197 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21199 html = '<div style="'+m[0]+'">' + html + '</div>';
21202 html = this.cleanHtml(html);
21203 // fix up the special chars.. normaly like back quotes in word...
21204 // however we do not want to do this with chinese..
21205 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
21207 var cc = match.charCodeAt();
21209 // Get the character value, handling surrogate pairs
21210 if (match.length == 2) {
21211 // It's a surrogate pair, calculate the Unicode code point
21212 var high = match.charCodeAt(0) - 0xD800;
21213 var low = match.charCodeAt(1) - 0xDC00;
21214 cc = (high * 0x400) + low + 0x10000;
21216 (cc >= 0x4E00 && cc < 0xA000 ) ||
21217 (cc >= 0x3400 && cc < 0x4E00 ) ||
21218 (cc >= 0xf900 && cc < 0xfb00 )
21223 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
21224 return "&#" + cc + ";";
21231 if(this.owner.fireEvent('beforesync', this, html) !== false){
21232 this.el.dom.value = html;
21233 this.owner.fireEvent('sync', this, html);
21239 * Protected method that will not generally be called directly. Pushes the value of the textarea
21240 * into the iframe editor.
21242 pushValue : function(){
21243 if(this.initialized){
21244 var v = this.el.dom.value.trim();
21246 // if(v.length < 1){
21250 if(this.owner.fireEvent('beforepush', this, v) !== false){
21251 var d = (this.doc.body || this.doc.documentElement);
21253 this.cleanUpPaste();
21254 this.el.dom.value = d.innerHTML;
21255 this.owner.fireEvent('push', this, v);
21261 deferFocus : function(){
21262 this.focus.defer(10, this);
21266 focus : function(){
21267 if(this.win && !this.sourceEditMode){
21274 assignDocWin: function()
21276 var iframe = this.iframe;
21279 this.doc = iframe.contentWindow.document;
21280 this.win = iframe.contentWindow;
21282 // if (!Roo.get(this.frameId)) {
21285 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21286 // this.win = Roo.get(this.frameId).dom.contentWindow;
21288 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21292 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21293 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21298 initEditor : function(){
21299 //console.log("INIT EDITOR");
21300 this.assignDocWin();
21304 this.doc.designMode="on";
21306 this.doc.write(this.getDocMarkup());
21309 var dbody = (this.doc.body || this.doc.documentElement);
21310 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21311 // this copies styles from the containing element into thsi one..
21312 // not sure why we need all of this..
21313 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21315 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21316 //ss['background-attachment'] = 'fixed'; // w3c
21317 dbody.bgProperties = 'fixed'; // ie
21318 //Roo.DomHelper.applyStyles(dbody, ss);
21319 Roo.EventManager.on(this.doc, {
21320 //'mousedown': this.onEditorEvent,
21321 'mouseup': this.onEditorEvent,
21322 'dblclick': this.onEditorEvent,
21323 'click': this.onEditorEvent,
21324 'keyup': this.onEditorEvent,
21329 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21331 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21332 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21334 this.initialized = true;
21336 this.owner.fireEvent('initialize', this);
21341 onDestroy : function(){
21347 //for (var i =0; i < this.toolbars.length;i++) {
21348 // // fixme - ask toolbars for heights?
21349 // this.toolbars[i].onDestroy();
21352 //this.wrap.dom.innerHTML = '';
21353 //this.wrap.remove();
21358 onFirstFocus : function(){
21360 this.assignDocWin();
21363 this.activated = true;
21366 if(Roo.isGecko){ // prevent silly gecko errors
21368 var s = this.win.getSelection();
21369 if(!s.focusNode || s.focusNode.nodeType != 3){
21370 var r = s.getRangeAt(0);
21371 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21376 this.execCmd('useCSS', true);
21377 this.execCmd('styleWithCSS', false);
21380 this.owner.fireEvent('activate', this);
21384 adjustFont: function(btn){
21385 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21386 //if(Roo.isSafari){ // safari
21389 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21390 if(Roo.isSafari){ // safari
21391 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21392 v = (v < 10) ? 10 : v;
21393 v = (v > 48) ? 48 : v;
21394 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21399 v = Math.max(1, v+adjust);
21401 this.execCmd('FontSize', v );
21404 onEditorEvent : function(e)
21406 this.owner.fireEvent('editorevent', this, e);
21407 // this.updateToolbar();
21408 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21411 insertTag : function(tg)
21413 // could be a bit smarter... -> wrap the current selected tRoo..
21414 if (tg.toLowerCase() == 'span' ||
21415 tg.toLowerCase() == 'code' ||
21416 tg.toLowerCase() == 'sup' ||
21417 tg.toLowerCase() == 'sub'
21420 range = this.createRange(this.getSelection());
21421 var wrappingNode = this.doc.createElement(tg.toLowerCase());
21422 wrappingNode.appendChild(range.extractContents());
21423 range.insertNode(wrappingNode);
21430 this.execCmd("formatblock", tg);
21434 insertText : function(txt)
21438 var range = this.createRange();
21439 range.deleteContents();
21440 //alert(Sender.getAttribute('label'));
21442 range.insertNode(this.doc.createTextNode(txt));
21448 * Executes a Midas editor command on the editor document and performs necessary focus and
21449 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21450 * @param {String} cmd The Midas command
21451 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21453 relayCmd : function(cmd, value){
21455 this.execCmd(cmd, value);
21456 this.owner.fireEvent('editorevent', this);
21457 //this.updateToolbar();
21458 this.owner.deferFocus();
21462 * Executes a Midas editor command directly on the editor document.
21463 * For visual commands, you should use {@link #relayCmd} instead.
21464 * <b>This should only be called after the editor is initialized.</b>
21465 * @param {String} cmd The Midas command
21466 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21468 execCmd : function(cmd, value){
21469 this.doc.execCommand(cmd, false, value === undefined ? null : value);
21476 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21478 * @param {String} text | dom node..
21480 insertAtCursor : function(text)
21483 if(!this.activated){
21489 var r = this.doc.selection.createRange();
21500 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21504 // from jquery ui (MIT licenced)
21506 var win = this.win;
21508 if (win.getSelection && win.getSelection().getRangeAt) {
21509 range = win.getSelection().getRangeAt(0);
21510 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21511 range.insertNode(node);
21512 } else if (win.document.selection && win.document.selection.createRange) {
21513 // no firefox support
21514 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21515 win.document.selection.createRange().pasteHTML(txt);
21517 // no firefox support
21518 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21519 this.execCmd('InsertHTML', txt);
21528 mozKeyPress : function(e){
21530 var c = e.getCharCode(), cmd;
21533 c = String.fromCharCode(c).toLowerCase();
21547 this.cleanUpPaste.defer(100, this);
21555 e.preventDefault();
21563 fixKeys : function(){ // load time branching for fastest keydown performance
21565 return function(e){
21566 var k = e.getKey(), r;
21569 r = this.doc.selection.createRange();
21572 r.pasteHTML('    ');
21579 r = this.doc.selection.createRange();
21581 var target = r.parentElement();
21582 if(!target || target.tagName.toLowerCase() != 'li'){
21584 r.pasteHTML('<br />');
21590 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21591 this.cleanUpPaste.defer(100, this);
21597 }else if(Roo.isOpera){
21598 return function(e){
21599 var k = e.getKey();
21603 this.execCmd('InsertHTML','    ');
21606 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21607 this.cleanUpPaste.defer(100, this);
21612 }else if(Roo.isSafari){
21613 return function(e){
21614 var k = e.getKey();
21618 this.execCmd('InsertText','\t');
21622 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21623 this.cleanUpPaste.defer(100, this);
21631 getAllAncestors: function()
21633 var p = this.getSelectedNode();
21636 a.push(p); // push blank onto stack..
21637 p = this.getParentElement();
21641 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21645 a.push(this.doc.body);
21649 lastSelNode : false,
21652 getSelection : function()
21654 this.assignDocWin();
21655 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21658 getSelectedNode: function()
21660 // this may only work on Gecko!!!
21662 // should we cache this!!!!
21667 var range = this.createRange(this.getSelection()).cloneRange();
21670 var parent = range.parentElement();
21672 var testRange = range.duplicate();
21673 testRange.moveToElementText(parent);
21674 if (testRange.inRange(range)) {
21677 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21680 parent = parent.parentElement;
21685 // is ancestor a text element.
21686 var ac = range.commonAncestorContainer;
21687 if (ac.nodeType == 3) {
21688 ac = ac.parentNode;
21691 var ar = ac.childNodes;
21694 var other_nodes = [];
21695 var has_other_nodes = false;
21696 for (var i=0;i<ar.length;i++) {
21697 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21700 // fullly contained node.
21702 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21707 // probably selected..
21708 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21709 other_nodes.push(ar[i]);
21713 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21718 has_other_nodes = true;
21720 if (!nodes.length && other_nodes.length) {
21721 nodes= other_nodes;
21723 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21729 createRange: function(sel)
21731 // this has strange effects when using with
21732 // top toolbar - not sure if it's a great idea.
21733 //this.editor.contentWindow.focus();
21734 if (typeof sel != "undefined") {
21736 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21738 return this.doc.createRange();
21741 return this.doc.createRange();
21744 getParentElement: function()
21747 this.assignDocWin();
21748 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21750 var range = this.createRange(sel);
21753 var p = range.commonAncestorContainer;
21754 while (p.nodeType == 3) { // text node
21765 * Range intersection.. the hard stuff...
21769 * [ -- selected range --- ]
21773 * if end is before start or hits it. fail.
21774 * if start is after end or hits it fail.
21776 * if either hits (but other is outside. - then it's not
21782 // @see http://www.thismuchiknow.co.uk/?p=64.
21783 rangeIntersectsNode : function(range, node)
21785 var nodeRange = node.ownerDocument.createRange();
21787 nodeRange.selectNode(node);
21789 nodeRange.selectNodeContents(node);
21792 var rangeStartRange = range.cloneRange();
21793 rangeStartRange.collapse(true);
21795 var rangeEndRange = range.cloneRange();
21796 rangeEndRange.collapse(false);
21798 var nodeStartRange = nodeRange.cloneRange();
21799 nodeStartRange.collapse(true);
21801 var nodeEndRange = nodeRange.cloneRange();
21802 nodeEndRange.collapse(false);
21804 return rangeStartRange.compareBoundaryPoints(
21805 Range.START_TO_START, nodeEndRange) == -1 &&
21806 rangeEndRange.compareBoundaryPoints(
21807 Range.START_TO_START, nodeStartRange) == 1;
21811 rangeCompareNode : function(range, node)
21813 var nodeRange = node.ownerDocument.createRange();
21815 nodeRange.selectNode(node);
21817 nodeRange.selectNodeContents(node);
21821 range.collapse(true);
21823 nodeRange.collapse(true);
21825 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21826 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21828 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21830 var nodeIsBefore = ss == 1;
21831 var nodeIsAfter = ee == -1;
21833 if (nodeIsBefore && nodeIsAfter) {
21836 if (!nodeIsBefore && nodeIsAfter) {
21837 return 1; //right trailed.
21840 if (nodeIsBefore && !nodeIsAfter) {
21841 return 2; // left trailed.
21847 // private? - in a new class?
21848 cleanUpPaste : function()
21850 // cleans up the whole document..
21851 Roo.log('cleanuppaste');
21853 this.cleanUpChildren(this.doc.body);
21854 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21855 if (clean != this.doc.body.innerHTML) {
21856 this.doc.body.innerHTML = clean;
21861 cleanWordChars : function(input) {// change the chars to hex code
21862 var he = Roo.HtmlEditorCore;
21864 var output = input;
21865 Roo.each(he.swapCodes, function(sw) {
21866 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21868 output = output.replace(swapper, sw[1]);
21875 cleanUpChildren : function (n)
21877 if (!n.childNodes.length) {
21880 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21881 this.cleanUpChild(n.childNodes[i]);
21888 cleanUpChild : function (node)
21891 //console.log(node);
21892 if (node.nodeName == "#text") {
21893 // clean up silly Windows -- stuff?
21896 if (node.nodeName == "#comment") {
21897 node.parentNode.removeChild(node);
21898 // clean up silly Windows -- stuff?
21901 var lcname = node.tagName.toLowerCase();
21902 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21903 // whitelist of tags..
21905 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21907 node.parentNode.removeChild(node);
21912 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21914 // spans with no attributes - just remove them..
21915 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
21916 remove_keep_children = true;
21919 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21920 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21922 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21923 // remove_keep_children = true;
21926 if (remove_keep_children) {
21927 this.cleanUpChildren(node);
21928 // inserts everything just before this node...
21929 while (node.childNodes.length) {
21930 var cn = node.childNodes[0];
21931 node.removeChild(cn);
21932 node.parentNode.insertBefore(cn, node);
21934 node.parentNode.removeChild(node);
21938 if (!node.attributes || !node.attributes.length) {
21943 this.cleanUpChildren(node);
21947 function cleanAttr(n,v)
21950 if (v.match(/^\./) || v.match(/^\//)) {
21953 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21956 if (v.match(/^#/)) {
21959 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21960 node.removeAttribute(n);
21964 var cwhite = this.cwhite;
21965 var cblack = this.cblack;
21967 function cleanStyle(n,v)
21969 if (v.match(/expression/)) { //XSS?? should we even bother..
21970 node.removeAttribute(n);
21974 var parts = v.split(/;/);
21977 Roo.each(parts, function(p) {
21978 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21982 var l = p.split(':').shift().replace(/\s+/g,'');
21983 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21985 if ( cwhite.length && cblack.indexOf(l) > -1) {
21986 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21987 //node.removeAttribute(n);
21991 // only allow 'c whitelisted system attributes'
21992 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21993 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21994 //node.removeAttribute(n);
22004 if (clean.length) {
22005 node.setAttribute(n, clean.join(';'));
22007 node.removeAttribute(n);
22013 for (var i = node.attributes.length-1; i > -1 ; i--) {
22014 var a = node.attributes[i];
22017 if (a.name.toLowerCase().substr(0,2)=='on') {
22018 node.removeAttribute(a.name);
22021 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22022 node.removeAttribute(a.name);
22025 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22026 cleanAttr(a.name,a.value); // fixme..
22029 if (a.name == 'style') {
22030 cleanStyle(a.name,a.value);
22033 /// clean up MS crap..
22034 // tecnically this should be a list of valid class'es..
22037 if (a.name == 'class') {
22038 if (a.value.match(/^Mso/)) {
22039 node.removeAttribute('class');
22042 if (a.value.match(/^body$/)) {
22043 node.removeAttribute('class');
22054 this.cleanUpChildren(node);
22060 * Clean up MS wordisms...
22062 cleanWord : function(node)
22065 this.cleanWord(this.doc.body);
22070 node.nodeName == 'SPAN' &&
22071 !node.hasAttributes() &&
22072 node.childNodes.length == 1 &&
22073 node.firstChild.nodeName == "#text"
22075 var textNode = node.firstChild;
22076 node.removeChild(textNode);
22077 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
22078 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
22080 node.parentNode.insertBefore(textNode, node);
22081 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
22082 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
22084 node.parentNode.removeChild(node);
22087 if (node.nodeName == "#text") {
22088 // clean up silly Windows -- stuff?
22091 if (node.nodeName == "#comment") {
22092 node.parentNode.removeChild(node);
22093 // clean up silly Windows -- stuff?
22097 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22098 node.parentNode.removeChild(node);
22101 //Roo.log(node.tagName);
22102 // remove - but keep children..
22103 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
22104 //Roo.log('-- removed');
22105 while (node.childNodes.length) {
22106 var cn = node.childNodes[0];
22107 node.removeChild(cn);
22108 node.parentNode.insertBefore(cn, node);
22109 // move node to parent - and clean it..
22110 this.cleanWord(cn);
22112 node.parentNode.removeChild(node);
22113 /// no need to iterate chidlren = it's got none..
22114 //this.iterateChildren(node, this.cleanWord);
22118 if (node.className.length) {
22120 var cn = node.className.split(/\W+/);
22122 Roo.each(cn, function(cls) {
22123 if (cls.match(/Mso[a-zA-Z]+/)) {
22128 node.className = cna.length ? cna.join(' ') : '';
22130 node.removeAttribute("class");
22134 if (node.hasAttribute("lang")) {
22135 node.removeAttribute("lang");
22138 if (node.hasAttribute("style")) {
22140 var styles = node.getAttribute("style").split(";");
22142 Roo.each(styles, function(s) {
22143 if (!s.match(/:/)) {
22146 var kv = s.split(":");
22147 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22150 // what ever is left... we allow.
22153 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22154 if (!nstyle.length) {
22155 node.removeAttribute('style');
22158 this.iterateChildren(node, this.cleanWord);
22164 * iterateChildren of a Node, calling fn each time, using this as the scole..
22165 * @param {DomNode} node node to iterate children of.
22166 * @param {Function} fn method of this class to call on each item.
22168 iterateChildren : function(node, fn)
22170 if (!node.childNodes.length) {
22173 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22174 fn.call(this, node.childNodes[i])
22180 * cleanTableWidths.
22182 * Quite often pasting from word etc.. results in tables with column and widths.
22183 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22186 cleanTableWidths : function(node)
22191 this.cleanTableWidths(this.doc.body);
22196 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22199 Roo.log(node.tagName);
22200 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22201 this.iterateChildren(node, this.cleanTableWidths);
22204 if (node.hasAttribute('width')) {
22205 node.removeAttribute('width');
22209 if (node.hasAttribute("style")) {
22212 var styles = node.getAttribute("style").split(";");
22214 Roo.each(styles, function(s) {
22215 if (!s.match(/:/)) {
22218 var kv = s.split(":");
22219 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22222 // what ever is left... we allow.
22225 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22226 if (!nstyle.length) {
22227 node.removeAttribute('style');
22231 this.iterateChildren(node, this.cleanTableWidths);
22239 domToHTML : function(currentElement, depth, nopadtext) {
22241 depth = depth || 0;
22242 nopadtext = nopadtext || false;
22244 if (!currentElement) {
22245 return this.domToHTML(this.doc.body);
22248 //Roo.log(currentElement);
22250 var allText = false;
22251 var nodeName = currentElement.nodeName;
22252 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22254 if (nodeName == '#text') {
22256 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22261 if (nodeName != 'BODY') {
22264 // Prints the node tagName, such as <A>, <IMG>, etc
22267 for(i = 0; i < currentElement.attributes.length;i++) {
22269 var aname = currentElement.attributes.item(i).name;
22270 if (!currentElement.attributes.item(i).value.length) {
22273 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22276 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22285 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22288 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22293 // Traverse the tree
22295 var currentElementChild = currentElement.childNodes.item(i);
22296 var allText = true;
22297 var innerHTML = '';
22299 while (currentElementChild) {
22300 // Formatting code (indent the tree so it looks nice on the screen)
22301 var nopad = nopadtext;
22302 if (lastnode == 'SPAN') {
22306 if (currentElementChild.nodeName == '#text') {
22307 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22308 toadd = nopadtext ? toadd : toadd.trim();
22309 if (!nopad && toadd.length > 80) {
22310 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22312 innerHTML += toadd;
22315 currentElementChild = currentElement.childNodes.item(i);
22321 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22323 // Recursively traverse the tree structure of the child node
22324 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22325 lastnode = currentElementChild.nodeName;
22327 currentElementChild=currentElement.childNodes.item(i);
22333 // The remaining code is mostly for formatting the tree
22334 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22339 ret+= "</"+tagName+">";
22345 applyBlacklists : function()
22347 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22348 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22352 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22353 if (b.indexOf(tag) > -1) {
22356 this.white.push(tag);
22360 Roo.each(w, function(tag) {
22361 if (b.indexOf(tag) > -1) {
22364 if (this.white.indexOf(tag) > -1) {
22367 this.white.push(tag);
22372 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22373 if (w.indexOf(tag) > -1) {
22376 this.black.push(tag);
22380 Roo.each(b, function(tag) {
22381 if (w.indexOf(tag) > -1) {
22384 if (this.black.indexOf(tag) > -1) {
22387 this.black.push(tag);
22392 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22393 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22397 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22398 if (b.indexOf(tag) > -1) {
22401 this.cwhite.push(tag);
22405 Roo.each(w, function(tag) {
22406 if (b.indexOf(tag) > -1) {
22409 if (this.cwhite.indexOf(tag) > -1) {
22412 this.cwhite.push(tag);
22417 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22418 if (w.indexOf(tag) > -1) {
22421 this.cblack.push(tag);
22425 Roo.each(b, function(tag) {
22426 if (w.indexOf(tag) > -1) {
22429 if (this.cblack.indexOf(tag) > -1) {
22432 this.cblack.push(tag);
22437 setStylesheets : function(stylesheets)
22439 if(typeof(stylesheets) == 'string'){
22440 Roo.get(this.iframe.contentDocument.head).createChild({
22442 rel : 'stylesheet',
22451 Roo.each(stylesheets, function(s) {
22456 Roo.get(_this.iframe.contentDocument.head).createChild({
22458 rel : 'stylesheet',
22467 removeStylesheets : function()
22471 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22476 setStyle : function(style)
22478 Roo.get(this.iframe.contentDocument.head).createChild({
22487 // hide stuff that is not compatible
22501 * @event specialkey
22505 * @cfg {String} fieldClass @hide
22508 * @cfg {String} focusClass @hide
22511 * @cfg {String} autoCreate @hide
22514 * @cfg {String} inputType @hide
22517 * @cfg {String} invalidClass @hide
22520 * @cfg {String} invalidText @hide
22523 * @cfg {String} msgFx @hide
22526 * @cfg {String} validateOnBlur @hide
22530 Roo.HtmlEditorCore.white = [
22531 'area', 'br', 'img', 'input', 'hr', 'wbr',
22533 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
22534 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
22535 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
22536 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
22537 'table', 'ul', 'xmp',
22539 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
22542 'dir', 'menu', 'ol', 'ul', 'dl',
22548 Roo.HtmlEditorCore.black = [
22549 // 'embed', 'object', // enable - backend responsiblity to clean thiese
22551 'base', 'basefont', 'bgsound', 'blink', 'body',
22552 'frame', 'frameset', 'head', 'html', 'ilayer',
22553 'iframe', 'layer', 'link', 'meta', 'object',
22554 'script', 'style' ,'title', 'xml' // clean later..
22556 Roo.HtmlEditorCore.clean = [
22557 'script', 'style', 'title', 'xml'
22559 Roo.HtmlEditorCore.remove = [
22564 Roo.HtmlEditorCore.ablack = [
22568 Roo.HtmlEditorCore.aclean = [
22569 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
22573 Roo.HtmlEditorCore.pwhite= [
22574 'http', 'https', 'mailto'
22577 // white listed style attributes.
22578 Roo.HtmlEditorCore.cwhite= [
22579 // 'text-align', /// default is to allow most things..
22585 // black listed style attributes.
22586 Roo.HtmlEditorCore.cblack= [
22587 // 'font-size' -- this can be set by the project
22591 Roo.HtmlEditorCore.swapCodes =[
22602 //<script type="text/javascript">
22605 * Ext JS Library 1.1.1
22606 * Copyright(c) 2006-2007, Ext JS, LLC.
22612 Roo.form.HtmlEditor = function(config){
22616 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22618 if (!this.toolbars) {
22619 this.toolbars = [];
22621 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22627 * @class Roo.form.HtmlEditor
22628 * @extends Roo.form.Field
22629 * Provides a lightweight HTML Editor component.
22631 * This has been tested on Fireforx / Chrome.. IE may not be so great..
22633 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22634 * supported by this editor.</b><br/><br/>
22635 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22636 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22638 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22640 * @cfg {Boolean} clearUp
22644 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22649 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22654 * @cfg {Number} height (in pixels)
22658 * @cfg {Number} width (in pixels)
22663 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22666 stylesheets: false,
22670 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22675 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22681 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22686 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22694 // private properties
22695 validationEvent : false,
22697 initialized : false,
22700 onFocus : Roo.emptyFn,
22702 hideMode:'offsets',
22704 actionMode : 'container', // defaults to hiding it...
22706 defaultAutoCreate : { // modified by initCompnoent..
22708 style:"width:500px;height:300px;",
22709 autocomplete: "new-password"
22713 initComponent : function(){
22716 * @event initialize
22717 * Fires when the editor is fully initialized (including the iframe)
22718 * @param {HtmlEditor} this
22723 * Fires when the editor is first receives the focus. Any insertion must wait
22724 * until after this event.
22725 * @param {HtmlEditor} this
22729 * @event beforesync
22730 * Fires before the textarea is updated with content from the editor iframe. Return false
22731 * to cancel the sync.
22732 * @param {HtmlEditor} this
22733 * @param {String} html
22737 * @event beforepush
22738 * Fires before the iframe editor is updated with content from the textarea. Return false
22739 * to cancel the push.
22740 * @param {HtmlEditor} this
22741 * @param {String} html
22746 * Fires when the textarea is updated with content from the editor iframe.
22747 * @param {HtmlEditor} this
22748 * @param {String} html
22753 * Fires when the iframe editor is updated with content from the textarea.
22754 * @param {HtmlEditor} this
22755 * @param {String} html
22759 * @event editmodechange
22760 * Fires when the editor switches edit modes
22761 * @param {HtmlEditor} this
22762 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22764 editmodechange: true,
22766 * @event editorevent
22767 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22768 * @param {HtmlEditor} this
22772 * @event firstfocus
22773 * Fires when on first focus - needed by toolbars..
22774 * @param {HtmlEditor} this
22779 * Auto save the htmlEditor value as a file into Events
22780 * @param {HtmlEditor} this
22784 * @event savedpreview
22785 * preview the saved version of htmlEditor
22786 * @param {HtmlEditor} this
22788 savedpreview: true,
22791 * @event stylesheetsclick
22792 * Fires when press the Sytlesheets button
22793 * @param {Roo.HtmlEditorCore} this
22795 stylesheetsclick: true
22797 this.defaultAutoCreate = {
22799 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22800 autocomplete: "new-password"
22805 * Protected method that will not generally be called directly. It
22806 * is called when the editor creates its toolbar. Override this method if you need to
22807 * add custom toolbar buttons.
22808 * @param {HtmlEditor} editor
22810 createToolbar : function(editor){
22811 Roo.log("create toolbars");
22812 if (!editor.toolbars || !editor.toolbars.length) {
22813 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22816 for (var i =0 ; i < editor.toolbars.length;i++) {
22817 editor.toolbars[i] = Roo.factory(
22818 typeof(editor.toolbars[i]) == 'string' ?
22819 { xtype: editor.toolbars[i]} : editor.toolbars[i],
22820 Roo.form.HtmlEditor);
22821 editor.toolbars[i].init(editor);
22829 onRender : function(ct, position)
22832 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22834 this.wrap = this.el.wrap({
22835 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22838 this.editorcore.onRender(ct, position);
22840 if (this.resizable) {
22841 this.resizeEl = new Roo.Resizable(this.wrap, {
22845 minHeight : this.height,
22846 height: this.height,
22847 handles : this.resizable,
22850 resize : function(r, w, h) {
22851 _t.onResize(w,h); // -something
22857 this.createToolbar(this);
22861 this.setSize(this.wrap.getSize());
22863 if (this.resizeEl) {
22864 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22865 // should trigger onReize..
22868 this.keyNav = new Roo.KeyNav(this.el, {
22870 "tab" : function(e){
22871 e.preventDefault();
22873 var value = this.getValue();
22875 var start = this.el.dom.selectionStart;
22876 var end = this.el.dom.selectionEnd;
22880 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22881 this.el.dom.setSelectionRange(end + 1, end + 1);
22885 var f = value.substring(0, start).split("\t");
22887 if(f.pop().length != 0){
22891 this.setValue(f.join("\t") + value.substring(end));
22892 this.el.dom.setSelectionRange(start - 1, start - 1);
22896 "home" : function(e){
22897 e.preventDefault();
22899 var curr = this.el.dom.selectionStart;
22900 var lines = this.getValue().split("\n");
22907 this.el.dom.setSelectionRange(0, 0);
22913 for (var i = 0; i < lines.length;i++) {
22914 pos += lines[i].length;
22924 pos -= lines[i].length;
22930 this.el.dom.setSelectionRange(pos, pos);
22934 this.el.dom.selectionStart = pos;
22935 this.el.dom.selectionEnd = curr;
22938 "end" : function(e){
22939 e.preventDefault();
22941 var curr = this.el.dom.selectionStart;
22942 var lines = this.getValue().split("\n");
22949 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22955 for (var i = 0; i < lines.length;i++) {
22957 pos += lines[i].length;
22971 this.el.dom.setSelectionRange(pos, pos);
22975 this.el.dom.selectionStart = curr;
22976 this.el.dom.selectionEnd = pos;
22981 doRelay : function(foo, bar, hname){
22982 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22988 // if(this.autosave && this.w){
22989 // this.autoSaveFn = setInterval(this.autosave, 1000);
22994 onResize : function(w, h)
22996 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
23001 if(typeof w == 'number'){
23002 var aw = w - this.wrap.getFrameWidth('lr');
23003 this.el.setWidth(this.adjustWidth('textarea', aw));
23006 if(typeof h == 'number'){
23008 for (var i =0; i < this.toolbars.length;i++) {
23009 // fixme - ask toolbars for heights?
23010 tbh += this.toolbars[i].tb.el.getHeight();
23011 if (this.toolbars[i].footer) {
23012 tbh += this.toolbars[i].footer.el.getHeight();
23019 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23020 ah -= 5; // knock a few pixes off for look..
23022 this.el.setHeight(this.adjustWidth('textarea', ah));
23026 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23027 this.editorcore.onResize(ew,eh);
23032 * Toggles the editor between standard and source edit mode.
23033 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23035 toggleSourceEdit : function(sourceEditMode)
23037 this.editorcore.toggleSourceEdit(sourceEditMode);
23039 if(this.editorcore.sourceEditMode){
23040 Roo.log('editor - showing textarea');
23043 // Roo.log(this.syncValue());
23044 this.editorcore.syncValue();
23045 this.el.removeClass('x-hidden');
23046 this.el.dom.removeAttribute('tabIndex');
23049 for (var i = 0; i < this.toolbars.length; i++) {
23050 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23051 this.toolbars[i].tb.hide();
23052 this.toolbars[i].footer.hide();
23057 Roo.log('editor - hiding textarea');
23059 // Roo.log(this.pushValue());
23060 this.editorcore.pushValue();
23062 this.el.addClass('x-hidden');
23063 this.el.dom.setAttribute('tabIndex', -1);
23065 for (var i = 0; i < this.toolbars.length; i++) {
23066 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23067 this.toolbars[i].tb.show();
23068 this.toolbars[i].footer.show();
23072 //this.deferFocus();
23075 this.setSize(this.wrap.getSize());
23076 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
23078 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23081 // private (for BoxComponent)
23082 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23084 // private (for BoxComponent)
23085 getResizeEl : function(){
23089 // private (for BoxComponent)
23090 getPositionEl : function(){
23095 initEvents : function(){
23096 this.originalValue = this.getValue();
23100 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23103 markInvalid : Roo.emptyFn,
23105 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23108 clearInvalid : Roo.emptyFn,
23110 setValue : function(v){
23111 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
23112 this.editorcore.pushValue();
23117 deferFocus : function(){
23118 this.focus.defer(10, this);
23122 focus : function(){
23123 this.editorcore.focus();
23129 onDestroy : function(){
23135 for (var i =0; i < this.toolbars.length;i++) {
23136 // fixme - ask toolbars for heights?
23137 this.toolbars[i].onDestroy();
23140 this.wrap.dom.innerHTML = '';
23141 this.wrap.remove();
23146 onFirstFocus : function(){
23147 //Roo.log("onFirstFocus");
23148 this.editorcore.onFirstFocus();
23149 for (var i =0; i < this.toolbars.length;i++) {
23150 this.toolbars[i].onFirstFocus();
23156 syncValue : function()
23158 this.editorcore.syncValue();
23161 pushValue : function()
23163 this.editorcore.pushValue();
23166 setStylesheets : function(stylesheets)
23168 this.editorcore.setStylesheets(stylesheets);
23171 removeStylesheets : function()
23173 this.editorcore.removeStylesheets();
23177 // hide stuff that is not compatible
23191 * @event specialkey
23195 * @cfg {String} fieldClass @hide
23198 * @cfg {String} focusClass @hide
23201 * @cfg {String} autoCreate @hide
23204 * @cfg {String} inputType @hide
23207 * @cfg {String} invalidClass @hide
23210 * @cfg {String} invalidText @hide
23213 * @cfg {String} msgFx @hide
23216 * @cfg {String} validateOnBlur @hide
23220 // <script type="text/javascript">
23223 * Ext JS Library 1.1.1
23224 * Copyright(c) 2006-2007, Ext JS, LLC.
23230 * @class Roo.form.HtmlEditorToolbar1
23235 new Roo.form.HtmlEditor({
23238 new Roo.form.HtmlEditorToolbar1({
23239 disable : { fonts: 1 , format: 1, ..., ... , ...],
23245 * @cfg {Object} disable List of elements to disable..
23246 * @cfg {Array} btns List of additional buttons.
23250 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23253 Roo.form.HtmlEditor.ToolbarStandard = function(config)
23256 Roo.apply(this, config);
23258 // default disabled, based on 'good practice'..
23259 this.disable = this.disable || {};
23260 Roo.applyIf(this.disable, {
23263 specialElements : true
23267 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23268 // dont call parent... till later.
23271 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype, {
23278 editorcore : false,
23280 * @cfg {Object} disable List of toolbar elements to disable
23287 * @cfg {String} createLinkText The default text for the create link prompt
23289 createLinkText : 'Please enter the URL for the link:',
23291 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23293 defaultLinkValue : 'http:/'+'/',
23297 * @cfg {Array} fontFamilies An array of available font families
23315 // "á" , ?? a acute?
23320 "°" // , // degrees
23322 // "é" , // e ecute
23323 // "ú" , // u ecute?
23326 specialElements : [
23328 text: "Insert Table",
23331 ihtml : '<table><tr><td>Cell</td></tr></table>'
23335 text: "Insert Image",
23338 ihtml : '<img src="about:blank"/>'
23347 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
23348 "input:submit", "input:button", "select", "textarea", "label" ],
23351 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
23353 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23362 * @cfg {String} defaultFont default font to use.
23364 defaultFont: 'tahoma',
23366 fontSelect : false,
23369 formatCombo : false,
23371 init : function(editor)
23373 this.editor = editor;
23374 this.editorcore = editor.editorcore ? editor.editorcore : editor;
23375 var editorcore = this.editorcore;
23379 var fid = editorcore.frameId;
23381 function btn(id, toggle, handler){
23382 var xid = fid + '-'+ id ;
23386 cls : 'x-btn-icon x-edit-'+id,
23387 enableToggle:toggle !== false,
23388 scope: _t, // was editor...
23389 handler:handler||_t.relayBtnCmd,
23390 clickEvent:'mousedown',
23391 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23398 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23400 // stop form submits
23401 tb.el.on('click', function(e){
23402 e.preventDefault(); // what does this do?
23405 if(!this.disable.font) { // && !Roo.isSafari){
23406 /* why no safari for fonts
23407 editor.fontSelect = tb.el.createChild({
23410 cls:'x-font-select',
23411 html: this.createFontOptions()
23414 editor.fontSelect.on('change', function(){
23415 var font = editor.fontSelect.dom.value;
23416 editor.relayCmd('fontname', font);
23417 editor.deferFocus();
23421 editor.fontSelect.dom,
23427 if(!this.disable.formats){
23428 this.formatCombo = new Roo.form.ComboBox({
23429 store: new Roo.data.SimpleStore({
23432 data : this.formats // from states.js
23436 //autoCreate : {tag: "div", size: "20"},
23437 displayField:'tag',
23441 triggerAction: 'all',
23442 emptyText:'Add tag',
23443 selectOnFocus:true,
23446 'select': function(c, r, i) {
23447 editorcore.insertTag(r.get('tag'));
23453 tb.addField(this.formatCombo);
23457 if(!this.disable.format){
23462 btn('strikethrough')
23465 if(!this.disable.fontSize){
23470 btn('increasefontsize', false, editorcore.adjustFont),
23471 btn('decreasefontsize', false, editorcore.adjustFont)
23476 if(!this.disable.colors){
23479 id:editorcore.frameId +'-forecolor',
23480 cls:'x-btn-icon x-edit-forecolor',
23481 clickEvent:'mousedown',
23482 tooltip: this.buttonTips['forecolor'] || undefined,
23484 menu : new Roo.menu.ColorMenu({
23485 allowReselect: true,
23486 focus: Roo.emptyFn,
23489 selectHandler: function(cp, color){
23490 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23491 editor.deferFocus();
23494 clickEvent:'mousedown'
23497 id:editorcore.frameId +'backcolor',
23498 cls:'x-btn-icon x-edit-backcolor',
23499 clickEvent:'mousedown',
23500 tooltip: this.buttonTips['backcolor'] || undefined,
23502 menu : new Roo.menu.ColorMenu({
23503 focus: Roo.emptyFn,
23506 allowReselect: true,
23507 selectHandler: function(cp, color){
23509 editorcore.execCmd('useCSS', false);
23510 editorcore.execCmd('hilitecolor', color);
23511 editorcore.execCmd('useCSS', true);
23512 editor.deferFocus();
23514 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
23515 Roo.isSafari || Roo.isIE ? '#'+color : color);
23516 editor.deferFocus();
23520 clickEvent:'mousedown'
23525 // now add all the items...
23528 if(!this.disable.alignments){
23531 btn('justifyleft'),
23532 btn('justifycenter'),
23533 btn('justifyright')
23537 //if(!Roo.isSafari){
23538 if(!this.disable.links){
23541 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
23545 if(!this.disable.lists){
23548 btn('insertorderedlist'),
23549 btn('insertunorderedlist')
23552 if(!this.disable.sourceEdit){
23555 btn('sourceedit', true, function(btn){
23556 this.toggleSourceEdit(btn.pressed);
23563 // special menu.. - needs to be tidied up..
23564 if (!this.disable.special) {
23567 cls: 'x-edit-none',
23573 for (var i =0; i < this.specialChars.length; i++) {
23574 smenu.menu.items.push({
23576 html: this.specialChars[i],
23577 handler: function(a,b) {
23578 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23579 //editor.insertAtCursor(a.html);
23593 if (!this.disable.cleanStyles) {
23595 cls: 'x-btn-icon x-btn-clear',
23601 for (var i =0; i < this.cleanStyles.length; i++) {
23602 cmenu.menu.items.push({
23603 actiontype : this.cleanStyles[i],
23604 html: 'Remove ' + this.cleanStyles[i],
23605 handler: function(a,b) {
23608 var c = Roo.get(editorcore.doc.body);
23609 c.select('[style]').each(function(s) {
23610 s.dom.style.removeProperty(a.actiontype);
23612 editorcore.syncValue();
23617 cmenu.menu.items.push({
23618 actiontype : 'tablewidths',
23619 html: 'Remove Table Widths',
23620 handler: function(a,b) {
23621 editorcore.cleanTableWidths();
23622 editorcore.syncValue();
23626 cmenu.menu.items.push({
23627 actiontype : 'word',
23628 html: 'Remove MS Word Formating',
23629 handler: function(a,b) {
23630 editorcore.cleanWord();
23631 editorcore.syncValue();
23636 cmenu.menu.items.push({
23637 actiontype : 'all',
23638 html: 'Remove All Styles',
23639 handler: function(a,b) {
23641 var c = Roo.get(editorcore.doc.body);
23642 c.select('[style]').each(function(s) {
23643 s.dom.removeAttribute('style');
23645 editorcore.syncValue();
23650 cmenu.menu.items.push({
23651 actiontype : 'all',
23652 html: 'Remove All CSS Classes',
23653 handler: function(a,b) {
23655 var c = Roo.get(editorcore.doc.body);
23656 c.select('[class]').each(function(s) {
23657 s.dom.removeAttribute('class');
23659 editorcore.cleanWord();
23660 editorcore.syncValue();
23665 cmenu.menu.items.push({
23666 actiontype : 'tidy',
23667 html: 'Tidy HTML Source',
23668 handler: function(a,b) {
23669 editorcore.doc.body.innerHTML = editorcore.domToHTML();
23670 editorcore.syncValue();
23679 if (!this.disable.specialElements) {
23682 cls: 'x-edit-none',
23687 for (var i =0; i < this.specialElements.length; i++) {
23688 semenu.menu.items.push(
23690 handler: function(a,b) {
23691 editor.insertAtCursor(this.ihtml);
23693 }, this.specialElements[i])
23705 for(var i =0; i< this.btns.length;i++) {
23706 var b = Roo.factory(this.btns[i],Roo.form);
23707 b.cls = 'x-edit-none';
23709 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23710 b.cls += ' x-init-enable';
23713 b.scope = editorcore;
23721 // disable everything...
23723 this.tb.items.each(function(item){
23726 item.id != editorcore.frameId+ '-sourceedit' &&
23727 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23733 this.rendered = true;
23735 // the all the btns;
23736 editor.on('editorevent', this.updateToolbar, this);
23737 // other toolbars need to implement this..
23738 //editor.on('editmodechange', this.updateToolbar, this);
23742 relayBtnCmd : function(btn) {
23743 this.editorcore.relayCmd(btn.cmd);
23745 // private used internally
23746 createLink : function(){
23747 Roo.log("create link?");
23748 var url = prompt(this.createLinkText, this.defaultLinkValue);
23749 if(url && url != 'http:/'+'/'){
23750 this.editorcore.relayCmd('createlink', url);
23756 * Protected method that will not generally be called directly. It triggers
23757 * a toolbar update by reading the markup state of the current selection in the editor.
23759 updateToolbar: function(){
23761 if(!this.editorcore.activated){
23762 this.editor.onFirstFocus();
23766 var btns = this.tb.items.map,
23767 doc = this.editorcore.doc,
23768 frameId = this.editorcore.frameId;
23770 if(!this.disable.font && !Roo.isSafari){
23772 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23773 if(name != this.fontSelect.dom.value){
23774 this.fontSelect.dom.value = name;
23778 if(!this.disable.format){
23779 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23780 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23781 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23782 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23784 if(!this.disable.alignments){
23785 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23786 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23787 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23789 if(!Roo.isSafari && !this.disable.lists){
23790 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23791 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23794 var ans = this.editorcore.getAllAncestors();
23795 if (this.formatCombo) {
23798 var store = this.formatCombo.store;
23799 this.formatCombo.setValue("");
23800 for (var i =0; i < ans.length;i++) {
23801 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23803 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23811 // hides menus... - so this cant be on a menu...
23812 Roo.menu.MenuMgr.hideAll();
23814 //this.editorsyncValue();
23818 createFontOptions : function(){
23819 var buf = [], fs = this.fontFamilies, ff, lc;
23823 for(var i = 0, len = fs.length; i< len; i++){
23825 lc = ff.toLowerCase();
23827 '<option value="',lc,'" style="font-family:',ff,';"',
23828 (this.defaultFont == lc ? ' selected="true">' : '>'),
23833 return buf.join('');
23836 toggleSourceEdit : function(sourceEditMode){
23838 Roo.log("toolbar toogle");
23839 if(sourceEditMode === undefined){
23840 sourceEditMode = !this.sourceEditMode;
23842 this.sourceEditMode = sourceEditMode === true;
23843 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23844 // just toggle the button?
23845 if(btn.pressed !== this.sourceEditMode){
23846 btn.toggle(this.sourceEditMode);
23850 if(sourceEditMode){
23851 Roo.log("disabling buttons");
23852 this.tb.items.each(function(item){
23853 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23859 Roo.log("enabling buttons");
23860 if(this.editorcore.initialized){
23861 this.tb.items.each(function(item){
23867 Roo.log("calling toggole on editor");
23868 // tell the editor that it's been pressed..
23869 this.editor.toggleSourceEdit(sourceEditMode);
23873 * Object collection of toolbar tooltips for the buttons in the editor. The key
23874 * is the command id associated with that button and the value is a valid QuickTips object.
23879 title: 'Bold (Ctrl+B)',
23880 text: 'Make the selected text bold.',
23881 cls: 'x-html-editor-tip'
23884 title: 'Italic (Ctrl+I)',
23885 text: 'Make the selected text italic.',
23886 cls: 'x-html-editor-tip'
23894 title: 'Bold (Ctrl+B)',
23895 text: 'Make the selected text bold.',
23896 cls: 'x-html-editor-tip'
23899 title: 'Italic (Ctrl+I)',
23900 text: 'Make the selected text italic.',
23901 cls: 'x-html-editor-tip'
23904 title: 'Underline (Ctrl+U)',
23905 text: 'Underline the selected text.',
23906 cls: 'x-html-editor-tip'
23909 title: 'Strikethrough',
23910 text: 'Strikethrough the selected text.',
23911 cls: 'x-html-editor-tip'
23913 increasefontsize : {
23914 title: 'Grow Text',
23915 text: 'Increase the font size.',
23916 cls: 'x-html-editor-tip'
23918 decreasefontsize : {
23919 title: 'Shrink Text',
23920 text: 'Decrease the font size.',
23921 cls: 'x-html-editor-tip'
23924 title: 'Text Highlight Color',
23925 text: 'Change the background color of the selected text.',
23926 cls: 'x-html-editor-tip'
23929 title: 'Font Color',
23930 text: 'Change the color of the selected text.',
23931 cls: 'x-html-editor-tip'
23934 title: 'Align Text Left',
23935 text: 'Align text to the left.',
23936 cls: 'x-html-editor-tip'
23939 title: 'Center Text',
23940 text: 'Center text in the editor.',
23941 cls: 'x-html-editor-tip'
23944 title: 'Align Text Right',
23945 text: 'Align text to the right.',
23946 cls: 'x-html-editor-tip'
23948 insertunorderedlist : {
23949 title: 'Bullet List',
23950 text: 'Start a bulleted list.',
23951 cls: 'x-html-editor-tip'
23953 insertorderedlist : {
23954 title: 'Numbered List',
23955 text: 'Start a numbered list.',
23956 cls: 'x-html-editor-tip'
23959 title: 'Hyperlink',
23960 text: 'Make the selected text a hyperlink.',
23961 cls: 'x-html-editor-tip'
23964 title: 'Source Edit',
23965 text: 'Switch to source editing mode.',
23966 cls: 'x-html-editor-tip'
23970 onDestroy : function(){
23973 this.tb.items.each(function(item){
23975 item.menu.removeAll();
23977 item.menu.el.destroy();
23985 onFirstFocus: function() {
23986 this.tb.items.each(function(item){
23995 // <script type="text/javascript">
23998 * Ext JS Library 1.1.1
23999 * Copyright(c) 2006-2007, Ext JS, LLC.
24006 * @class Roo.form.HtmlEditor.ToolbarContext
24011 new Roo.form.HtmlEditor({
24014 { xtype: 'ToolbarStandard', styles : {} }
24015 { xtype: 'ToolbarContext', disable : {} }
24021 * @config : {Object} disable List of elements to disable.. (not done yet.)
24022 * @config : {Object} styles Map of styles available.
24026 Roo.form.HtmlEditor.ToolbarContext = function(config)
24029 Roo.apply(this, config);
24030 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24031 // dont call parent... till later.
24032 this.styles = this.styles || {};
24037 Roo.form.HtmlEditor.ToolbarContext.types = {
24049 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
24115 opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
24120 opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
24130 style : 'fontFamily',
24131 displayField: 'display',
24132 optname : 'font-family',
24181 // should we really allow this??
24182 // should this just be
24193 style : 'fontFamily',
24194 displayField: 'display',
24195 optname : 'font-family',
24202 style : 'fontFamily',
24203 displayField: 'display',
24204 optname : 'font-family',
24211 style : 'fontFamily',
24212 displayField: 'display',
24213 optname : 'font-family',
24224 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
24225 Roo.form.HtmlEditor.ToolbarContext.stores = false;
24227 Roo.form.HtmlEditor.ToolbarContext.options = {
24229 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
24230 [ 'Courier New', 'Courier New'],
24231 [ 'Tahoma', 'Tahoma'],
24232 [ 'Times New Roman,serif', 'Times'],
24233 [ 'Verdana','Verdana' ]
24237 // fixme - these need to be configurable..
24240 //Roo.form.HtmlEditor.ToolbarContext.types
24243 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
24250 editorcore : false,
24252 * @cfg {Object} disable List of toolbar elements to disable
24257 * @cfg {Object} styles List of styles
24258 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
24260 * These must be defined in the page, so they get rendered correctly..
24271 init : function(editor)
24273 this.editor = editor;
24274 this.editorcore = editor.editorcore ? editor.editorcore : editor;
24275 var editorcore = this.editorcore;
24277 var fid = editorcore.frameId;
24279 function btn(id, toggle, handler){
24280 var xid = fid + '-'+ id ;
24284 cls : 'x-btn-icon x-edit-'+id,
24285 enableToggle:toggle !== false,
24286 scope: editorcore, // was editor...
24287 handler:handler||editorcore.relayBtnCmd,
24288 clickEvent:'mousedown',
24289 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24293 // create a new element.
24294 var wdiv = editor.wrap.createChild({
24296 }, editor.wrap.dom.firstChild.nextSibling, true);
24298 // can we do this more than once??
24300 // stop form submits
24303 // disable everything...
24304 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24305 this.toolbars = {};
24307 for (var i in ty) {
24309 this.toolbars[i] = this.buildToolbar(ty[i],i);
24311 this.tb = this.toolbars.BODY;
24313 this.buildFooter();
24314 this.footer.show();
24315 editor.on('hide', function( ) { this.footer.hide() }, this);
24316 editor.on('show', function( ) { this.footer.show() }, this);
24319 this.rendered = true;
24321 // the all the btns;
24322 editor.on('editorevent', this.updateToolbar, this);
24323 // other toolbars need to implement this..
24324 //editor.on('editmodechange', this.updateToolbar, this);
24330 * Protected method that will not generally be called directly. It triggers
24331 * a toolbar update by reading the markup state of the current selection in the editor.
24333 * Note you can force an update by calling on('editorevent', scope, false)
24335 updateToolbar: function(editor,ev,sel){
24338 // capture mouse up - this is handy for selecting images..
24339 // perhaps should go somewhere else...
24340 if(!this.editorcore.activated){
24341 this.editor.onFirstFocus();
24347 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24348 // selectNode - might want to handle IE?
24350 (ev.type == 'mouseup' || ev.type == 'click' ) &&
24351 ev.target && ev.target.tagName == 'IMG') {
24352 // they have click on an image...
24353 // let's see if we can change the selection...
24356 var nodeRange = sel.ownerDocument.createRange();
24358 nodeRange.selectNode(sel);
24360 nodeRange.selectNodeContents(sel);
24362 //nodeRange.collapse(true);
24363 var s = this.editorcore.win.getSelection();
24364 s.removeAllRanges();
24365 s.addRange(nodeRange);
24369 var updateFooter = sel ? false : true;
24372 var ans = this.editorcore.getAllAncestors();
24375 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24378 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
24379 sel = sel ? sel : this.editorcore.doc.body;
24380 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24383 // pick a menu that exists..
24384 var tn = sel.tagName.toUpperCase();
24385 //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24387 tn = sel.tagName.toUpperCase();
24389 var lastSel = this.tb.selectedNode;
24391 this.tb.selectedNode = sel;
24393 // if current menu does not match..
24395 if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24398 ///console.log("show: " + tn);
24399 this.tb = typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24402 this.tb.items.first().el.innerHTML = tn + ': ';
24405 // update attributes
24406 if (this.tb.fields) {
24407 this.tb.fields.each(function(e) {
24409 e.setValue(sel.style[e.stylename]);
24412 e.setValue(sel.getAttribute(e.attrname));
24416 var hasStyles = false;
24417 for(var i in this.styles) {
24424 var st = this.tb.fields.item(0);
24426 st.store.removeAll();
24429 var cn = sel.className.split(/\s+/);
24432 if (this.styles['*']) {
24434 Roo.each(this.styles['*'], function(v) {
24435 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
24438 if (this.styles[tn]) {
24439 Roo.each(this.styles[tn], function(v) {
24440 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
24444 st.store.loadData(avs);
24448 // flag our selected Node.
24449 this.tb.selectedNode = sel;
24452 Roo.menu.MenuMgr.hideAll();
24456 if (!updateFooter) {
24457 //this.footDisp.dom.innerHTML = '';
24460 // update the footer
24464 this.footerEls = ans.reverse();
24465 Roo.each(this.footerEls, function(a,i) {
24466 if (!a) { return; }
24467 html += html.length ? ' > ' : '';
24469 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24474 var sz = this.footDisp.up('td').getSize();
24475 this.footDisp.dom.style.width = (sz.width -10) + 'px';
24476 this.footDisp.dom.style.marginLeft = '5px';
24478 this.footDisp.dom.style.overflow = 'hidden';
24480 this.footDisp.dom.innerHTML = html;
24482 //this.editorsyncValue();
24489 onDestroy : function(){
24492 this.tb.items.each(function(item){
24494 item.menu.removeAll();
24496 item.menu.el.destroy();
24504 onFirstFocus: function() {
24505 // need to do this for all the toolbars..
24506 this.tb.items.each(function(item){
24510 buildToolbar: function(tlist, nm)
24512 var editor = this.editor;
24513 var editorcore = this.editorcore;
24514 // create a new element.
24515 var wdiv = editor.wrap.createChild({
24517 }, editor.wrap.dom.firstChild.nextSibling, true);
24520 var tb = new Roo.Toolbar(wdiv);
24523 tb.add(nm+ ": ");
24526 for(var i in this.styles) {
24531 if (styles && styles.length) {
24533 // this needs a multi-select checkbox...
24534 tb.addField( new Roo.form.ComboBox({
24535 store: new Roo.data.SimpleStore({
24537 fields: ['val', 'selected'],
24540 name : '-roo-edit-className',
24541 attrname : 'className',
24542 displayField: 'val',
24546 triggerAction: 'all',
24547 emptyText:'Select Style',
24548 selectOnFocus:true,
24551 'select': function(c, r, i) {
24552 // initial support only for on class per el..
24553 tb.selectedNode.className = r ? r.get('val') : '';
24554 editorcore.syncValue();
24561 var tbc = Roo.form.HtmlEditor.ToolbarContext;
24562 var tbops = tbc.options;
24564 for (var i in tlist) {
24566 var item = tlist[i];
24567 tb.add(item.title + ": ");
24570 //optname == used so you can configure the options available..
24571 var opts = item.opts ? item.opts : false;
24572 if (item.optname) {
24573 opts = tbops[item.optname];
24578 // opts == pulldown..
24579 tb.addField( new Roo.form.ComboBox({
24580 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24582 fields: ['val', 'display'],
24585 name : '-roo-edit-' + i,
24587 stylename : item.style ? item.style : false,
24588 displayField: item.displayField ? item.displayField : 'val',
24589 valueField : 'val',
24591 mode: typeof(tbc.stores[i]) != 'undefined' ? 'remote' : 'local',
24593 triggerAction: 'all',
24594 emptyText:'Select',
24595 selectOnFocus:true,
24596 width: item.width ? item.width : 130,
24598 'select': function(c, r, i) {
24600 tb.selectedNode.style[c.stylename] = r.get('val');
24603 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24612 tb.addField( new Roo.form.TextField({
24615 //allowBlank:false,
24620 tb.addField( new Roo.form.TextField({
24621 name: '-roo-edit-' + i,
24628 'change' : function(f, nv, ov) {
24629 tb.selectedNode.setAttribute(f.attrname, nv);
24630 editorcore.syncValue();
24643 text: 'Stylesheets',
24646 click : function ()
24648 _this.editor.fireEvent('stylesheetsclick', _this.editor);
24656 text: 'Remove Tag',
24659 click : function ()
24662 // undo does not work.
24664 var sn = tb.selectedNode;
24666 var pn = sn.parentNode;
24668 var stn = sn.childNodes[0];
24669 var en = sn.childNodes[sn.childNodes.length - 1 ];
24670 while (sn.childNodes.length) {
24671 var node = sn.childNodes[0];
24672 sn.removeChild(node);
24674 pn.insertBefore(node, sn);
24677 pn.removeChild(sn);
24678 var range = editorcore.createRange();
24680 range.setStart(stn,0);
24681 range.setEnd(en,0); //????
24682 //range.selectNode(sel);
24685 var selection = editorcore.getSelection();
24686 selection.removeAllRanges();
24687 selection.addRange(range);
24691 //_this.updateToolbar(null, null, pn);
24692 _this.updateToolbar(null, null, null);
24693 _this.footDisp.dom.innerHTML = '';
24703 tb.el.on('click', function(e){
24704 e.preventDefault(); // what does this do?
24706 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24709 // dont need to disable them... as they will get hidden
24714 buildFooter : function()
24717 var fel = this.editor.wrap.createChild();
24718 this.footer = new Roo.Toolbar(fel);
24719 // toolbar has scrolly on left / right?
24720 var footDisp= new Roo.Toolbar.Fill();
24726 handler : function() {
24727 _t.footDisp.scrollTo('left',0,true)
24731 this.footer.add( footDisp );
24736 handler : function() {
24738 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24742 var fel = Roo.get(footDisp.el);
24743 fel.addClass('x-editor-context');
24744 this.footDispWrap = fel;
24745 this.footDispWrap.overflow = 'hidden';
24747 this.footDisp = fel.createChild();
24748 this.footDispWrap.on('click', this.onContextClick, this)
24752 onContextClick : function (ev,dom)
24754 ev.preventDefault();
24755 var cn = dom.className;
24757 if (!cn.match(/x-ed-loc-/)) {
24760 var n = cn.split('-').pop();
24761 var ans = this.footerEls;
24765 var range = this.editorcore.createRange();
24767 range.selectNodeContents(sel);
24768 //range.selectNode(sel);
24771 var selection = this.editorcore.getSelection();
24772 selection.removeAllRanges();
24773 selection.addRange(range);
24777 this.updateToolbar(null, null, sel);
24794 * Ext JS Library 1.1.1
24795 * Copyright(c) 2006-2007, Ext JS, LLC.
24797 * Originally Released Under LGPL - original licence link has changed is not relivant.
24800 * <script type="text/javascript">
24804 * @class Roo.form.BasicForm
24805 * @extends Roo.util.Observable
24806 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24808 * @param {String/HTMLElement/Roo.Element} el The form element or its id
24809 * @param {Object} config Configuration options
24811 Roo.form.BasicForm = function(el, config){
24812 this.allItems = [];
24813 this.childForms = [];
24814 Roo.apply(this, config);
24816 * The Roo.form.Field items in this form.
24817 * @type MixedCollection
24821 this.items = new Roo.util.MixedCollection(false, function(o){
24822 return o.id || (o.id = Roo.id());
24826 * @event beforeaction
24827 * Fires before any action is performed. Return false to cancel the action.
24828 * @param {Form} this
24829 * @param {Action} action The action to be performed
24831 beforeaction: true,
24833 * @event actionfailed
24834 * Fires when an action fails.
24835 * @param {Form} this
24836 * @param {Action} action The action that failed
24838 actionfailed : true,
24840 * @event actioncomplete
24841 * Fires when an action is completed.
24842 * @param {Form} this
24843 * @param {Action} action The action that completed
24845 actioncomplete : true
24850 Roo.form.BasicForm.superclass.constructor.call(this);
24852 Roo.form.BasicForm.popover.apply();
24855 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24857 * @cfg {String} method
24858 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24861 * @cfg {DataReader} reader
24862 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24863 * This is optional as there is built-in support for processing JSON.
24866 * @cfg {DataReader} errorReader
24867 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24868 * This is completely optional as there is built-in support for processing JSON.
24871 * @cfg {String} url
24872 * The URL to use for form actions if one isn't supplied in the action options.
24875 * @cfg {Boolean} fileUpload
24876 * Set to true if this form is a file upload.
24880 * @cfg {Object} baseParams
24881 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24886 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24891 activeAction : null,
24894 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24895 * or setValues() data instead of when the form was first created.
24897 trackResetOnLoad : false,
24901 * childForms - used for multi-tab forms
24904 childForms : false,
24907 * allItems - full list of fields.
24913 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24914 * element by passing it or its id or mask the form itself by passing in true.
24917 waitMsgTarget : false,
24922 disableMask : false,
24925 * @cfg {Boolean} errorMask (true|false) default false
24930 * @cfg {Number} maskOffset Default 100
24935 initEl : function(el){
24936 this.el = Roo.get(el);
24937 this.id = this.el.id || Roo.id();
24938 this.el.on('submit', this.onSubmit, this);
24939 this.el.addClass('x-form');
24943 onSubmit : function(e){
24948 * Returns true if client-side validation on the form is successful.
24951 isValid : function(){
24953 var target = false;
24954 this.items.each(function(f){
24961 if(!target && f.el.isVisible(true)){
24966 if(this.errorMask && !valid){
24967 Roo.form.BasicForm.popover.mask(this, target);
24974 * DEPRICATED Returns true if any fields in this form have changed since their original load.
24977 isDirty : function(){
24979 this.items.each(function(f){
24989 * Returns true if any fields in this form have changed since their original load. (New version)
24993 hasChanged : function()
24996 this.items.each(function(f){
24997 if(f.hasChanged()){
25006 * Resets all hasChanged to 'false' -
25007 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
25008 * So hasChanged storage is only to be used for this purpose
25011 resetHasChanged : function()
25013 this.items.each(function(f){
25014 f.resetHasChanged();
25021 * Performs a predefined action (submit or load) or custom actions you define on this form.
25022 * @param {String} actionName The name of the action type
25023 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
25024 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
25025 * accept other config options):
25027 Property Type Description
25028 ---------------- --------------- ----------------------------------------------------------------------------------
25029 url String The url for the action (defaults to the form's url)
25030 method String The form method to use (defaults to the form's method, or POST if not defined)
25031 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
25032 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
25033 validate the form on the client (defaults to false)
25035 * @return {BasicForm} this
25037 doAction : function(action, options){
25038 if(typeof action == 'string'){
25039 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
25041 if(this.fireEvent('beforeaction', this, action) !== false){
25042 this.beforeAction(action);
25043 action.run.defer(100, action);
25049 * Shortcut to do a submit action.
25050 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25051 * @return {BasicForm} this
25053 submit : function(options){
25054 this.doAction('submit', options);
25059 * Shortcut to do a load action.
25060 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25061 * @return {BasicForm} this
25063 load : function(options){
25064 this.doAction('load', options);
25069 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
25070 * @param {Record} record The record to edit
25071 * @return {BasicForm} this
25073 updateRecord : function(record){
25074 record.beginEdit();
25075 var fs = record.fields;
25076 fs.each(function(f){
25077 var field = this.findField(f.name);
25079 record.set(f.name, field.getValue());
25087 * Loads an Roo.data.Record into this form.
25088 * @param {Record} record The record to load
25089 * @return {BasicForm} this
25091 loadRecord : function(record){
25092 this.setValues(record.data);
25097 beforeAction : function(action){
25098 var o = action.options;
25100 if(!this.disableMask) {
25101 if(this.waitMsgTarget === true){
25102 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
25103 }else if(this.waitMsgTarget){
25104 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
25105 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
25107 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
25115 afterAction : function(action, success){
25116 this.activeAction = null;
25117 var o = action.options;
25119 if(!this.disableMask) {
25120 if(this.waitMsgTarget === true){
25122 }else if(this.waitMsgTarget){
25123 this.waitMsgTarget.unmask();
25125 Roo.MessageBox.updateProgress(1);
25126 Roo.MessageBox.hide();
25134 Roo.callback(o.success, o.scope, [this, action]);
25135 this.fireEvent('actioncomplete', this, action);
25139 // failure condition..
25140 // we have a scenario where updates need confirming.
25141 // eg. if a locking scenario exists..
25142 // we look for { errors : { needs_confirm : true }} in the response.
25144 (typeof(action.result) != 'undefined') &&
25145 (typeof(action.result.errors) != 'undefined') &&
25146 (typeof(action.result.errors.needs_confirm) != 'undefined')
25149 Roo.MessageBox.confirm(
25150 "Change requires confirmation",
25151 action.result.errorMsg,
25156 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
25166 Roo.callback(o.failure, o.scope, [this, action]);
25167 // show an error message if no failed handler is set..
25168 if (!this.hasListener('actionfailed')) {
25169 Roo.MessageBox.alert("Error",
25170 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
25171 action.result.errorMsg :
25172 "Saving Failed, please check your entries or try again"
25176 this.fireEvent('actionfailed', this, action);
25182 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
25183 * @param {String} id The value to search for
25186 findField : function(id){
25187 var field = this.items.get(id);
25189 this.items.each(function(f){
25190 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
25196 return field || null;
25200 * Add a secondary form to this one,
25201 * Used to provide tabbed forms. One form is primary, with hidden values
25202 * which mirror the elements from the other forms.
25204 * @param {Roo.form.Form} form to add.
25207 addForm : function(form)
25210 if (this.childForms.indexOf(form) > -1) {
25214 this.childForms.push(form);
25216 Roo.each(form.allItems, function (fe) {
25218 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
25219 if (this.findField(n)) { // already added..
25222 var add = new Roo.form.Hidden({
25225 add.render(this.el);
25232 * Mark fields in this form invalid in bulk.
25233 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
25234 * @return {BasicForm} this
25236 markInvalid : function(errors){
25237 if(errors instanceof Array){
25238 for(var i = 0, len = errors.length; i < len; i++){
25239 var fieldError = errors[i];
25240 var f = this.findField(fieldError.id);
25242 f.markInvalid(fieldError.msg);
25248 if(typeof errors[id] != 'function' && (field = this.findField(id))){
25249 field.markInvalid(errors[id]);
25253 Roo.each(this.childForms || [], function (f) {
25254 f.markInvalid(errors);
25261 * Set values for fields in this form in bulk.
25262 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
25263 * @return {BasicForm} this
25265 setValues : function(values){
25266 if(values instanceof Array){ // array of objects
25267 for(var i = 0, len = values.length; i < len; i++){
25269 var f = this.findField(v.id);
25271 f.setValue(v.value);
25272 if(this.trackResetOnLoad){
25273 f.originalValue = f.getValue();
25277 }else{ // object hash
25280 if(typeof values[id] != 'function' && (field = this.findField(id))){
25282 if (field.setFromData &&
25283 field.valueField &&
25284 field.displayField &&
25285 // combos' with local stores can
25286 // be queried via setValue()
25287 // to set their value..
25288 (field.store && !field.store.isLocal)
25292 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
25293 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
25294 field.setFromData(sd);
25297 field.setValue(values[id]);
25301 if(this.trackResetOnLoad){
25302 field.originalValue = field.getValue();
25307 this.resetHasChanged();
25310 Roo.each(this.childForms || [], function (f) {
25311 f.setValues(values);
25312 f.resetHasChanged();
25319 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25320 * they are returned as an array.
25321 * @param {Boolean} asString
25324 getValues : function(asString){
25325 if (this.childForms) {
25326 // copy values from the child forms
25327 Roo.each(this.childForms, function (f) {
25328 this.setValues(f.getValues());
25333 if (typeof(FormData) != 'undefined' && asString !== true) {
25334 var fd = (new FormData(this.el.dom)).entries();
25336 var ent = fd.next();
25337 while (!ent.done) {
25338 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25345 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25346 if(asString === true){
25349 return Roo.urlDecode(fs);
25353 * Returns the fields in this form as an object with key/value pairs.
25354 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25357 getFieldValues : function(with_hidden)
25359 if (this.childForms) {
25360 // copy values from the child forms
25361 // should this call getFieldValues - probably not as we do not currently copy
25362 // hidden fields when we generate..
25363 Roo.each(this.childForms, function (f) {
25364 this.setValues(f.getValues());
25369 this.items.each(function(f){
25370 if (!f.getName()) {
25373 var v = f.getValue();
25374 if (f.inputType =='radio') {
25375 if (typeof(ret[f.getName()]) == 'undefined') {
25376 ret[f.getName()] = ''; // empty..
25379 if (!f.el.dom.checked) {
25383 v = f.el.dom.value;
25387 // not sure if this supported any more..
25388 if ((typeof(v) == 'object') && f.getRawValue) {
25389 v = f.getRawValue() ; // dates..
25391 // combo boxes where name != hiddenName...
25392 if (f.name != f.getName()) {
25393 ret[f.name] = f.getRawValue();
25395 ret[f.getName()] = v;
25402 * Clears all invalid messages in this form.
25403 * @return {BasicForm} this
25405 clearInvalid : function(){
25406 this.items.each(function(f){
25410 Roo.each(this.childForms || [], function (f) {
25419 * Resets this form.
25420 * @return {BasicForm} this
25422 reset : function(){
25423 this.items.each(function(f){
25427 Roo.each(this.childForms || [], function (f) {
25430 this.resetHasChanged();
25436 * Add Roo.form components to this form.
25437 * @param {Field} field1
25438 * @param {Field} field2 (optional)
25439 * @param {Field} etc (optional)
25440 * @return {BasicForm} this
25443 this.items.addAll(Array.prototype.slice.call(arguments, 0));
25449 * Removes a field from the items collection (does NOT remove its markup).
25450 * @param {Field} field
25451 * @return {BasicForm} this
25453 remove : function(field){
25454 this.items.remove(field);
25459 * Looks at the fields in this form, checks them for an id attribute,
25460 * and calls applyTo on the existing dom element with that id.
25461 * @return {BasicForm} this
25463 render : function(){
25464 this.items.each(function(f){
25465 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25473 * Calls {@link Ext#apply} for all fields in this form with the passed object.
25474 * @param {Object} values
25475 * @return {BasicForm} this
25477 applyToFields : function(o){
25478 this.items.each(function(f){
25485 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25486 * @param {Object} values
25487 * @return {BasicForm} this
25489 applyIfToFields : function(o){
25490 this.items.each(function(f){
25498 Roo.BasicForm = Roo.form.BasicForm;
25500 Roo.apply(Roo.form.BasicForm, {
25514 intervalID : false,
25520 if(this.isApplied){
25525 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25526 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25527 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25528 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25531 this.maskEl.top.enableDisplayMode("block");
25532 this.maskEl.left.enableDisplayMode("block");
25533 this.maskEl.bottom.enableDisplayMode("block");
25534 this.maskEl.right.enableDisplayMode("block");
25536 Roo.get(document.body).on('click', function(){
25540 Roo.get(document.body).on('touchstart', function(){
25544 this.isApplied = true
25547 mask : function(form, target)
25551 this.target = target;
25553 if(!this.form.errorMask || !target.el){
25557 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25559 var ot = this.target.el.calcOffsetsTo(scrollable);
25561 var scrollTo = ot[1] - this.form.maskOffset;
25563 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25565 scrollable.scrollTo('top', scrollTo);
25567 var el = this.target.wrap || this.target.el;
25569 var box = el.getBox();
25571 this.maskEl.top.setStyle('position', 'absolute');
25572 this.maskEl.top.setStyle('z-index', 10000);
25573 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25574 this.maskEl.top.setLeft(0);
25575 this.maskEl.top.setTop(0);
25576 this.maskEl.top.show();
25578 this.maskEl.left.setStyle('position', 'absolute');
25579 this.maskEl.left.setStyle('z-index', 10000);
25580 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25581 this.maskEl.left.setLeft(0);
25582 this.maskEl.left.setTop(box.y - this.padding);
25583 this.maskEl.left.show();
25585 this.maskEl.bottom.setStyle('position', 'absolute');
25586 this.maskEl.bottom.setStyle('z-index', 10000);
25587 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25588 this.maskEl.bottom.setLeft(0);
25589 this.maskEl.bottom.setTop(box.bottom + this.padding);
25590 this.maskEl.bottom.show();
25592 this.maskEl.right.setStyle('position', 'absolute');
25593 this.maskEl.right.setStyle('z-index', 10000);
25594 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25595 this.maskEl.right.setLeft(box.right + this.padding);
25596 this.maskEl.right.setTop(box.y - this.padding);
25597 this.maskEl.right.show();
25599 this.intervalID = window.setInterval(function() {
25600 Roo.form.BasicForm.popover.unmask();
25603 window.onwheel = function(){ return false;};
25605 (function(){ this.isMasked = true; }).defer(500, this);
25609 unmask : function()
25611 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25615 this.maskEl.top.setStyle('position', 'absolute');
25616 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25617 this.maskEl.top.hide();
25619 this.maskEl.left.setStyle('position', 'absolute');
25620 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25621 this.maskEl.left.hide();
25623 this.maskEl.bottom.setStyle('position', 'absolute');
25624 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25625 this.maskEl.bottom.hide();
25627 this.maskEl.right.setStyle('position', 'absolute');
25628 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25629 this.maskEl.right.hide();
25631 window.onwheel = function(){ return true;};
25633 if(this.intervalID){
25634 window.clearInterval(this.intervalID);
25635 this.intervalID = false;
25638 this.isMasked = false;
25646 * Ext JS Library 1.1.1
25647 * Copyright(c) 2006-2007, Ext JS, LLC.
25649 * Originally Released Under LGPL - original licence link has changed is not relivant.
25652 * <script type="text/javascript">
25656 * @class Roo.form.Form
25657 * @extends Roo.form.BasicForm
25658 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25660 * @param {Object} config Configuration options
25662 Roo.form.Form = function(config){
25664 if (config.items) {
25665 xitems = config.items;
25666 delete config.items;
25670 Roo.form.Form.superclass.constructor.call(this, null, config);
25671 this.url = this.url || this.action;
25673 this.root = new Roo.form.Layout(Roo.applyIf({
25677 this.active = this.root;
25679 * Array of all the buttons that have been added to this form via {@link addButton}
25683 this.allItems = [];
25686 * @event clientvalidation
25687 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25688 * @param {Form} this
25689 * @param {Boolean} valid true if the form has passed client-side validation
25691 clientvalidation: true,
25694 * Fires when the form is rendered
25695 * @param {Roo.form.Form} form
25700 if (this.progressUrl) {
25701 // push a hidden field onto the list of fields..
25705 name : 'UPLOAD_IDENTIFIER'
25710 Roo.each(xitems, this.addxtype, this);
25714 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25716 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25719 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25722 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25724 buttonAlign:'center',
25727 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25732 * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25733 * This property cascades to child containers if not set.
25738 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25739 * fires a looping event with that state. This is required to bind buttons to the valid
25740 * state using the config value formBind:true on the button.
25742 monitorValid : false,
25745 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25750 * @cfg {String} progressUrl - Url to return progress data
25753 progressUrl : false,
25755 * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25756 * sending a formdata with extra parameters - eg uploaded elements.
25762 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25763 * fields are added and the column is closed. If no fields are passed the column remains open
25764 * until end() is called.
25765 * @param {Object} config The config to pass to the column
25766 * @param {Field} field1 (optional)
25767 * @param {Field} field2 (optional)
25768 * @param {Field} etc (optional)
25769 * @return Column The column container object
25771 column : function(c){
25772 var col = new Roo.form.Column(c);
25774 if(arguments.length > 1){ // duplicate code required because of Opera
25775 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25782 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25783 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25784 * until end() is called.
25785 * @param {Object} config The config to pass to the fieldset
25786 * @param {Field} field1 (optional)
25787 * @param {Field} field2 (optional)
25788 * @param {Field} etc (optional)
25789 * @return FieldSet The fieldset container object
25791 fieldset : function(c){
25792 var fs = new Roo.form.FieldSet(c);
25794 if(arguments.length > 1){ // duplicate code required because of Opera
25795 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25802 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25803 * fields are added and the container is closed. If no fields are passed the container remains open
25804 * until end() is called.
25805 * @param {Object} config The config to pass to the Layout
25806 * @param {Field} field1 (optional)
25807 * @param {Field} field2 (optional)
25808 * @param {Field} etc (optional)
25809 * @return Layout The container object
25811 container : function(c){
25812 var l = new Roo.form.Layout(c);
25814 if(arguments.length > 1){ // duplicate code required because of Opera
25815 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25822 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25823 * @param {Object} container A Roo.form.Layout or subclass of Layout
25824 * @return {Form} this
25826 start : function(c){
25827 // cascade label info
25828 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25829 this.active.stack.push(c);
25830 c.ownerCt = this.active;
25836 * Closes the current open container
25837 * @return {Form} this
25840 if(this.active == this.root){
25843 this.active = this.active.ownerCt;
25848 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
25849 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25850 * as the label of the field.
25851 * @param {Field} field1
25852 * @param {Field} field2 (optional)
25853 * @param {Field} etc. (optional)
25854 * @return {Form} this
25857 this.active.stack.push.apply(this.active.stack, arguments);
25858 this.allItems.push.apply(this.allItems,arguments);
25860 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25861 if(a[i].isFormField){
25866 Roo.form.Form.superclass.add.apply(this, r);
25876 * Find any element that has been added to a form, using it's ID or name
25877 * This can include framesets, columns etc. along with regular fields..
25878 * @param {String} id - id or name to find.
25880 * @return {Element} e - or false if nothing found.
25882 findbyId : function(id)
25888 Roo.each(this.allItems, function(f){
25889 if (f.id == id || f.name == id ){
25900 * Render this form into the passed container. This should only be called once!
25901 * @param {String/HTMLElement/Element} container The element this component should be rendered into
25902 * @return {Form} this
25904 render : function(ct)
25910 var o = this.autoCreate || {
25912 method : this.method || 'POST',
25913 id : this.id || Roo.id()
25915 this.initEl(ct.createChild(o));
25917 this.root.render(this.el);
25921 this.items.each(function(f){
25922 f.render('x-form-el-'+f.id);
25925 if(this.buttons.length > 0){
25926 // tables are required to maintain order and for correct IE layout
25927 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25928 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25929 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25931 var tr = tb.getElementsByTagName('tr')[0];
25932 for(var i = 0, len = this.buttons.length; i < len; i++) {
25933 var b = this.buttons[i];
25934 var td = document.createElement('td');
25935 td.className = 'x-form-btn-td';
25936 b.render(tr.appendChild(td));
25939 if(this.monitorValid){ // initialize after render
25940 this.startMonitoring();
25942 this.fireEvent('rendered', this);
25947 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25948 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25949 * object or a valid Roo.DomHelper element config
25950 * @param {Function} handler The function called when the button is clicked
25951 * @param {Object} scope (optional) The scope of the handler function
25952 * @return {Roo.Button}
25954 addButton : function(config, handler, scope){
25958 minWidth: this.minButtonWidth,
25961 if(typeof config == "string"){
25964 Roo.apply(bc, config);
25966 var btn = new Roo.Button(null, bc);
25967 this.buttons.push(btn);
25972 * Adds a series of form elements (using the xtype property as the factory method.
25973 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25974 * @param {Object} config
25977 addxtype : function()
25979 var ar = Array.prototype.slice.call(arguments, 0);
25981 for(var i = 0; i < ar.length; i++) {
25983 continue; // skip -- if this happends something invalid got sent, we
25984 // should ignore it, as basically that interface element will not show up
25985 // and that should be pretty obvious!!
25988 if (Roo.form[ar[i].xtype]) {
25990 var fe = Roo.factory(ar[i], Roo.form);
25996 fe.store.form = this;
26001 this.allItems.push(fe);
26002 if (fe.items && fe.addxtype) {
26003 fe.addxtype.apply(fe, fe.items);
26013 // console.log('adding ' + ar[i].xtype);
26015 if (ar[i].xtype == 'Button') {
26016 //console.log('adding button');
26017 //console.log(ar[i]);
26018 this.addButton(ar[i]);
26019 this.allItems.push(fe);
26023 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26024 alert('end is not supported on xtype any more, use items');
26026 // //console.log('adding end');
26034 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26035 * option "monitorValid"
26037 startMonitoring : function(){
26040 Roo.TaskMgr.start({
26041 run : this.bindHandler,
26042 interval : this.monitorPoll || 200,
26049 * Stops monitoring of the valid state of this form
26051 stopMonitoring : function(){
26052 this.bound = false;
26056 bindHandler : function(){
26058 return false; // stops binding
26061 this.items.each(function(f){
26062 if(!f.isValid(true)){
26067 for(var i = 0, len = this.buttons.length; i < len; i++){
26068 var btn = this.buttons[i];
26069 if(btn.formBind === true && btn.disabled === valid){
26070 btn.setDisabled(!valid);
26073 this.fireEvent('clientvalidation', this, valid);
26087 Roo.Form = Roo.form.Form;
26090 * Ext JS Library 1.1.1
26091 * Copyright(c) 2006-2007, Ext JS, LLC.
26093 * Originally Released Under LGPL - original licence link has changed is not relivant.
26096 * <script type="text/javascript">
26099 // as we use this in bootstrap.
26100 Roo.namespace('Roo.form');
26102 * @class Roo.form.Action
26103 * Internal Class used to handle form actions
26105 * @param {Roo.form.BasicForm} el The form element or its id
26106 * @param {Object} config Configuration options
26111 // define the action interface
26112 Roo.form.Action = function(form, options){
26114 this.options = options || {};
26117 * Client Validation Failed
26120 Roo.form.Action.CLIENT_INVALID = 'client';
26122 * Server Validation Failed
26125 Roo.form.Action.SERVER_INVALID = 'server';
26127 * Connect to Server Failed
26130 Roo.form.Action.CONNECT_FAILURE = 'connect';
26132 * Reading Data from Server Failed
26135 Roo.form.Action.LOAD_FAILURE = 'load';
26137 Roo.form.Action.prototype = {
26139 failureType : undefined,
26140 response : undefined,
26141 result : undefined,
26143 // interface method
26144 run : function(options){
26148 // interface method
26149 success : function(response){
26153 // interface method
26154 handleResponse : function(response){
26158 // default connection failure
26159 failure : function(response){
26161 this.response = response;
26162 this.failureType = Roo.form.Action.CONNECT_FAILURE;
26163 this.form.afterAction(this, false);
26166 processResponse : function(response){
26167 this.response = response;
26168 if(!response.responseText){
26171 this.result = this.handleResponse(response);
26172 return this.result;
26175 // utility functions used internally
26176 getUrl : function(appendParams){
26177 var url = this.options.url || this.form.url || this.form.el.dom.action;
26179 var p = this.getParams();
26181 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26187 getMethod : function(){
26188 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26191 getParams : function(){
26192 var bp = this.form.baseParams;
26193 var p = this.options.params;
26195 if(typeof p == "object"){
26196 p = Roo.urlEncode(Roo.applyIf(p, bp));
26197 }else if(typeof p == 'string' && bp){
26198 p += '&' + Roo.urlEncode(bp);
26201 p = Roo.urlEncode(bp);
26206 createCallback : function(){
26208 success: this.success,
26209 failure: this.failure,
26211 timeout: (this.form.timeout*1000),
26212 upload: this.form.fileUpload ? this.success : undefined
26217 Roo.form.Action.Submit = function(form, options){
26218 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
26221 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
26224 haveProgress : false,
26225 uploadComplete : false,
26227 // uploadProgress indicator.
26228 uploadProgress : function()
26230 if (!this.form.progressUrl) {
26234 if (!this.haveProgress) {
26235 Roo.MessageBox.progress("Uploading", "Uploading");
26237 if (this.uploadComplete) {
26238 Roo.MessageBox.hide();
26242 this.haveProgress = true;
26244 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
26246 var c = new Roo.data.Connection();
26248 url : this.form.progressUrl,
26253 success : function(req){
26254 //console.log(data);
26258 rdata = Roo.decode(req.responseText)
26260 Roo.log("Invalid data from server..");
26264 if (!rdata || !rdata.success) {
26266 Roo.MessageBox.alert(Roo.encode(rdata));
26269 var data = rdata.data;
26271 if (this.uploadComplete) {
26272 Roo.MessageBox.hide();
26277 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
26278 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
26281 this.uploadProgress.defer(2000,this);
26284 failure: function(data) {
26285 Roo.log('progress url failed ');
26296 // run get Values on the form, so it syncs any secondary forms.
26297 this.form.getValues();
26299 var o = this.options;
26300 var method = this.getMethod();
26301 var isPost = method == 'POST';
26302 if(o.clientValidation === false || this.form.isValid()){
26304 if (this.form.progressUrl) {
26305 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26306 (new Date() * 1) + '' + Math.random());
26311 Roo.Ajax.request(Roo.apply(this.createCallback(), {
26312 form:this.form.el.dom,
26313 url:this.getUrl(!isPost),
26315 params:isPost ? this.getParams() : null,
26316 isUpload: this.form.fileUpload,
26317 formData : this.form.formData
26320 this.uploadProgress();
26322 }else if (o.clientValidation !== false){ // client validation failed
26323 this.failureType = Roo.form.Action.CLIENT_INVALID;
26324 this.form.afterAction(this, false);
26328 success : function(response)
26330 this.uploadComplete= true;
26331 if (this.haveProgress) {
26332 Roo.MessageBox.hide();
26336 var result = this.processResponse(response);
26337 if(result === true || result.success){
26338 this.form.afterAction(this, true);
26342 this.form.markInvalid(result.errors);
26343 this.failureType = Roo.form.Action.SERVER_INVALID;
26345 this.form.afterAction(this, false);
26347 failure : function(response)
26349 this.uploadComplete= true;
26350 if (this.haveProgress) {
26351 Roo.MessageBox.hide();
26354 this.response = response;
26355 this.failureType = Roo.form.Action.CONNECT_FAILURE;
26356 this.form.afterAction(this, false);
26359 handleResponse : function(response){
26360 if(this.form.errorReader){
26361 var rs = this.form.errorReader.read(response);
26364 for(var i = 0, len = rs.records.length; i < len; i++) {
26365 var r = rs.records[i];
26366 errors[i] = r.data;
26369 if(errors.length < 1){
26373 success : rs.success,
26379 ret = Roo.decode(response.responseText);
26383 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26393 Roo.form.Action.Load = function(form, options){
26394 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26395 this.reader = this.form.reader;
26398 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26403 Roo.Ajax.request(Roo.apply(
26404 this.createCallback(), {
26405 method:this.getMethod(),
26406 url:this.getUrl(false),
26407 params:this.getParams()
26411 success : function(response){
26413 var result = this.processResponse(response);
26414 if(result === true || !result.success || !result.data){
26415 this.failureType = Roo.form.Action.LOAD_FAILURE;
26416 this.form.afterAction(this, false);
26419 this.form.clearInvalid();
26420 this.form.setValues(result.data);
26421 this.form.afterAction(this, true);
26424 handleResponse : function(response){
26425 if(this.form.reader){
26426 var rs = this.form.reader.read(response);
26427 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26429 success : rs.success,
26433 return Roo.decode(response.responseText);
26437 Roo.form.Action.ACTION_TYPES = {
26438 'load' : Roo.form.Action.Load,
26439 'submit' : Roo.form.Action.Submit
26442 * Ext JS Library 1.1.1
26443 * Copyright(c) 2006-2007, Ext JS, LLC.
26445 * Originally Released Under LGPL - original licence link has changed is not relivant.
26448 * <script type="text/javascript">
26452 * @class Roo.form.Layout
26453 * @extends Roo.Component
26454 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26456 * @param {Object} config Configuration options
26458 Roo.form.Layout = function(config){
26460 if (config.items) {
26461 xitems = config.items;
26462 delete config.items;
26464 Roo.form.Layout.superclass.constructor.call(this, config);
26466 Roo.each(xitems, this.addxtype, this);
26470 Roo.extend(Roo.form.Layout, Roo.Component, {
26472 * @cfg {String/Object} autoCreate
26473 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26476 * @cfg {String/Object/Function} style
26477 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26478 * a function which returns such a specification.
26481 * @cfg {String} labelAlign
26482 * Valid values are "left," "top" and "right" (defaults to "left")
26485 * @cfg {Number} labelWidth
26486 * Fixed width in pixels of all field labels (defaults to undefined)
26489 * @cfg {Boolean} clear
26490 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26494 * @cfg {String} labelSeparator
26495 * The separator to use after field labels (defaults to ':')
26497 labelSeparator : ':',
26499 * @cfg {Boolean} hideLabels
26500 * True to suppress the display of field labels in this layout (defaults to false)
26502 hideLabels : false,
26505 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26510 onRender : function(ct, position){
26511 if(this.el){ // from markup
26512 this.el = Roo.get(this.el);
26513 }else { // generate
26514 var cfg = this.getAutoCreate();
26515 this.el = ct.createChild(cfg, position);
26518 this.el.applyStyles(this.style);
26520 if(this.labelAlign){
26521 this.el.addClass('x-form-label-'+this.labelAlign);
26523 if(this.hideLabels){
26524 this.labelStyle = "display:none";
26525 this.elementStyle = "padding-left:0;";
26527 if(typeof this.labelWidth == 'number'){
26528 this.labelStyle = "width:"+this.labelWidth+"px;";
26529 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26531 if(this.labelAlign == 'top'){
26532 this.labelStyle = "width:auto;";
26533 this.elementStyle = "padding-left:0;";
26536 var stack = this.stack;
26537 var slen = stack.length;
26539 if(!this.fieldTpl){
26540 var t = new Roo.Template(
26541 '<div class="x-form-item {5}">',
26542 '<label for="{0}" style="{2}">{1}{4}</label>',
26543 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26545 '</div><div class="x-form-clear-left"></div>'
26547 t.disableFormats = true;
26549 Roo.form.Layout.prototype.fieldTpl = t;
26551 for(var i = 0; i < slen; i++) {
26552 if(stack[i].isFormField){
26553 this.renderField(stack[i]);
26555 this.renderComponent(stack[i]);
26560 this.el.createChild({cls:'x-form-clear'});
26565 renderField : function(f){
26566 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26569 f.labelStyle||this.labelStyle||'', //2
26570 this.elementStyle||'', //3
26571 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26572 f.itemCls||this.itemCls||'' //5
26573 ], true).getPrevSibling());
26577 renderComponent : function(c){
26578 c.render(c.isLayout ? this.el : this.el.createChild());
26581 * Adds a object form elements (using the xtype property as the factory method.)
26582 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
26583 * @param {Object} config
26585 addxtype : function(o)
26587 // create the lement.
26588 o.form = this.form;
26589 var fe = Roo.factory(o, Roo.form);
26590 this.form.allItems.push(fe);
26591 this.stack.push(fe);
26593 if (fe.isFormField) {
26594 this.form.items.add(fe);
26602 * @class Roo.form.Column
26603 * @extends Roo.form.Layout
26604 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26606 * @param {Object} config Configuration options
26608 Roo.form.Column = function(config){
26609 Roo.form.Column.superclass.constructor.call(this, config);
26612 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26614 * @cfg {Number/String} width
26615 * The fixed width of the column in pixels or CSS value (defaults to "auto")
26618 * @cfg {String/Object} autoCreate
26619 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26623 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26626 onRender : function(ct, position){
26627 Roo.form.Column.superclass.onRender.call(this, ct, position);
26629 this.el.setWidth(this.width);
26636 * @class Roo.form.Row
26637 * @extends Roo.form.Layout
26638 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26640 * @param {Object} config Configuration options
26644 Roo.form.Row = function(config){
26645 Roo.form.Row.superclass.constructor.call(this, config);
26648 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26650 * @cfg {Number/String} width
26651 * The fixed width of the column in pixels or CSS value (defaults to "auto")
26654 * @cfg {Number/String} height
26655 * The fixed height of the column in pixels or CSS value (defaults to "auto")
26657 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26661 onRender : function(ct, position){
26662 //console.log('row render');
26664 var t = new Roo.Template(
26665 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26666 '<label for="{0}" style="{2}">{1}{4}</label>',
26667 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26671 t.disableFormats = true;
26673 Roo.form.Layout.prototype.rowTpl = t;
26675 this.fieldTpl = this.rowTpl;
26677 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26678 var labelWidth = 100;
26680 if ((this.labelAlign != 'top')) {
26681 if (typeof this.labelWidth == 'number') {
26682 labelWidth = this.labelWidth
26684 this.padWidth = 20 + labelWidth;
26688 Roo.form.Column.superclass.onRender.call(this, ct, position);
26690 this.el.setWidth(this.width);
26693 this.el.setHeight(this.height);
26698 renderField : function(f){
26699 f.fieldEl = this.fieldTpl.append(this.el, [
26700 f.id, f.fieldLabel,
26701 f.labelStyle||this.labelStyle||'',
26702 this.elementStyle||'',
26703 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26704 f.itemCls||this.itemCls||'',
26705 f.width ? f.width + this.padWidth : 160 + this.padWidth
26712 * @class Roo.form.FieldSet
26713 * @extends Roo.form.Layout
26714 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26716 * @param {Object} config Configuration options
26718 Roo.form.FieldSet = function(config){
26719 Roo.form.FieldSet.superclass.constructor.call(this, config);
26722 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26724 * @cfg {String} legend
26725 * The text to display as the legend for the FieldSet (defaults to '')
26728 * @cfg {String/Object} autoCreate
26729 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26733 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26736 onRender : function(ct, position){
26737 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26739 this.setLegend(this.legend);
26744 setLegend : function(text){
26746 this.el.child('legend').update(text);
26751 * Ext JS Library 1.1.1
26752 * Copyright(c) 2006-2007, Ext JS, LLC.
26754 * Originally Released Under LGPL - original licence link has changed is not relivant.
26757 * <script type="text/javascript">
26760 * @class Roo.form.VTypes
26761 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26764 Roo.form.VTypes = function(){
26765 // closure these in so they are only created once.
26766 var alpha = /^[a-zA-Z_]+$/;
26767 var alphanum = /^[a-zA-Z0-9_]+$/;
26768 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26769 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26771 // All these messages and functions are configurable
26774 * The function used to validate email addresses
26775 * @param {String} value The email address
26777 'email' : function(v){
26778 return email.test(v);
26781 * The error text to display when the email validation function returns false
26784 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26786 * The keystroke filter mask to be applied on email input
26789 'emailMask' : /[a-z0-9_\.\-@]/i,
26792 * The function used to validate URLs
26793 * @param {String} value The URL
26795 'url' : function(v){
26796 return url.test(v);
26799 * The error text to display when the url validation function returns false
26802 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26805 * The function used to validate alpha values
26806 * @param {String} value The value
26808 'alpha' : function(v){
26809 return alpha.test(v);
26812 * The error text to display when the alpha validation function returns false
26815 'alphaText' : 'This field should only contain letters and _',
26817 * The keystroke filter mask to be applied on alpha input
26820 'alphaMask' : /[a-z_]/i,
26823 * The function used to validate alphanumeric values
26824 * @param {String} value The value
26826 'alphanum' : function(v){
26827 return alphanum.test(v);
26830 * The error text to display when the alphanumeric validation function returns false
26833 'alphanumText' : 'This field should only contain letters, numbers and _',
26835 * The keystroke filter mask to be applied on alphanumeric input
26838 'alphanumMask' : /[a-z0-9_]/i
26840 }();//<script type="text/javascript">
26843 * @class Roo.form.FCKeditor
26844 * @extends Roo.form.TextArea
26845 * Wrapper around the FCKEditor http://www.fckeditor.net
26847 * Creates a new FCKeditor
26848 * @param {Object} config Configuration options
26850 Roo.form.FCKeditor = function(config){
26851 Roo.form.FCKeditor.superclass.constructor.call(this, config);
26854 * @event editorinit
26855 * Fired when the editor is initialized - you can add extra handlers here..
26856 * @param {FCKeditor} this
26857 * @param {Object} the FCK object.
26864 Roo.form.FCKeditor.editors = { };
26865 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26867 //defaultAutoCreate : {
26868 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
26872 * @cfg {Object} fck options - see fck manual for details.
26877 * @cfg {Object} fck toolbar set (Basic or Default)
26879 toolbarSet : 'Basic',
26881 * @cfg {Object} fck BasePath
26883 basePath : '/fckeditor/',
26891 onRender : function(ct, position)
26894 this.defaultAutoCreate = {
26896 style:"width:300px;height:60px;",
26897 autocomplete: "new-password"
26900 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26903 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26904 if(this.preventScrollbars){
26905 this.el.setStyle("overflow", "hidden");
26907 this.el.setHeight(this.growMin);
26910 //console.log('onrender' + this.getId() );
26911 Roo.form.FCKeditor.editors[this.getId()] = this;
26914 this.replaceTextarea() ;
26918 getEditor : function() {
26919 return this.fckEditor;
26922 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
26923 * @param {Mixed} value The value to set
26927 setValue : function(value)
26929 //console.log('setValue: ' + value);
26931 if(typeof(value) == 'undefined') { // not sure why this is happending...
26934 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26936 //if(!this.el || !this.getEditor()) {
26937 // this.value = value;
26938 //this.setValue.defer(100,this,[value]);
26942 if(!this.getEditor()) {
26946 this.getEditor().SetData(value);
26953 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
26954 * @return {Mixed} value The field value
26956 getValue : function()
26959 if (this.frame && this.frame.dom.style.display == 'none') {
26960 return Roo.form.FCKeditor.superclass.getValue.call(this);
26963 if(!this.el || !this.getEditor()) {
26965 // this.getValue.defer(100,this);
26970 var value=this.getEditor().GetData();
26971 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26972 return Roo.form.FCKeditor.superclass.getValue.call(this);
26978 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
26979 * @return {Mixed} value The field value
26981 getRawValue : function()
26983 if (this.frame && this.frame.dom.style.display == 'none') {
26984 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26987 if(!this.el || !this.getEditor()) {
26988 //this.getRawValue.defer(100,this);
26995 var value=this.getEditor().GetData();
26996 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26997 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27001 setSize : function(w,h) {
27005 //if (this.frame && this.frame.dom.style.display == 'none') {
27006 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27009 //if(!this.el || !this.getEditor()) {
27010 // this.setSize.defer(100,this, [w,h]);
27016 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27018 this.frame.dom.setAttribute('width', w);
27019 this.frame.dom.setAttribute('height', h);
27020 this.frame.setSize(w,h);
27024 toggleSourceEdit : function(value) {
27028 this.el.dom.style.display = value ? '' : 'none';
27029 this.frame.dom.style.display = value ? 'none' : '';
27034 focus: function(tag)
27036 if (this.frame.dom.style.display == 'none') {
27037 return Roo.form.FCKeditor.superclass.focus.call(this);
27039 if(!this.el || !this.getEditor()) {
27040 this.focus.defer(100,this, [tag]);
27047 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27048 this.getEditor().Focus();
27050 if (!this.getEditor().Selection.GetSelection()) {
27051 this.focus.defer(100,this, [tag]);
27056 var r = this.getEditor().EditorDocument.createRange();
27057 r.setStart(tgs[0],0);
27058 r.setEnd(tgs[0],0);
27059 this.getEditor().Selection.GetSelection().removeAllRanges();
27060 this.getEditor().Selection.GetSelection().addRange(r);
27061 this.getEditor().Focus();
27068 replaceTextarea : function()
27070 if ( document.getElementById( this.getId() + '___Frame' ) ) {
27073 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27075 // We must check the elements firstly using the Id and then the name.
27076 var oTextarea = document.getElementById( this.getId() );
27078 var colElementsByName = document.getElementsByName( this.getId() ) ;
27080 oTextarea.style.display = 'none' ;
27082 if ( oTextarea.tabIndex ) {
27083 this.TabIndex = oTextarea.tabIndex ;
27086 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27087 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27088 this.frame = Roo.get(this.getId() + '___Frame')
27091 _getConfigHtml : function()
27095 for ( var o in this.fckconfig ) {
27096 sConfig += sConfig.length > 0 ? '&' : '';
27097 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27100 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27104 _getIFrameHtml : function()
27106 var sFile = 'fckeditor.html' ;
27107 /* no idea what this is about..
27110 if ( (/fcksource=true/i).test( window.top.location.search ) )
27111 sFile = 'fckeditor.original.html' ;
27116 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27117 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
27120 var html = '<iframe id="' + this.getId() +
27121 '___Frame" src="' + sLink +
27122 '" width="' + this.width +
27123 '" height="' + this.height + '"' +
27124 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
27125 ' frameborder="0" scrolling="no"></iframe>' ;
27130 _insertHtmlBefore : function( html, element )
27132 if ( element.insertAdjacentHTML ) {
27134 element.insertAdjacentHTML( 'beforeBegin', html ) ;
27136 var oRange = document.createRange() ;
27137 oRange.setStartBefore( element ) ;
27138 var oFragment = oRange.createContextualFragment( html );
27139 element.parentNode.insertBefore( oFragment, element ) ;
27152 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27154 function FCKeditor_OnComplete(editorInstance){
27155 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27156 f.fckEditor = editorInstance;
27157 //console.log("loaded");
27158 f.fireEvent('editorinit', f, editorInstance);
27178 //<script type="text/javascript">
27180 * @class Roo.form.GridField
27181 * @extends Roo.form.Field
27182 * Embed a grid (or editable grid into a form)
27185 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
27187 * xgrid.store = Roo.data.Store
27188 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
27189 * xgrid.store.reader = Roo.data.JsonReader
27193 * Creates a new GridField
27194 * @param {Object} config Configuration options
27196 Roo.form.GridField = function(config){
27197 Roo.form.GridField.superclass.constructor.call(this, config);
27201 Roo.extend(Roo.form.GridField, Roo.form.Field, {
27203 * @cfg {Number} width - used to restrict width of grid..
27207 * @cfg {Number} height - used to restrict height of grid..
27211 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
27217 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27218 * {tag: "input", type: "checkbox", autocomplete: "off"})
27220 // defaultAutoCreate : { tag: 'div' },
27221 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
27223 * @cfg {String} addTitle Text to include for adding a title.
27227 onResize : function(){
27228 Roo.form.Field.superclass.onResize.apply(this, arguments);
27231 initEvents : function(){
27232 // Roo.form.Checkbox.superclass.initEvents.call(this);
27233 // has no events...
27238 getResizeEl : function(){
27242 getPositionEl : function(){
27247 onRender : function(ct, position){
27249 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
27250 var style = this.style;
27253 Roo.form.GridField.superclass.onRender.call(this, ct, position);
27254 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
27255 this.viewEl = this.wrap.createChild({ tag: 'div' });
27257 this.viewEl.applyStyles(style);
27260 this.viewEl.setWidth(this.width);
27263 this.viewEl.setHeight(this.height);
27265 //if(this.inputValue !== undefined){
27266 //this.setValue(this.value);
27269 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
27272 this.grid.render();
27273 this.grid.getDataSource().on('remove', this.refreshValue, this);
27274 this.grid.getDataSource().on('update', this.refreshValue, this);
27275 this.grid.on('afteredit', this.refreshValue, this);
27281 * Sets the value of the item.
27282 * @param {String} either an object or a string..
27284 setValue : function(v){
27286 v = v || []; // empty set..
27287 // this does not seem smart - it really only affects memoryproxy grids..
27288 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27289 var ds = this.grid.getDataSource();
27290 // assumes a json reader..
27292 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
27293 ds.loadData( data);
27295 // clear selection so it does not get stale.
27296 if (this.grid.sm) {
27297 this.grid.sm.clearSelections();
27300 Roo.form.GridField.superclass.setValue.call(this, v);
27301 this.refreshValue();
27302 // should load data in the grid really....
27306 refreshValue: function() {
27308 this.grid.getDataSource().each(function(r) {
27311 this.el.dom.value = Roo.encode(val);
27319 * Ext JS Library 1.1.1
27320 * Copyright(c) 2006-2007, Ext JS, LLC.
27322 * Originally Released Under LGPL - original licence link has changed is not relivant.
27325 * <script type="text/javascript">
27328 * @class Roo.form.DisplayField
27329 * @extends Roo.form.Field
27330 * A generic Field to display non-editable data.
27331 * @cfg {Boolean} closable (true|false) default false
27333 * Creates a new Display Field item.
27334 * @param {Object} config Configuration options
27336 Roo.form.DisplayField = function(config){
27337 Roo.form.DisplayField.superclass.constructor.call(this, config);
27342 * Fires after the click the close btn
27343 * @param {Roo.form.DisplayField} this
27349 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
27350 inputType: 'hidden',
27356 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27358 focusClass : undefined,
27360 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27362 fieldClass: 'x-form-field',
27365 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27367 valueRenderer: undefined,
27371 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27372 * {tag: "input", type: "checkbox", autocomplete: "off"})
27375 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27379 onResize : function(){
27380 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27384 initEvents : function(){
27385 // Roo.form.Checkbox.superclass.initEvents.call(this);
27386 // has no events...
27389 this.closeEl.on('click', this.onClose, this);
27395 getResizeEl : function(){
27399 getPositionEl : function(){
27404 onRender : function(ct, position){
27406 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27407 //if(this.inputValue !== undefined){
27408 this.wrap = this.el.wrap();
27410 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27413 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27416 if (this.bodyStyle) {
27417 this.viewEl.applyStyles(this.bodyStyle);
27419 //this.viewEl.setStyle('padding', '2px');
27421 this.setValue(this.value);
27426 initValue : Roo.emptyFn,
27431 onClick : function(){
27436 * Sets the checked state of the checkbox.
27437 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27439 setValue : function(v){
27441 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
27442 // this might be called before we have a dom element..
27443 if (!this.viewEl) {
27446 this.viewEl.dom.innerHTML = html;
27447 Roo.form.DisplayField.superclass.setValue.call(this, v);
27451 onClose : function(e)
27453 e.preventDefault();
27455 this.fireEvent('close', this);
27464 * @class Roo.form.DayPicker
27465 * @extends Roo.form.Field
27466 * A Day picker show [M] [T] [W] ....
27468 * Creates a new Day Picker
27469 * @param {Object} config Configuration options
27471 Roo.form.DayPicker= function(config){
27472 Roo.form.DayPicker.superclass.constructor.call(this, config);
27476 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
27478 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27480 focusClass : undefined,
27482 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27484 fieldClass: "x-form-field",
27487 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27488 * {tag: "input", type: "checkbox", autocomplete: "off"})
27490 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27493 actionMode : 'viewEl',
27497 inputType : 'hidden',
27500 inputElement: false, // real input element?
27501 basedOn: false, // ????
27503 isFormField: true, // not sure where this is needed!!!!
27505 onResize : function(){
27506 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27507 if(!this.boxLabel){
27508 this.el.alignTo(this.wrap, 'c-c');
27512 initEvents : function(){
27513 Roo.form.Checkbox.superclass.initEvents.call(this);
27514 this.el.on("click", this.onClick, this);
27515 this.el.on("change", this.onClick, this);
27519 getResizeEl : function(){
27523 getPositionEl : function(){
27529 onRender : function(ct, position){
27530 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27532 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27534 var r1 = '<table><tr>';
27535 var r2 = '<tr class="x-form-daypick-icons">';
27536 for (var i=0; i < 7; i++) {
27537 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27538 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
27541 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27542 viewEl.select('img').on('click', this.onClick, this);
27543 this.viewEl = viewEl;
27546 // this will not work on Chrome!!!
27547 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
27548 this.el.on('propertychange', this.setFromHidden, this); //ie
27556 initValue : Roo.emptyFn,
27559 * Returns the checked state of the checkbox.
27560 * @return {Boolean} True if checked, else false
27562 getValue : function(){
27563 return this.el.dom.value;
27568 onClick : function(e){
27569 //this.setChecked(!this.checked);
27570 Roo.get(e.target).toggleClass('x-menu-item-checked');
27571 this.refreshValue();
27572 //if(this.el.dom.checked != this.checked){
27573 // this.setValue(this.el.dom.checked);
27578 refreshValue : function()
27581 this.viewEl.select('img',true).each(function(e,i,n) {
27582 val += e.is(".x-menu-item-checked") ? String(n) : '';
27584 this.setValue(val, true);
27588 * Sets the checked state of the checkbox.
27589 * On is always based on a string comparison between inputValue and the param.
27590 * @param {Boolean/String} value - the value to set
27591 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27593 setValue : function(v,suppressEvent){
27594 if (!this.el.dom) {
27597 var old = this.el.dom.value ;
27598 this.el.dom.value = v;
27599 if (suppressEvent) {
27603 // update display..
27604 this.viewEl.select('img',true).each(function(e,i,n) {
27606 var on = e.is(".x-menu-item-checked");
27607 var newv = v.indexOf(String(n)) > -1;
27609 e.toggleClass('x-menu-item-checked');
27615 this.fireEvent('change', this, v, old);
27620 // handle setting of hidden value by some other method!!?!?
27621 setFromHidden: function()
27626 //console.log("SET FROM HIDDEN");
27627 //alert('setFrom hidden');
27628 this.setValue(this.el.dom.value);
27631 onDestroy : function()
27634 Roo.get(this.viewEl).remove();
27637 Roo.form.DayPicker.superclass.onDestroy.call(this);
27641 * RooJS Library 1.1.1
27642 * Copyright(c) 2008-2011 Alan Knowles
27649 * @class Roo.form.ComboCheck
27650 * @extends Roo.form.ComboBox
27651 * A combobox for multiple select items.
27653 * FIXME - could do with a reset button..
27656 * Create a new ComboCheck
27657 * @param {Object} config Configuration options
27659 Roo.form.ComboCheck = function(config){
27660 Roo.form.ComboCheck.superclass.constructor.call(this, config);
27661 // should verify some data...
27663 // hiddenName = required..
27664 // displayField = required
27665 // valudField == required
27666 var req= [ 'hiddenName', 'displayField', 'valueField' ];
27668 Roo.each(req, function(e) {
27669 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27670 throw "Roo.form.ComboCheck : missing value for: " + e;
27677 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27682 selectedClass: 'x-menu-item-checked',
27685 onRender : function(ct, position){
27691 var cls = 'x-combo-list';
27694 this.tpl = new Roo.Template({
27695 html : '<div class="'+cls+'-item x-menu-check-item">' +
27696 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
27697 '<span>{' + this.displayField + '}</span>' +
27704 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27705 this.view.singleSelect = false;
27706 this.view.multiSelect = true;
27707 this.view.toggleSelect = true;
27708 this.pageTb.add(new Roo.Toolbar.Fill(), {
27711 handler: function()
27718 onViewOver : function(e, t){
27724 onViewClick : function(doFocus,index){
27728 select: function () {
27729 //Roo.log("SELECT CALLED");
27732 selectByValue : function(xv, scrollIntoView){
27733 var ar = this.getValueArray();
27736 Roo.each(ar, function(v) {
27737 if(v === undefined || v === null){
27740 var r = this.findRecord(this.valueField, v);
27742 sels.push(this.store.indexOf(r))
27746 this.view.select(sels);
27752 onSelect : function(record, index){
27753 // Roo.log("onselect Called");
27754 // this is only called by the clear button now..
27755 this.view.clearSelections();
27756 this.setValue('[]');
27757 if (this.value != this.valueBefore) {
27758 this.fireEvent('change', this, this.value, this.valueBefore);
27759 this.valueBefore = this.value;
27762 getValueArray : function()
27767 //Roo.log(this.value);
27768 if (typeof(this.value) == 'undefined') {
27771 var ar = Roo.decode(this.value);
27772 return ar instanceof Array ? ar : []; //?? valid?
27775 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
27780 expand : function ()
27783 Roo.form.ComboCheck.superclass.expand.call(this);
27784 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27785 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27790 collapse : function(){
27791 Roo.form.ComboCheck.superclass.collapse.call(this);
27792 var sl = this.view.getSelectedIndexes();
27793 var st = this.store;
27797 Roo.each(sl, function(i) {
27799 nv.push(r.get(this.valueField));
27801 this.setValue(Roo.encode(nv));
27802 if (this.value != this.valueBefore) {
27804 this.fireEvent('change', this, this.value, this.valueBefore);
27805 this.valueBefore = this.value;
27810 setValue : function(v){
27814 var vals = this.getValueArray();
27816 Roo.each(vals, function(k) {
27817 var r = this.findRecord(this.valueField, k);
27819 tv.push(r.data[this.displayField]);
27820 }else if(this.valueNotFoundText !== undefined){
27821 tv.push( this.valueNotFoundText );
27826 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27827 this.hiddenField.value = v;
27833 * Ext JS Library 1.1.1
27834 * Copyright(c) 2006-2007, Ext JS, LLC.
27836 * Originally Released Under LGPL - original licence link has changed is not relivant.
27839 * <script type="text/javascript">
27843 * @class Roo.form.Signature
27844 * @extends Roo.form.Field
27848 * @param {Object} config Configuration options
27851 Roo.form.Signature = function(config){
27852 Roo.form.Signature.superclass.constructor.call(this, config);
27854 this.addEvents({// not in used??
27857 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27858 * @param {Roo.form.Signature} combo This combo box
27863 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27864 * @param {Roo.form.ComboBox} combo This combo box
27865 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27871 Roo.extend(Roo.form.Signature, Roo.form.Field, {
27873 * @cfg {Object} labels Label to use when rendering a form.
27877 * confirm : "Confirm"
27882 confirm : "Confirm"
27885 * @cfg {Number} width The signature panel width (defaults to 300)
27889 * @cfg {Number} height The signature panel height (defaults to 100)
27893 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27895 allowBlank : false,
27898 // {Object} signPanel The signature SVG panel element (defaults to {})
27900 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27901 isMouseDown : false,
27902 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27903 isConfirmed : false,
27904 // {String} signatureTmp SVG mapping string (defaults to empty string)
27908 defaultAutoCreate : { // modified by initCompnoent..
27914 onRender : function(ct, position){
27916 Roo.form.Signature.superclass.onRender.call(this, ct, position);
27918 this.wrap = this.el.wrap({
27919 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27922 this.createToolbar(this);
27923 this.signPanel = this.wrap.createChild({
27925 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27929 this.svgID = Roo.id();
27930 this.svgEl = this.signPanel.createChild({
27931 xmlns : 'http://www.w3.org/2000/svg',
27933 id : this.svgID + "-svg",
27935 height: this.height,
27936 viewBox: '0 0 '+this.width+' '+this.height,
27940 id: this.svgID + "-svg-r",
27942 height: this.height,
27947 id: this.svgID + "-svg-l",
27949 y1: (this.height*0.8), // start set the line in 80% of height
27950 x2: this.width, // end
27951 y2: (this.height*0.8), // end set the line in 80% of height
27953 'stroke-width': "1",
27954 'stroke-dasharray': "3",
27955 'shape-rendering': "crispEdges",
27956 'pointer-events': "none"
27960 id: this.svgID + "-svg-p",
27962 'stroke-width': "3",
27964 'pointer-events': 'none'
27969 this.svgBox = this.svgEl.dom.getScreenCTM();
27971 createSVG : function(){
27972 var svg = this.signPanel;
27973 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27976 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27977 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27978 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27979 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27980 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27981 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27982 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27985 isTouchEvent : function(e){
27986 return e.type.match(/^touch/);
27988 getCoords : function (e) {
27989 var pt = this.svgEl.dom.createSVGPoint();
27992 if (this.isTouchEvent(e)) {
27993 pt.x = e.targetTouches[0].clientX;
27994 pt.y = e.targetTouches[0].clientY;
27996 var a = this.svgEl.dom.getScreenCTM();
27997 var b = a.inverse();
27998 var mx = pt.matrixTransform(b);
27999 return mx.x + ',' + mx.y;
28001 //mouse event headler
28002 down : function (e) {
28003 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
28004 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
28006 this.isMouseDown = true;
28008 e.preventDefault();
28010 move : function (e) {
28011 if (this.isMouseDown) {
28012 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
28013 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
28016 e.preventDefault();
28018 up : function (e) {
28019 this.isMouseDown = false;
28020 var sp = this.signatureTmp.split(' ');
28023 if(!sp[sp.length-2].match(/^L/)){
28027 this.signatureTmp = sp.join(" ");
28030 if(this.getValue() != this.signatureTmp){
28031 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28032 this.isConfirmed = false;
28034 e.preventDefault();
28038 * Protected method that will not generally be called directly. It
28039 * is called when the editor creates its toolbar. Override this method if you need to
28040 * add custom toolbar buttons.
28041 * @param {HtmlEditor} editor
28043 createToolbar : function(editor){
28044 function btn(id, toggle, handler){
28045 var xid = fid + '-'+ id ;
28049 cls : 'x-btn-icon x-edit-'+id,
28050 enableToggle:toggle !== false,
28051 scope: editor, // was editor...
28052 handler:handler||editor.relayBtnCmd,
28053 clickEvent:'mousedown',
28054 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28060 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
28064 cls : ' x-signature-btn x-signature-'+id,
28065 scope: editor, // was editor...
28066 handler: this.reset,
28067 clickEvent:'mousedown',
28068 text: this.labels.clear
28075 cls : ' x-signature-btn x-signature-'+id,
28076 scope: editor, // was editor...
28077 handler: this.confirmHandler,
28078 clickEvent:'mousedown',
28079 text: this.labels.confirm
28086 * when user is clicked confirm then show this image.....
28088 * @return {String} Image Data URI
28090 getImageDataURI : function(){
28091 var svg = this.svgEl.dom.parentNode.innerHTML;
28092 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
28097 * @return {Boolean} this.isConfirmed
28099 getConfirmed : function(){
28100 return this.isConfirmed;
28104 * @return {Number} this.width
28106 getWidth : function(){
28111 * @return {Number} this.height
28113 getHeight : function(){
28114 return this.height;
28117 getSignature : function(){
28118 return this.signatureTmp;
28121 reset : function(){
28122 this.signatureTmp = '';
28123 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28124 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
28125 this.isConfirmed = false;
28126 Roo.form.Signature.superclass.reset.call(this);
28128 setSignature : function(s){
28129 this.signatureTmp = s;
28130 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28131 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
28133 this.isConfirmed = false;
28134 Roo.form.Signature.superclass.reset.call(this);
28137 // Roo.log(this.signPanel.dom.contentWindow.up())
28140 setConfirmed : function(){
28144 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
28147 confirmHandler : function(){
28148 if(!this.getSignature()){
28152 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
28153 this.setValue(this.getSignature());
28154 this.isConfirmed = true;
28156 this.fireEvent('confirm', this);
28159 // Subclasses should provide the validation implementation by overriding this
28160 validateValue : function(value){
28161 if(this.allowBlank){
28165 if(this.isConfirmed){
28172 * Ext JS Library 1.1.1
28173 * Copyright(c) 2006-2007, Ext JS, LLC.
28175 * Originally Released Under LGPL - original licence link has changed is not relivant.
28178 * <script type="text/javascript">
28183 * @class Roo.form.ComboBox
28184 * @extends Roo.form.TriggerField
28185 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
28187 * Create a new ComboBox.
28188 * @param {Object} config Configuration options
28190 Roo.form.Select = function(config){
28191 Roo.form.Select.superclass.constructor.call(this, config);
28195 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
28197 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
28200 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
28201 * rendering into an Roo.Editor, defaults to false)
28204 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
28205 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
28208 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
28211 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
28212 * the dropdown list (defaults to undefined, with no header element)
28216 * @cfg {String/Roo.Template} tpl The template to use to render the output
28220 defaultAutoCreate : {tag: "select" },
28222 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
28224 listWidth: undefined,
28226 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
28227 * mode = 'remote' or 'text' if mode = 'local')
28229 displayField: undefined,
28231 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
28232 * mode = 'remote' or 'value' if mode = 'local').
28233 * Note: use of a valueField requires the user make a selection
28234 * in order for a value to be mapped.
28236 valueField: undefined,
28240 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
28241 * field's data value (defaults to the underlying DOM element's name)
28243 hiddenName: undefined,
28245 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
28249 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
28251 selectedClass: 'x-combo-selected',
28253 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
28254 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
28255 * which displays a downward arrow icon).
28257 triggerClass : 'x-form-arrow-trigger',
28259 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28263 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
28264 * anchor positions (defaults to 'tl-bl')
28266 listAlign: 'tl-bl?',
28268 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
28272 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
28273 * query specified by the allQuery config option (defaults to 'query')
28275 triggerAction: 'query',
28277 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
28278 * (defaults to 4, does not apply if editable = false)
28282 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
28283 * delay (typeAheadDelay) if it matches a known value (defaults to false)
28287 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
28288 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
28292 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28293 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
28297 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
28298 * when editable = true (defaults to false)
28300 selectOnFocus:false,
28302 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28304 queryParam: 'query',
28306 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
28307 * when mode = 'remote' (defaults to 'Loading...')
28309 loadingText: 'Loading...',
28311 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28315 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28319 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28320 * traditional select (defaults to true)
28324 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28328 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28332 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28333 * listWidth has a higher value)
28337 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28338 * allow the user to set arbitrary text into the field (defaults to false)
28340 forceSelection:false,
28342 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28343 * if typeAhead = true (defaults to 250)
28345 typeAheadDelay : 250,
28347 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28348 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28350 valueNotFoundText : undefined,
28353 * @cfg {String} defaultValue The value displayed after loading the store.
28358 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28360 blockFocus : false,
28363 * @cfg {Boolean} disableClear Disable showing of clear button.
28365 disableClear : false,
28367 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
28369 alwaysQuery : false,
28375 // element that contains real text value.. (when hidden is used..)
28378 onRender : function(ct, position){
28379 Roo.form.Field.prototype.onRender.call(this, ct, position);
28382 this.store.on('beforeload', this.onBeforeLoad, this);
28383 this.store.on('load', this.onLoad, this);
28384 this.store.on('loadexception', this.onLoadException, this);
28385 this.store.load({});
28393 initEvents : function(){
28394 //Roo.form.ComboBox.superclass.initEvents.call(this);
28398 onDestroy : function(){
28401 this.store.un('beforeload', this.onBeforeLoad, this);
28402 this.store.un('load', this.onLoad, this);
28403 this.store.un('loadexception', this.onLoadException, this);
28405 //Roo.form.ComboBox.superclass.onDestroy.call(this);
28409 fireKey : function(e){
28410 if(e.isNavKeyPress() && !this.list.isVisible()){
28411 this.fireEvent("specialkey", this, e);
28416 onResize: function(w, h){
28424 * Allow or prevent the user from directly editing the field text. If false is passed,
28425 * the user will only be able to select from the items defined in the dropdown list. This method
28426 * is the runtime equivalent of setting the 'editable' config option at config time.
28427 * @param {Boolean} value True to allow the user to directly edit the field text
28429 setEditable : function(value){
28434 onBeforeLoad : function(){
28436 Roo.log("Select before load");
28439 this.innerList.update(this.loadingText ?
28440 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28441 //this.restrictHeight();
28442 this.selectedIndex = -1;
28446 onLoad : function(){
28449 var dom = this.el.dom;
28450 dom.innerHTML = '';
28451 var od = dom.ownerDocument;
28453 if (this.emptyText) {
28454 var op = od.createElement('option');
28455 op.setAttribute('value', '');
28456 op.innerHTML = String.format('{0}', this.emptyText);
28457 dom.appendChild(op);
28459 if(this.store.getCount() > 0){
28461 var vf = this.valueField;
28462 var df = this.displayField;
28463 this.store.data.each(function(r) {
28464 // which colmsn to use... testing - cdoe / title..
28465 var op = od.createElement('option');
28466 op.setAttribute('value', r.data[vf]);
28467 op.innerHTML = String.format('{0}', r.data[df]);
28468 dom.appendChild(op);
28470 if (typeof(this.defaultValue != 'undefined')) {
28471 this.setValue(this.defaultValue);
28476 //this.onEmptyResults();
28481 onLoadException : function()
28483 dom.innerHTML = '';
28485 Roo.log("Select on load exception");
28489 Roo.log(this.store.reader.jsonData);
28490 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28491 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28497 onTypeAhead : function(){
28502 onSelect : function(record, index){
28503 Roo.log('on select?');
28505 if(this.fireEvent('beforeselect', this, record, index) !== false){
28506 this.setFromData(index > -1 ? record.data : false);
28508 this.fireEvent('select', this, record, index);
28513 * Returns the currently selected field value or empty string if no value is set.
28514 * @return {String} value The selected value
28516 getValue : function(){
28517 var dom = this.el.dom;
28518 this.value = dom.options[dom.selectedIndex].value;
28524 * Clears any text/value currently set in the field
28526 clearValue : function(){
28528 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28533 * Sets the specified value into the field. If the value finds a match, the corresponding record text
28534 * will be displayed in the field. If the value does not match the data value of an existing item,
28535 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28536 * Otherwise the field will be blank (although the value will still be set).
28537 * @param {String} value The value to match
28539 setValue : function(v){
28540 var d = this.el.dom;
28541 for (var i =0; i < d.options.length;i++) {
28542 if (v == d.options[i].value) {
28543 d.selectedIndex = i;
28551 * @property {Object} the last set data for the element
28556 * Sets the value of the field based on a object which is related to the record format for the store.
28557 * @param {Object} value the value to set as. or false on reset?
28559 setFromData : function(o){
28560 Roo.log('setfrom data?');
28566 reset : function(){
28570 findRecord : function(prop, value){
28575 if(this.store.getCount() > 0){
28576 this.store.each(function(r){
28577 if(r.data[prop] == value){
28587 getName: function()
28589 // returns hidden if it's set..
28590 if (!this.rendered) {return ''};
28591 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
28599 onEmptyResults : function(){
28600 Roo.log('empty results');
28605 * Returns true if the dropdown list is expanded, else false.
28607 isExpanded : function(){
28612 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28613 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28614 * @param {String} value The data value of the item to select
28615 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28616 * selected item if it is not currently in view (defaults to true)
28617 * @return {Boolean} True if the value matched an item in the list, else false
28619 selectByValue : function(v, scrollIntoView){
28620 Roo.log('select By Value');
28623 if(v !== undefined && v !== null){
28624 var r = this.findRecord(this.valueField || this.displayField, v);
28626 this.select(this.store.indexOf(r), scrollIntoView);
28634 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28635 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28636 * @param {Number} index The zero-based index of the list item to select
28637 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28638 * selected item if it is not currently in view (defaults to true)
28640 select : function(index, scrollIntoView){
28641 Roo.log('select ');
28644 this.selectedIndex = index;
28645 this.view.select(index);
28646 if(scrollIntoView !== false){
28647 var el = this.view.getNode(index);
28649 this.innerList.scrollChildIntoView(el, false);
28657 validateBlur : function(){
28664 initQuery : function(){
28665 this.doQuery(this.getRawValue());
28669 doForce : function(){
28670 if(this.el.dom.value.length > 0){
28671 this.el.dom.value =
28672 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28678 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
28679 * query allowing the query action to be canceled if needed.
28680 * @param {String} query The SQL query to execute
28681 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28682 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
28683 * saved in the current store (defaults to false)
28685 doQuery : function(q, forceAll){
28687 Roo.log('doQuery?');
28688 if(q === undefined || q === null){
28693 forceAll: forceAll,
28697 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28701 forceAll = qe.forceAll;
28702 if(forceAll === true || (q.length >= this.minChars)){
28703 if(this.lastQuery != q || this.alwaysQuery){
28704 this.lastQuery = q;
28705 if(this.mode == 'local'){
28706 this.selectedIndex = -1;
28708 this.store.clearFilter();
28710 this.store.filter(this.displayField, q);
28714 this.store.baseParams[this.queryParam] = q;
28716 params: this.getParams(q)
28721 this.selectedIndex = -1;
28728 getParams : function(q){
28730 //p[this.queryParam] = q;
28733 p.limit = this.pageSize;
28739 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28741 collapse : function(){
28746 collapseIf : function(e){
28751 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28753 expand : function(){
28761 * @cfg {Boolean} grow
28765 * @cfg {Number} growMin
28769 * @cfg {Number} growMax
28777 setWidth : function()
28781 getResizeEl : function(){
28784 });//<script type="text/javasscript">
28788 * @class Roo.DDView
28789 * A DnD enabled version of Roo.View.
28790 * @param {Element/String} container The Element in which to create the View.
28791 * @param {String} tpl The template string used to create the markup for each element of the View
28792 * @param {Object} config The configuration properties. These include all the config options of
28793 * {@link Roo.View} plus some specific to this class.<br>
28795 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28796 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28798 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28799 .x-view-drag-insert-above {
28800 border-top:1px dotted #3366cc;
28802 .x-view-drag-insert-below {
28803 border-bottom:1px dotted #3366cc;
28809 Roo.DDView = function(container, tpl, config) {
28810 Roo.DDView.superclass.constructor.apply(this, arguments);
28811 this.getEl().setStyle("outline", "0px none");
28812 this.getEl().unselectable();
28813 if (this.dragGroup) {
28814 this.setDraggable(this.dragGroup.split(","));
28816 if (this.dropGroup) {
28817 this.setDroppable(this.dropGroup.split(","));
28819 if (this.deletable) {
28820 this.setDeletable();
28822 this.isDirtyFlag = false;
28828 Roo.extend(Roo.DDView, Roo.View, {
28829 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28830 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28831 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28832 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28836 reset: Roo.emptyFn,
28838 clearInvalid: Roo.form.Field.prototype.clearInvalid,
28840 validate: function() {
28844 destroy: function() {
28845 this.purgeListeners();
28846 this.getEl.removeAllListeners();
28847 this.getEl().remove();
28848 if (this.dragZone) {
28849 if (this.dragZone.destroy) {
28850 this.dragZone.destroy();
28853 if (this.dropZone) {
28854 if (this.dropZone.destroy) {
28855 this.dropZone.destroy();
28860 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28861 getName: function() {
28865 /** Loads the View from a JSON string representing the Records to put into the Store. */
28866 setValue: function(v) {
28868 throw "DDView.setValue(). DDView must be constructed with a valid Store";
28871 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28872 this.store.proxy = new Roo.data.MemoryProxy(data);
28876 /** @return {String} a parenthesised list of the ids of the Records in the View. */
28877 getValue: function() {
28879 this.store.each(function(rec) {
28880 result += rec.id + ',';
28882 return result.substr(0, result.length - 1) + ')';
28885 getIds: function() {
28886 var i = 0, result = new Array(this.store.getCount());
28887 this.store.each(function(rec) {
28888 result[i++] = rec.id;
28893 isDirty: function() {
28894 return this.isDirtyFlag;
28898 * Part of the Roo.dd.DropZone interface. If no target node is found, the
28899 * whole Element becomes the target, and this causes the drop gesture to append.
28901 getTargetFromEvent : function(e) {
28902 var target = e.getTarget();
28903 while ((target !== null) && (target.parentNode != this.el.dom)) {
28904 target = target.parentNode;
28907 target = this.el.dom.lastChild || this.el.dom;
28913 * Create the drag data which consists of an object which has the property "ddel" as
28914 * the drag proxy element.
28916 getDragData : function(e) {
28917 var target = this.findItemFromChild(e.getTarget());
28919 this.handleSelection(e);
28920 var selNodes = this.getSelectedNodes();
28923 copy: this.copy || (this.allowCopy && e.ctrlKey),
28927 var selectedIndices = this.getSelectedIndexes();
28928 for (var i = 0; i < selectedIndices.length; i++) {
28929 dragData.records.push(this.store.getAt(selectedIndices[i]));
28931 if (selNodes.length == 1) {
28932 dragData.ddel = target.cloneNode(true); // the div element
28934 var div = document.createElement('div'); // create the multi element drag "ghost"
28935 div.className = 'multi-proxy';
28936 for (var i = 0, len = selNodes.length; i < len; i++) {
28937 div.appendChild(selNodes[i].cloneNode(true));
28939 dragData.ddel = div;
28941 //console.log(dragData)
28942 //console.log(dragData.ddel.innerHTML)
28945 //console.log('nodragData')
28949 /** Specify to which ddGroup items in this DDView may be dragged. */
28950 setDraggable: function(ddGroup) {
28951 if (ddGroup instanceof Array) {
28952 Roo.each(ddGroup, this.setDraggable, this);
28955 if (this.dragZone) {
28956 this.dragZone.addToGroup(ddGroup);
28958 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28959 containerScroll: true,
28963 // Draggability implies selection. DragZone's mousedown selects the element.
28964 if (!this.multiSelect) { this.singleSelect = true; }
28966 // Wire the DragZone's handlers up to methods in *this*
28967 this.dragZone.getDragData = this.getDragData.createDelegate(this);
28971 /** Specify from which ddGroup this DDView accepts drops. */
28972 setDroppable: function(ddGroup) {
28973 if (ddGroup instanceof Array) {
28974 Roo.each(ddGroup, this.setDroppable, this);
28977 if (this.dropZone) {
28978 this.dropZone.addToGroup(ddGroup);
28980 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28981 containerScroll: true,
28985 // Wire the DropZone's handlers up to methods in *this*
28986 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28987 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28988 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28989 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28990 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28994 /** Decide whether to drop above or below a View node. */
28995 getDropPoint : function(e, n, dd){
28996 if (n == this.el.dom) { return "above"; }
28997 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28998 var c = t + (b - t) / 2;
28999 var y = Roo.lib.Event.getPageY(e);
29007 onNodeEnter : function(n, dd, e, data){
29011 onNodeOver : function(n, dd, e, data){
29012 var pt = this.getDropPoint(e, n, dd);
29013 // set the insert point style on the target node
29014 var dragElClass = this.dropNotAllowed;
29017 if (pt == "above"){
29018 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
29019 targetElClass = "x-view-drag-insert-above";
29021 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
29022 targetElClass = "x-view-drag-insert-below";
29024 if (this.lastInsertClass != targetElClass){
29025 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
29026 this.lastInsertClass = targetElClass;
29029 return dragElClass;
29032 onNodeOut : function(n, dd, e, data){
29033 this.removeDropIndicators(n);
29036 onNodeDrop : function(n, dd, e, data){
29037 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
29040 var pt = this.getDropPoint(e, n, dd);
29041 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
29042 if (pt == "below") { insertAt++; }
29043 for (var i = 0; i < data.records.length; i++) {
29044 var r = data.records[i];
29045 var dup = this.store.getById(r.id);
29046 if (dup && (dd != this.dragZone)) {
29047 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
29050 this.store.insert(insertAt++, r.copy());
29052 data.source.isDirtyFlag = true;
29054 this.store.insert(insertAt++, r);
29056 this.isDirtyFlag = true;
29059 this.dragZone.cachedTarget = null;
29063 removeDropIndicators : function(n){
29065 Roo.fly(n).removeClass([
29066 "x-view-drag-insert-above",
29067 "x-view-drag-insert-below"]);
29068 this.lastInsertClass = "_noclass";
29073 * Utility method. Add a delete option to the DDView's context menu.
29074 * @param {String} imageUrl The URL of the "delete" icon image.
29076 setDeletable: function(imageUrl) {
29077 if (!this.singleSelect && !this.multiSelect) {
29078 this.singleSelect = true;
29080 var c = this.getContextMenu();
29081 this.contextMenu.on("itemclick", function(item) {
29084 this.remove(this.getSelectedIndexes());
29088 this.contextMenu.add({
29095 /** Return the context menu for this DDView. */
29096 getContextMenu: function() {
29097 if (!this.contextMenu) {
29098 // Create the View's context menu
29099 this.contextMenu = new Roo.menu.Menu({
29100 id: this.id + "-contextmenu"
29102 this.el.on("contextmenu", this.showContextMenu, this);
29104 return this.contextMenu;
29107 disableContextMenu: function() {
29108 if (this.contextMenu) {
29109 this.el.un("contextmenu", this.showContextMenu, this);
29113 showContextMenu: function(e, item) {
29114 item = this.findItemFromChild(e.getTarget());
29117 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
29118 this.contextMenu.showAt(e.getXY());
29123 * Remove {@link Roo.data.Record}s at the specified indices.
29124 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
29126 remove: function(selectedIndices) {
29127 selectedIndices = [].concat(selectedIndices);
29128 for (var i = 0; i < selectedIndices.length; i++) {
29129 var rec = this.store.getAt(selectedIndices[i]);
29130 this.store.remove(rec);
29135 * Double click fires the event, but also, if this is draggable, and there is only one other
29136 * related DropZone, it transfers the selected node.
29138 onDblClick : function(e){
29139 var item = this.findItemFromChild(e.getTarget());
29141 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
29144 if (this.dragGroup) {
29145 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
29146 while (targets.indexOf(this.dropZone) > -1) {
29147 targets.remove(this.dropZone);
29149 if (targets.length == 1) {
29150 this.dragZone.cachedTarget = null;
29151 var el = Roo.get(targets[0].getEl());
29152 var box = el.getBox(true);
29153 targets[0].onNodeDrop(el.dom, {
29155 xy: [box.x, box.y + box.height - 1]
29156 }, null, this.getDragData(e));
29162 handleSelection: function(e) {
29163 this.dragZone.cachedTarget = null;
29164 var item = this.findItemFromChild(e.getTarget());
29166 this.clearSelections(true);
29169 if (item && (this.multiSelect || this.singleSelect)){
29170 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
29171 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
29172 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
29173 this.unselect(item);
29175 this.select(item, this.multiSelect && e.ctrlKey);
29176 this.lastSelection = item;
29181 onItemClick : function(item, index, e){
29182 if(this.fireEvent("beforeclick", this, index, item, e) === false){
29188 unselect : function(nodeInfo, suppressEvent){
29189 var node = this.getNode(nodeInfo);
29190 if(node && this.isSelected(node)){
29191 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
29192 Roo.fly(node).removeClass(this.selectedClass);
29193 this.selections.remove(node);
29194 if(!suppressEvent){
29195 this.fireEvent("selectionchange", this, this.selections);
29203 * Ext JS Library 1.1.1
29204 * Copyright(c) 2006-2007, Ext JS, LLC.
29206 * Originally Released Under LGPL - original licence link has changed is not relivant.
29209 * <script type="text/javascript">
29213 * @class Roo.LayoutManager
29214 * @extends Roo.util.Observable
29215 * Base class for layout managers.
29217 Roo.LayoutManager = function(container, config){
29218 Roo.LayoutManager.superclass.constructor.call(this);
29219 this.el = Roo.get(container);
29220 // ie scrollbar fix
29221 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
29222 document.body.scroll = "no";
29223 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
29224 this.el.position('relative');
29226 this.id = this.el.id;
29227 this.el.addClass("x-layout-container");
29228 /** false to disable window resize monitoring @type Boolean */
29229 this.monitorWindowResize = true;
29234 * Fires when a layout is performed.
29235 * @param {Roo.LayoutManager} this
29239 * @event regionresized
29240 * Fires when the user resizes a region.
29241 * @param {Roo.LayoutRegion} region The resized region
29242 * @param {Number} newSize The new size (width for east/west, height for north/south)
29244 "regionresized" : true,
29246 * @event regioncollapsed
29247 * Fires when a region is collapsed.
29248 * @param {Roo.LayoutRegion} region The collapsed region
29250 "regioncollapsed" : true,
29252 * @event regionexpanded
29253 * Fires when a region is expanded.
29254 * @param {Roo.LayoutRegion} region The expanded region
29256 "regionexpanded" : true
29258 this.updating = false;
29259 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
29262 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
29264 * Returns true if this layout is currently being updated
29265 * @return {Boolean}
29267 isUpdating : function(){
29268 return this.updating;
29272 * Suspend the LayoutManager from doing auto-layouts while
29273 * making multiple add or remove calls
29275 beginUpdate : function(){
29276 this.updating = true;
29280 * Restore auto-layouts and optionally disable the manager from performing a layout
29281 * @param {Boolean} noLayout true to disable a layout update
29283 endUpdate : function(noLayout){
29284 this.updating = false;
29290 layout: function(){
29294 onRegionResized : function(region, newSize){
29295 this.fireEvent("regionresized", region, newSize);
29299 onRegionCollapsed : function(region){
29300 this.fireEvent("regioncollapsed", region);
29303 onRegionExpanded : function(region){
29304 this.fireEvent("regionexpanded", region);
29308 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29309 * performs box-model adjustments.
29310 * @return {Object} The size as an object {width: (the width), height: (the height)}
29312 getViewSize : function(){
29314 if(this.el.dom != document.body){
29315 size = this.el.getSize();
29317 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29319 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29320 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29325 * Returns the Element this layout is bound to.
29326 * @return {Roo.Element}
29328 getEl : function(){
29333 * Returns the specified region.
29334 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29335 * @return {Roo.LayoutRegion}
29337 getRegion : function(target){
29338 return this.regions[target.toLowerCase()];
29341 onWindowResize : function(){
29342 if(this.monitorWindowResize){
29348 * Ext JS Library 1.1.1
29349 * Copyright(c) 2006-2007, Ext JS, LLC.
29351 * Originally Released Under LGPL - original licence link has changed is not relivant.
29354 * <script type="text/javascript">
29357 * @class Roo.BorderLayout
29358 * @extends Roo.LayoutManager
29359 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29360 * please see: <br><br>
29361 * <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>
29362 * <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>
29365 var layout = new Roo.BorderLayout(document.body, {
29399 preferredTabWidth: 150
29404 var CP = Roo.ContentPanel;
29406 layout.beginUpdate();
29407 layout.add("north", new CP("north", "North"));
29408 layout.add("south", new CP("south", {title: "South", closable: true}));
29409 layout.add("west", new CP("west", {title: "West"}));
29410 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29411 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29412 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29413 layout.getRegion("center").showPanel("center1");
29414 layout.endUpdate();
29417 <b>The container the layout is rendered into can be either the body element or any other element.
29418 If it is not the body element, the container needs to either be an absolute positioned element,
29419 or you will need to add "position:relative" to the css of the container. You will also need to specify
29420 the container size if it is not the body element.</b>
29423 * Create a new BorderLayout
29424 * @param {String/HTMLElement/Element} container The container this layout is bound to
29425 * @param {Object} config Configuration options
29427 Roo.BorderLayout = function(container, config){
29428 config = config || {};
29429 Roo.BorderLayout.superclass.constructor.call(this, container, config);
29430 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29431 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29432 var target = this.factory.validRegions[i];
29433 if(config[target]){
29434 this.addRegion(target, config[target]);
29439 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29441 * Creates and adds a new region if it doesn't already exist.
29442 * @param {String} target The target region key (north, south, east, west or center).
29443 * @param {Object} config The regions config object
29444 * @return {BorderLayoutRegion} The new region
29446 addRegion : function(target, config){
29447 if(!this.regions[target]){
29448 var r = this.factory.create(target, this, config);
29449 this.bindRegion(target, r);
29451 return this.regions[target];
29455 bindRegion : function(name, r){
29456 this.regions[name] = r;
29457 r.on("visibilitychange", this.layout, this);
29458 r.on("paneladded", this.layout, this);
29459 r.on("panelremoved", this.layout, this);
29460 r.on("invalidated", this.layout, this);
29461 r.on("resized", this.onRegionResized, this);
29462 r.on("collapsed", this.onRegionCollapsed, this);
29463 r.on("expanded", this.onRegionExpanded, this);
29467 * Performs a layout update.
29469 layout : function(){
29470 if(this.updating) {
29473 var size = this.getViewSize();
29474 var w = size.width;
29475 var h = size.height;
29480 //var x = 0, y = 0;
29482 var rs = this.regions;
29483 var north = rs["north"];
29484 var south = rs["south"];
29485 var west = rs["west"];
29486 var east = rs["east"];
29487 var center = rs["center"];
29488 //if(this.hideOnLayout){ // not supported anymore
29489 //c.el.setStyle("display", "none");
29491 if(north && north.isVisible()){
29492 var b = north.getBox();
29493 var m = north.getMargins();
29494 b.width = w - (m.left+m.right);
29497 centerY = b.height + b.y + m.bottom;
29498 centerH -= centerY;
29499 north.updateBox(this.safeBox(b));
29501 if(south && south.isVisible()){
29502 var b = south.getBox();
29503 var m = south.getMargins();
29504 b.width = w - (m.left+m.right);
29506 var totalHeight = (b.height + m.top + m.bottom);
29507 b.y = h - totalHeight + m.top;
29508 centerH -= totalHeight;
29509 south.updateBox(this.safeBox(b));
29511 if(west && west.isVisible()){
29512 var b = west.getBox();
29513 var m = west.getMargins();
29514 b.height = centerH - (m.top+m.bottom);
29516 b.y = centerY + m.top;
29517 var totalWidth = (b.width + m.left + m.right);
29518 centerX += totalWidth;
29519 centerW -= totalWidth;
29520 west.updateBox(this.safeBox(b));
29522 if(east && east.isVisible()){
29523 var b = east.getBox();
29524 var m = east.getMargins();
29525 b.height = centerH - (m.top+m.bottom);
29526 var totalWidth = (b.width + m.left + m.right);
29527 b.x = w - totalWidth + m.left;
29528 b.y = centerY + m.top;
29529 centerW -= totalWidth;
29530 east.updateBox(this.safeBox(b));
29533 var m = center.getMargins();
29535 x: centerX + m.left,
29536 y: centerY + m.top,
29537 width: centerW - (m.left+m.right),
29538 height: centerH - (m.top+m.bottom)
29540 //if(this.hideOnLayout){
29541 //center.el.setStyle("display", "block");
29543 center.updateBox(this.safeBox(centerBox));
29546 this.fireEvent("layout", this);
29550 safeBox : function(box){
29551 box.width = Math.max(0, box.width);
29552 box.height = Math.max(0, box.height);
29557 * Adds a ContentPanel (or subclass) to this layout.
29558 * @param {String} target The target region key (north, south, east, west or center).
29559 * @param {Roo.ContentPanel} panel The panel to add
29560 * @return {Roo.ContentPanel} The added panel
29562 add : function(target, panel){
29564 target = target.toLowerCase();
29565 return this.regions[target].add(panel);
29569 * Remove a ContentPanel (or subclass) to this layout.
29570 * @param {String} target The target region key (north, south, east, west or center).
29571 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29572 * @return {Roo.ContentPanel} The removed panel
29574 remove : function(target, panel){
29575 target = target.toLowerCase();
29576 return this.regions[target].remove(panel);
29580 * Searches all regions for a panel with the specified id
29581 * @param {String} panelId
29582 * @return {Roo.ContentPanel} The panel or null if it wasn't found
29584 findPanel : function(panelId){
29585 var rs = this.regions;
29586 for(var target in rs){
29587 if(typeof rs[target] != "function"){
29588 var p = rs[target].getPanel(panelId);
29598 * Searches all regions for a panel with the specified id and activates (shows) it.
29599 * @param {String/ContentPanel} panelId The panels id or the panel itself
29600 * @return {Roo.ContentPanel} The shown panel or null
29602 showPanel : function(panelId) {
29603 var rs = this.regions;
29604 for(var target in rs){
29605 var r = rs[target];
29606 if(typeof r != "function"){
29607 if(r.hasPanel(panelId)){
29608 return r.showPanel(panelId);
29616 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29617 * @param {Roo.state.Provider} provider (optional) An alternate state provider
29619 restoreState : function(provider){
29621 provider = Roo.state.Manager;
29623 var sm = new Roo.LayoutStateManager();
29624 sm.init(this, provider);
29628 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
29629 * object should contain properties for each region to add ContentPanels to, and each property's value should be
29630 * a valid ContentPanel config object. Example:
29632 // Create the main layout
29633 var layout = new Roo.BorderLayout('main-ct', {
29644 // Create and add multiple ContentPanels at once via configs
29647 id: 'source-files',
29649 title:'Ext Source Files',
29662 * @param {Object} regions An object containing ContentPanel configs by region name
29664 batchAdd : function(regions){
29665 this.beginUpdate();
29666 for(var rname in regions){
29667 var lr = this.regions[rname];
29669 this.addTypedPanels(lr, regions[rname]);
29676 addTypedPanels : function(lr, ps){
29677 if(typeof ps == 'string'){
29678 lr.add(new Roo.ContentPanel(ps));
29680 else if(ps instanceof Array){
29681 for(var i =0, len = ps.length; i < len; i++){
29682 this.addTypedPanels(lr, ps[i]);
29685 else if(!ps.events){ // raw config?
29687 delete ps.el; // prevent conflict
29688 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29690 else { // panel object assumed!
29695 * Adds a xtype elements to the layout.
29699 xtype : 'ContentPanel',
29706 xtype : 'NestedLayoutPanel',
29712 items : [ ... list of content panels or nested layout panels.. ]
29716 * @param {Object} cfg Xtype definition of item to add.
29718 addxtype : function(cfg)
29720 // basically accepts a pannel...
29721 // can accept a layout region..!?!?
29722 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29724 if (!cfg.xtype.match(/Panel$/)) {
29729 if (typeof(cfg.region) == 'undefined') {
29730 Roo.log("Failed to add Panel, region was not set");
29734 var region = cfg.region;
29740 xitems = cfg.items;
29747 case 'ContentPanel': // ContentPanel (el, cfg)
29748 case 'ScrollPanel': // ContentPanel (el, cfg)
29750 if(cfg.autoCreate) {
29751 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29753 var el = this.el.createChild();
29754 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29757 this.add(region, ret);
29761 case 'TreePanel': // our new panel!
29762 cfg.el = this.el.createChild();
29763 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29764 this.add(region, ret);
29767 case 'NestedLayoutPanel':
29768 // create a new Layout (which is a Border Layout...
29769 var el = this.el.createChild();
29770 var clayout = cfg.layout;
29772 clayout.items = clayout.items || [];
29773 // replace this exitems with the clayout ones..
29774 xitems = clayout.items;
29777 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29778 cfg.background = false;
29780 var layout = new Roo.BorderLayout(el, clayout);
29782 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29783 //console.log('adding nested layout panel ' + cfg.toSource());
29784 this.add(region, ret);
29785 nb = {}; /// find first...
29790 // needs grid and region
29792 //var el = this.getRegion(region).el.createChild();
29793 var el = this.el.createChild();
29794 // create the grid first...
29796 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29798 if (region == 'center' && this.active ) {
29799 cfg.background = false;
29801 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29803 this.add(region, ret);
29804 if (cfg.background) {
29805 ret.on('activate', function(gp) {
29806 if (!gp.grid.rendered) {
29821 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29823 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29824 this.add(region, ret);
29827 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29831 // GridPanel (grid, cfg)
29834 this.beginUpdate();
29838 Roo.each(xitems, function(i) {
29839 region = nb && i.region ? i.region : false;
29841 var add = ret.addxtype(i);
29844 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29845 if (!i.background) {
29846 abn[region] = nb[region] ;
29853 // make the last non-background panel active..
29854 //if (nb) { Roo.log(abn); }
29857 for(var r in abn) {
29858 region = this.getRegion(r);
29860 // tried using nb[r], but it does not work..
29862 region.showPanel(abn[r]);
29873 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29874 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
29875 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29876 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
29879 var CP = Roo.ContentPanel;
29881 var layout = Roo.BorderLayout.create({
29885 panels: [new CP("north", "North")]
29894 panels: [new CP("west", {title: "West"})]
29903 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29912 panels: [new CP("south", {title: "South", closable: true})]
29919 preferredTabWidth: 150,
29921 new CP("center1", {title: "Close Me", closable: true}),
29922 new CP("center2", {title: "Center Panel", closable: false})
29927 layout.getRegion("center").showPanel("center1");
29932 Roo.BorderLayout.create = function(config, targetEl){
29933 var layout = new Roo.BorderLayout(targetEl || document.body, config);
29934 layout.beginUpdate();
29935 var regions = Roo.BorderLayout.RegionFactory.validRegions;
29936 for(var j = 0, jlen = regions.length; j < jlen; j++){
29937 var lr = regions[j];
29938 if(layout.regions[lr] && config[lr].panels){
29939 var r = layout.regions[lr];
29940 var ps = config[lr].panels;
29941 layout.addTypedPanels(r, ps);
29944 layout.endUpdate();
29949 Roo.BorderLayout.RegionFactory = {
29951 validRegions : ["north","south","east","west","center"],
29954 create : function(target, mgr, config){
29955 target = target.toLowerCase();
29956 if(config.lightweight || config.basic){
29957 return new Roo.BasicLayoutRegion(mgr, config, target);
29961 return new Roo.NorthLayoutRegion(mgr, config);
29963 return new Roo.SouthLayoutRegion(mgr, config);
29965 return new Roo.EastLayoutRegion(mgr, config);
29967 return new Roo.WestLayoutRegion(mgr, config);
29969 return new Roo.CenterLayoutRegion(mgr, config);
29971 throw 'Layout region "'+target+'" not supported.';
29975 * Ext JS Library 1.1.1
29976 * Copyright(c) 2006-2007, Ext JS, LLC.
29978 * Originally Released Under LGPL - original licence link has changed is not relivant.
29981 * <script type="text/javascript">
29985 * @class Roo.BasicLayoutRegion
29986 * @extends Roo.util.Observable
29987 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29988 * and does not have a titlebar, tabs or any other features. All it does is size and position
29989 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29991 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29993 this.position = pos;
29996 * @scope Roo.BasicLayoutRegion
30000 * @event beforeremove
30001 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
30002 * @param {Roo.LayoutRegion} this
30003 * @param {Roo.ContentPanel} panel The panel
30004 * @param {Object} e The cancel event object
30006 "beforeremove" : true,
30008 * @event invalidated
30009 * Fires when the layout for this region is changed.
30010 * @param {Roo.LayoutRegion} this
30012 "invalidated" : true,
30014 * @event visibilitychange
30015 * Fires when this region is shown or hidden
30016 * @param {Roo.LayoutRegion} this
30017 * @param {Boolean} visibility true or false
30019 "visibilitychange" : true,
30021 * @event paneladded
30022 * Fires when a panel is added.
30023 * @param {Roo.LayoutRegion} this
30024 * @param {Roo.ContentPanel} panel The panel
30026 "paneladded" : true,
30028 * @event panelremoved
30029 * Fires when a panel is removed.
30030 * @param {Roo.LayoutRegion} this
30031 * @param {Roo.ContentPanel} panel The panel
30033 "panelremoved" : true,
30035 * @event beforecollapse
30036 * Fires when this region before collapse.
30037 * @param {Roo.LayoutRegion} this
30039 "beforecollapse" : true,
30042 * Fires when this region is collapsed.
30043 * @param {Roo.LayoutRegion} this
30045 "collapsed" : true,
30048 * Fires when this region is expanded.
30049 * @param {Roo.LayoutRegion} this
30054 * Fires when this region is slid into view.
30055 * @param {Roo.LayoutRegion} this
30057 "slideshow" : true,
30060 * Fires when this region slides out of view.
30061 * @param {Roo.LayoutRegion} this
30063 "slidehide" : true,
30065 * @event panelactivated
30066 * Fires when a panel is activated.
30067 * @param {Roo.LayoutRegion} this
30068 * @param {Roo.ContentPanel} panel The activated panel
30070 "panelactivated" : true,
30073 * Fires when the user resizes this region.
30074 * @param {Roo.LayoutRegion} this
30075 * @param {Number} newSize The new size (width for east/west, height for north/south)
30079 /** A collection of panels in this region. @type Roo.util.MixedCollection */
30080 this.panels = new Roo.util.MixedCollection();
30081 this.panels.getKey = this.getPanelId.createDelegate(this);
30083 this.activePanel = null;
30084 // ensure listeners are added...
30086 if (config.listeners || config.events) {
30087 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
30088 listeners : config.listeners || {},
30089 events : config.events || {}
30093 if(skipConfig !== true){
30094 this.applyConfig(config);
30098 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
30099 getPanelId : function(p){
30103 applyConfig : function(config){
30104 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30105 this.config = config;
30110 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
30111 * the width, for horizontal (north, south) the height.
30112 * @param {Number} newSize The new width or height
30114 resizeTo : function(newSize){
30115 var el = this.el ? this.el :
30116 (this.activePanel ? this.activePanel.getEl() : null);
30118 switch(this.position){
30121 el.setWidth(newSize);
30122 this.fireEvent("resized", this, newSize);
30126 el.setHeight(newSize);
30127 this.fireEvent("resized", this, newSize);
30133 getBox : function(){
30134 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
30137 getMargins : function(){
30138 return this.margins;
30141 updateBox : function(box){
30143 var el = this.activePanel.getEl();
30144 el.dom.style.left = box.x + "px";
30145 el.dom.style.top = box.y + "px";
30146 this.activePanel.setSize(box.width, box.height);
30150 * Returns the container element for this region.
30151 * @return {Roo.Element}
30153 getEl : function(){
30154 return this.activePanel;
30158 * Returns true if this region is currently visible.
30159 * @return {Boolean}
30161 isVisible : function(){
30162 return this.activePanel ? true : false;
30165 setActivePanel : function(panel){
30166 panel = this.getPanel(panel);
30167 if(this.activePanel && this.activePanel != panel){
30168 this.activePanel.setActiveState(false);
30169 this.activePanel.getEl().setLeftTop(-10000,-10000);
30171 this.activePanel = panel;
30172 panel.setActiveState(true);
30174 panel.setSize(this.box.width, this.box.height);
30176 this.fireEvent("panelactivated", this, panel);
30177 this.fireEvent("invalidated");
30181 * Show the specified panel.
30182 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
30183 * @return {Roo.ContentPanel} The shown panel or null
30185 showPanel : function(panel){
30186 if(panel = this.getPanel(panel)){
30187 this.setActivePanel(panel);
30193 * Get the active panel for this region.
30194 * @return {Roo.ContentPanel} The active panel or null
30196 getActivePanel : function(){
30197 return this.activePanel;
30201 * Add the passed ContentPanel(s)
30202 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30203 * @return {Roo.ContentPanel} The panel added (if only one was added)
30205 add : function(panel){
30206 if(arguments.length > 1){
30207 for(var i = 0, len = arguments.length; i < len; i++) {
30208 this.add(arguments[i]);
30212 if(this.hasPanel(panel)){
30213 this.showPanel(panel);
30216 var el = panel.getEl();
30217 if(el.dom.parentNode != this.mgr.el.dom){
30218 this.mgr.el.dom.appendChild(el.dom);
30220 if(panel.setRegion){
30221 panel.setRegion(this);
30223 this.panels.add(panel);
30224 el.setStyle("position", "absolute");
30225 if(!panel.background){
30226 this.setActivePanel(panel);
30227 if(this.config.initialSize && this.panels.getCount()==1){
30228 this.resizeTo(this.config.initialSize);
30231 this.fireEvent("paneladded", this, panel);
30236 * Returns true if the panel is in this region.
30237 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30238 * @return {Boolean}
30240 hasPanel : function(panel){
30241 if(typeof panel == "object"){ // must be panel obj
30242 panel = panel.getId();
30244 return this.getPanel(panel) ? true : false;
30248 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30249 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30250 * @param {Boolean} preservePanel Overrides the config preservePanel option
30251 * @return {Roo.ContentPanel} The panel that was removed
30253 remove : function(panel, preservePanel){
30254 panel = this.getPanel(panel);
30259 this.fireEvent("beforeremove", this, panel, e);
30260 if(e.cancel === true){
30263 var panelId = panel.getId();
30264 this.panels.removeKey(panelId);
30269 * Returns the panel specified or null if it's not in this region.
30270 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30271 * @return {Roo.ContentPanel}
30273 getPanel : function(id){
30274 if(typeof id == "object"){ // must be panel obj
30277 return this.panels.get(id);
30281 * Returns this regions position (north/south/east/west/center).
30284 getPosition: function(){
30285 return this.position;
30289 * Ext JS Library 1.1.1
30290 * Copyright(c) 2006-2007, Ext JS, LLC.
30292 * Originally Released Under LGPL - original licence link has changed is not relivant.
30295 * <script type="text/javascript">
30299 * @class Roo.LayoutRegion
30300 * @extends Roo.BasicLayoutRegion
30301 * This class represents a region in a layout manager.
30302 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
30303 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
30304 * @cfg {Boolean} floatable False to disable floating (defaults to true)
30305 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30306 * @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})
30307 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
30308 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
30309 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
30310 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
30311 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
30312 * @cfg {String} title The title for the region (overrides panel titles)
30313 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
30314 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30315 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
30316 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30317 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
30318 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30319 * the space available, similar to FireFox 1.5 tabs (defaults to false)
30320 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
30321 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
30322 * @cfg {Boolean} showPin True to show a pin button
30323 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
30324 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
30325 * @cfg {Boolean} disableTabTips True to disable tab tooltips
30326 * @cfg {Number} width For East/West panels
30327 * @cfg {Number} height For North/South panels
30328 * @cfg {Boolean} split To show the splitter
30329 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
30331 Roo.LayoutRegion = function(mgr, config, pos){
30332 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30333 var dh = Roo.DomHelper;
30334 /** This region's container element
30335 * @type Roo.Element */
30336 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30337 /** This region's title element
30338 * @type Roo.Element */
30340 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30341 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
30342 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30344 this.titleEl.enableDisplayMode();
30345 /** This region's title text element
30346 * @type HTMLElement */
30347 this.titleTextEl = this.titleEl.dom.firstChild;
30348 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30349 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30350 this.closeBtn.enableDisplayMode();
30351 this.closeBtn.on("click", this.closeClicked, this);
30352 this.closeBtn.hide();
30354 this.createBody(config);
30355 this.visible = true;
30356 this.collapsed = false;
30358 if(config.hideWhenEmpty){
30360 this.on("paneladded", this.validateVisibility, this);
30361 this.on("panelremoved", this.validateVisibility, this);
30363 this.applyConfig(config);
30366 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30368 createBody : function(){
30369 /** This region's body element
30370 * @type Roo.Element */
30371 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30374 applyConfig : function(c){
30375 if(c.collapsible && this.position != "center" && !this.collapsedEl){
30376 var dh = Roo.DomHelper;
30377 if(c.titlebar !== false){
30378 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30379 this.collapseBtn.on("click", this.collapse, this);
30380 this.collapseBtn.enableDisplayMode();
30382 if(c.showPin === true || this.showPin){
30383 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30384 this.stickBtn.enableDisplayMode();
30385 this.stickBtn.on("click", this.expand, this);
30386 this.stickBtn.hide();
30389 /** This region's collapsed element
30390 * @type Roo.Element */
30391 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30392 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30394 if(c.floatable !== false){
30395 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30396 this.collapsedEl.on("click", this.collapseClick, this);
30399 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30400 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30401 id: "message", unselectable: "on", style:{"float":"left"}});
30402 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30404 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30405 this.expandBtn.on("click", this.expand, this);
30407 if(this.collapseBtn){
30408 this.collapseBtn.setVisible(c.collapsible == true);
30410 this.cmargins = c.cmargins || this.cmargins ||
30411 (this.position == "west" || this.position == "east" ?
30412 {top: 0, left: 2, right:2, bottom: 0} :
30413 {top: 2, left: 0, right:0, bottom: 2});
30414 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30415 this.bottomTabs = c.tabPosition != "top";
30416 this.autoScroll = c.autoScroll || false;
30417 if(this.autoScroll){
30418 this.bodyEl.setStyle("overflow", "auto");
30420 this.bodyEl.setStyle("overflow", "hidden");
30422 //if(c.titlebar !== false){
30423 if((!c.titlebar && !c.title) || c.titlebar === false){
30424 this.titleEl.hide();
30426 this.titleEl.show();
30428 this.titleTextEl.innerHTML = c.title;
30432 this.duration = c.duration || .30;
30433 this.slideDuration = c.slideDuration || .45;
30436 this.collapse(true);
30443 * Returns true if this region is currently visible.
30444 * @return {Boolean}
30446 isVisible : function(){
30447 return this.visible;
30451 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30452 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
30454 setCollapsedTitle : function(title){
30455 title = title || " ";
30456 if(this.collapsedTitleTextEl){
30457 this.collapsedTitleTextEl.innerHTML = title;
30461 getBox : function(){
30463 if(!this.collapsed){
30464 b = this.el.getBox(false, true);
30466 b = this.collapsedEl.getBox(false, true);
30471 getMargins : function(){
30472 return this.collapsed ? this.cmargins : this.margins;
30475 highlight : function(){
30476 this.el.addClass("x-layout-panel-dragover");
30479 unhighlight : function(){
30480 this.el.removeClass("x-layout-panel-dragover");
30483 updateBox : function(box){
30485 if(!this.collapsed){
30486 this.el.dom.style.left = box.x + "px";
30487 this.el.dom.style.top = box.y + "px";
30488 this.updateBody(box.width, box.height);
30490 this.collapsedEl.dom.style.left = box.x + "px";
30491 this.collapsedEl.dom.style.top = box.y + "px";
30492 this.collapsedEl.setSize(box.width, box.height);
30495 this.tabs.autoSizeTabs();
30499 updateBody : function(w, h){
30501 this.el.setWidth(w);
30502 w -= this.el.getBorderWidth("rl");
30503 if(this.config.adjustments){
30504 w += this.config.adjustments[0];
30508 this.el.setHeight(h);
30509 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30510 h -= this.el.getBorderWidth("tb");
30511 if(this.config.adjustments){
30512 h += this.config.adjustments[1];
30514 this.bodyEl.setHeight(h);
30516 h = this.tabs.syncHeight(h);
30519 if(this.panelSize){
30520 w = w !== null ? w : this.panelSize.width;
30521 h = h !== null ? h : this.panelSize.height;
30523 if(this.activePanel){
30524 var el = this.activePanel.getEl();
30525 w = w !== null ? w : el.getWidth();
30526 h = h !== null ? h : el.getHeight();
30527 this.panelSize = {width: w, height: h};
30528 this.activePanel.setSize(w, h);
30530 if(Roo.isIE && this.tabs){
30531 this.tabs.el.repaint();
30536 * Returns the container element for this region.
30537 * @return {Roo.Element}
30539 getEl : function(){
30544 * Hides this region.
30547 if(!this.collapsed){
30548 this.el.dom.style.left = "-2000px";
30551 this.collapsedEl.dom.style.left = "-2000px";
30552 this.collapsedEl.hide();
30554 this.visible = false;
30555 this.fireEvent("visibilitychange", this, false);
30559 * Shows this region if it was previously hidden.
30562 if(!this.collapsed){
30565 this.collapsedEl.show();
30567 this.visible = true;
30568 this.fireEvent("visibilitychange", this, true);
30571 closeClicked : function(){
30572 if(this.activePanel){
30573 this.remove(this.activePanel);
30577 collapseClick : function(e){
30579 e.stopPropagation();
30582 e.stopPropagation();
30588 * Collapses this region.
30589 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30591 collapse : function(skipAnim, skipCheck){
30592 if(this.collapsed) {
30596 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30598 this.collapsed = true;
30600 this.split.el.hide();
30602 if(this.config.animate && skipAnim !== true){
30603 this.fireEvent("invalidated", this);
30604 this.animateCollapse();
30606 this.el.setLocation(-20000,-20000);
30608 this.collapsedEl.show();
30609 this.fireEvent("collapsed", this);
30610 this.fireEvent("invalidated", this);
30616 animateCollapse : function(){
30621 * Expands this region if it was previously collapsed.
30622 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30623 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30625 expand : function(e, skipAnim){
30627 e.stopPropagation();
30629 if(!this.collapsed || this.el.hasActiveFx()) {
30633 this.afterSlideIn();
30636 this.collapsed = false;
30637 if(this.config.animate && skipAnim !== true){
30638 this.animateExpand();
30642 this.split.el.show();
30644 this.collapsedEl.setLocation(-2000,-2000);
30645 this.collapsedEl.hide();
30646 this.fireEvent("invalidated", this);
30647 this.fireEvent("expanded", this);
30651 animateExpand : function(){
30655 initTabs : function()
30657 this.bodyEl.setStyle("overflow", "hidden");
30658 var ts = new Roo.TabPanel(
30661 tabPosition: this.bottomTabs ? 'bottom' : 'top',
30662 disableTooltips: this.config.disableTabTips,
30663 toolbar : this.config.toolbar
30666 if(this.config.hideTabs){
30667 ts.stripWrap.setDisplayed(false);
30670 ts.resizeTabs = this.config.resizeTabs === true;
30671 ts.minTabWidth = this.config.minTabWidth || 40;
30672 ts.maxTabWidth = this.config.maxTabWidth || 250;
30673 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30674 ts.monitorResize = false;
30675 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30676 ts.bodyEl.addClass('x-layout-tabs-body');
30677 this.panels.each(this.initPanelAsTab, this);
30680 initPanelAsTab : function(panel){
30681 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30682 this.config.closeOnTab && panel.isClosable());
30683 if(panel.tabTip !== undefined){
30684 ti.setTooltip(panel.tabTip);
30686 ti.on("activate", function(){
30687 this.setActivePanel(panel);
30689 if(this.config.closeOnTab){
30690 ti.on("beforeclose", function(t, e){
30692 this.remove(panel);
30698 updatePanelTitle : function(panel, title){
30699 if(this.activePanel == panel){
30700 this.updateTitle(title);
30703 var ti = this.tabs.getTab(panel.getEl().id);
30705 if(panel.tabTip !== undefined){
30706 ti.setTooltip(panel.tabTip);
30711 updateTitle : function(title){
30712 if(this.titleTextEl && !this.config.title){
30713 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
30717 setActivePanel : function(panel){
30718 panel = this.getPanel(panel);
30719 if(this.activePanel && this.activePanel != panel){
30720 this.activePanel.setActiveState(false);
30722 this.activePanel = panel;
30723 panel.setActiveState(true);
30724 if(this.panelSize){
30725 panel.setSize(this.panelSize.width, this.panelSize.height);
30728 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30730 this.updateTitle(panel.getTitle());
30732 this.fireEvent("invalidated", this);
30734 this.fireEvent("panelactivated", this, panel);
30738 * Shows the specified panel.
30739 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30740 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30742 showPanel : function(panel)
30744 panel = this.getPanel(panel);
30747 var tab = this.tabs.getTab(panel.getEl().id);
30748 if(tab.isHidden()){
30749 this.tabs.unhideTab(tab.id);
30753 this.setActivePanel(panel);
30760 * Get the active panel for this region.
30761 * @return {Roo.ContentPanel} The active panel or null
30763 getActivePanel : function(){
30764 return this.activePanel;
30767 validateVisibility : function(){
30768 if(this.panels.getCount() < 1){
30769 this.updateTitle(" ");
30770 this.closeBtn.hide();
30773 if(!this.isVisible()){
30780 * Adds the passed ContentPanel(s) to this region.
30781 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30782 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30784 add : function(panel){
30785 if(arguments.length > 1){
30786 for(var i = 0, len = arguments.length; i < len; i++) {
30787 this.add(arguments[i]);
30791 if(this.hasPanel(panel)){
30792 this.showPanel(panel);
30795 panel.setRegion(this);
30796 this.panels.add(panel);
30797 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30798 this.bodyEl.dom.appendChild(panel.getEl().dom);
30799 if(panel.background !== true){
30800 this.setActivePanel(panel);
30802 this.fireEvent("paneladded", this, panel);
30808 this.initPanelAsTab(panel);
30810 if(panel.background !== true){
30811 this.tabs.activate(panel.getEl().id);
30813 this.fireEvent("paneladded", this, panel);
30818 * Hides the tab for the specified panel.
30819 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30821 hidePanel : function(panel){
30822 if(this.tabs && (panel = this.getPanel(panel))){
30823 this.tabs.hideTab(panel.getEl().id);
30828 * Unhides the tab for a previously hidden panel.
30829 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30831 unhidePanel : function(panel){
30832 if(this.tabs && (panel = this.getPanel(panel))){
30833 this.tabs.unhideTab(panel.getEl().id);
30837 clearPanels : function(){
30838 while(this.panels.getCount() > 0){
30839 this.remove(this.panels.first());
30844 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30845 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30846 * @param {Boolean} preservePanel Overrides the config preservePanel option
30847 * @return {Roo.ContentPanel} The panel that was removed
30849 remove : function(panel, preservePanel){
30850 panel = this.getPanel(panel);
30855 this.fireEvent("beforeremove", this, panel, e);
30856 if(e.cancel === true){
30859 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30860 var panelId = panel.getId();
30861 this.panels.removeKey(panelId);
30863 document.body.appendChild(panel.getEl().dom);
30866 this.tabs.removeTab(panel.getEl().id);
30867 }else if (!preservePanel){
30868 this.bodyEl.dom.removeChild(panel.getEl().dom);
30870 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30871 var p = this.panels.first();
30872 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30873 tempEl.appendChild(p.getEl().dom);
30874 this.bodyEl.update("");
30875 this.bodyEl.dom.appendChild(p.getEl().dom);
30877 this.updateTitle(p.getTitle());
30879 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30880 this.setActivePanel(p);
30882 panel.setRegion(null);
30883 if(this.activePanel == panel){
30884 this.activePanel = null;
30886 if(this.config.autoDestroy !== false && preservePanel !== true){
30887 try{panel.destroy();}catch(e){}
30889 this.fireEvent("panelremoved", this, panel);
30894 * Returns the TabPanel component used by this region
30895 * @return {Roo.TabPanel}
30897 getTabs : function(){
30901 createTool : function(parentEl, className){
30902 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30903 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
30904 btn.addClassOnOver("x-layout-tools-button-over");
30909 * Ext JS Library 1.1.1
30910 * Copyright(c) 2006-2007, Ext JS, LLC.
30912 * Originally Released Under LGPL - original licence link has changed is not relivant.
30915 * <script type="text/javascript">
30921 * @class Roo.SplitLayoutRegion
30922 * @extends Roo.LayoutRegion
30923 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30925 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30926 this.cursor = cursor;
30927 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30930 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30931 splitTip : "Drag to resize.",
30932 collapsibleSplitTip : "Drag to resize. Double click to hide.",
30933 useSplitTips : false,
30935 applyConfig : function(config){
30936 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30939 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
30940 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
30941 /** The SplitBar for this region
30942 * @type Roo.SplitBar */
30943 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30944 this.split.on("moved", this.onSplitMove, this);
30945 this.split.useShim = config.useShim === true;
30946 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30947 if(this.useSplitTips){
30948 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30950 if(config.collapsible){
30951 this.split.el.on("dblclick", this.collapse, this);
30954 if(typeof config.minSize != "undefined"){
30955 this.split.minSize = config.minSize;
30957 if(typeof config.maxSize != "undefined"){
30958 this.split.maxSize = config.maxSize;
30960 if(config.hideWhenEmpty || config.hidden || config.collapsed){
30961 this.hideSplitter();
30966 getHMaxSize : function(){
30967 var cmax = this.config.maxSize || 10000;
30968 var center = this.mgr.getRegion("center");
30969 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30972 getVMaxSize : function(){
30973 var cmax = this.config.maxSize || 10000;
30974 var center = this.mgr.getRegion("center");
30975 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30978 onSplitMove : function(split, newSize){
30979 this.fireEvent("resized", this, newSize);
30983 * Returns the {@link Roo.SplitBar} for this region.
30984 * @return {Roo.SplitBar}
30986 getSplitBar : function(){
30991 this.hideSplitter();
30992 Roo.SplitLayoutRegion.superclass.hide.call(this);
30995 hideSplitter : function(){
30997 this.split.el.setLocation(-2000,-2000);
30998 this.split.el.hide();
31004 this.split.el.show();
31006 Roo.SplitLayoutRegion.superclass.show.call(this);
31009 beforeSlide: function(){
31010 if(Roo.isGecko){// firefox overflow auto bug workaround
31011 this.bodyEl.clip();
31013 this.tabs.bodyEl.clip();
31015 if(this.activePanel){
31016 this.activePanel.getEl().clip();
31018 if(this.activePanel.beforeSlide){
31019 this.activePanel.beforeSlide();
31025 afterSlide : function(){
31026 if(Roo.isGecko){// firefox overflow auto bug workaround
31027 this.bodyEl.unclip();
31029 this.tabs.bodyEl.unclip();
31031 if(this.activePanel){
31032 this.activePanel.getEl().unclip();
31033 if(this.activePanel.afterSlide){
31034 this.activePanel.afterSlide();
31040 initAutoHide : function(){
31041 if(this.autoHide !== false){
31042 if(!this.autoHideHd){
31043 var st = new Roo.util.DelayedTask(this.slideIn, this);
31044 this.autoHideHd = {
31045 "mouseout": function(e){
31046 if(!e.within(this.el, true)){
31050 "mouseover" : function(e){
31056 this.el.on(this.autoHideHd);
31060 clearAutoHide : function(){
31061 if(this.autoHide !== false){
31062 this.el.un("mouseout", this.autoHideHd.mouseout);
31063 this.el.un("mouseover", this.autoHideHd.mouseover);
31067 clearMonitor : function(){
31068 Roo.get(document).un("click", this.slideInIf, this);
31071 // these names are backwards but not changed for compat
31072 slideOut : function(){
31073 if(this.isSlid || this.el.hasActiveFx()){
31076 this.isSlid = true;
31077 if(this.collapseBtn){
31078 this.collapseBtn.hide();
31080 this.closeBtnState = this.closeBtn.getStyle('display');
31081 this.closeBtn.hide();
31083 this.stickBtn.show();
31086 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
31087 this.beforeSlide();
31088 this.el.setStyle("z-index", 10001);
31089 this.el.slideIn(this.getSlideAnchor(), {
31090 callback: function(){
31092 this.initAutoHide();
31093 Roo.get(document).on("click", this.slideInIf, this);
31094 this.fireEvent("slideshow", this);
31101 afterSlideIn : function(){
31102 this.clearAutoHide();
31103 this.isSlid = false;
31104 this.clearMonitor();
31105 this.el.setStyle("z-index", "");
31106 if(this.collapseBtn){
31107 this.collapseBtn.show();
31109 this.closeBtn.setStyle('display', this.closeBtnState);
31111 this.stickBtn.hide();
31113 this.fireEvent("slidehide", this);
31116 slideIn : function(cb){
31117 if(!this.isSlid || this.el.hasActiveFx()){
31121 this.isSlid = false;
31122 this.beforeSlide();
31123 this.el.slideOut(this.getSlideAnchor(), {
31124 callback: function(){
31125 this.el.setLeftTop(-10000, -10000);
31127 this.afterSlideIn();
31135 slideInIf : function(e){
31136 if(!e.within(this.el)){
31141 animateCollapse : function(){
31142 this.beforeSlide();
31143 this.el.setStyle("z-index", 20000);
31144 var anchor = this.getSlideAnchor();
31145 this.el.slideOut(anchor, {
31146 callback : function(){
31147 this.el.setStyle("z-index", "");
31148 this.collapsedEl.slideIn(anchor, {duration:.3});
31150 this.el.setLocation(-10000,-10000);
31152 this.fireEvent("collapsed", this);
31159 animateExpand : function(){
31160 this.beforeSlide();
31161 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
31162 this.el.setStyle("z-index", 20000);
31163 this.collapsedEl.hide({
31166 this.el.slideIn(this.getSlideAnchor(), {
31167 callback : function(){
31168 this.el.setStyle("z-index", "");
31171 this.split.el.show();
31173 this.fireEvent("invalidated", this);
31174 this.fireEvent("expanded", this);
31202 getAnchor : function(){
31203 return this.anchors[this.position];
31206 getCollapseAnchor : function(){
31207 return this.canchors[this.position];
31210 getSlideAnchor : function(){
31211 return this.sanchors[this.position];
31214 getAlignAdj : function(){
31215 var cm = this.cmargins;
31216 switch(this.position){
31232 getExpandAdj : function(){
31233 var c = this.collapsedEl, cm = this.cmargins;
31234 switch(this.position){
31236 return [-(cm.right+c.getWidth()+cm.left), 0];
31239 return [cm.right+c.getWidth()+cm.left, 0];
31242 return [0, -(cm.top+cm.bottom+c.getHeight())];
31245 return [0, cm.top+cm.bottom+c.getHeight()];
31251 * Ext JS Library 1.1.1
31252 * Copyright(c) 2006-2007, Ext JS, LLC.
31254 * Originally Released Under LGPL - original licence link has changed is not relivant.
31257 * <script type="text/javascript">
31260 * These classes are private internal classes
31262 Roo.CenterLayoutRegion = function(mgr, config){
31263 Roo.LayoutRegion.call(this, mgr, config, "center");
31264 this.visible = true;
31265 this.minWidth = config.minWidth || 20;
31266 this.minHeight = config.minHeight || 20;
31269 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31271 // center panel can't be hidden
31275 // center panel can't be hidden
31278 getMinWidth: function(){
31279 return this.minWidth;
31282 getMinHeight: function(){
31283 return this.minHeight;
31288 Roo.NorthLayoutRegion = function(mgr, config){
31289 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31291 this.split.placement = Roo.SplitBar.TOP;
31292 this.split.orientation = Roo.SplitBar.VERTICAL;
31293 this.split.el.addClass("x-layout-split-v");
31295 var size = config.initialSize || config.height;
31296 if(typeof size != "undefined"){
31297 this.el.setHeight(size);
31300 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31301 orientation: Roo.SplitBar.VERTICAL,
31302 getBox : function(){
31303 if(this.collapsed){
31304 return this.collapsedEl.getBox();
31306 var box = this.el.getBox();
31308 box.height += this.split.el.getHeight();
31313 updateBox : function(box){
31314 if(this.split && !this.collapsed){
31315 box.height -= this.split.el.getHeight();
31316 this.split.el.setLeft(box.x);
31317 this.split.el.setTop(box.y+box.height);
31318 this.split.el.setWidth(box.width);
31320 if(this.collapsed){
31321 this.updateBody(box.width, null);
31323 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31327 Roo.SouthLayoutRegion = function(mgr, config){
31328 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31330 this.split.placement = Roo.SplitBar.BOTTOM;
31331 this.split.orientation = Roo.SplitBar.VERTICAL;
31332 this.split.el.addClass("x-layout-split-v");
31334 var size = config.initialSize || config.height;
31335 if(typeof size != "undefined"){
31336 this.el.setHeight(size);
31339 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31340 orientation: Roo.SplitBar.VERTICAL,
31341 getBox : function(){
31342 if(this.collapsed){
31343 return this.collapsedEl.getBox();
31345 var box = this.el.getBox();
31347 var sh = this.split.el.getHeight();
31354 updateBox : function(box){
31355 if(this.split && !this.collapsed){
31356 var sh = this.split.el.getHeight();
31359 this.split.el.setLeft(box.x);
31360 this.split.el.setTop(box.y-sh);
31361 this.split.el.setWidth(box.width);
31363 if(this.collapsed){
31364 this.updateBody(box.width, null);
31366 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31370 Roo.EastLayoutRegion = function(mgr, config){
31371 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31373 this.split.placement = Roo.SplitBar.RIGHT;
31374 this.split.orientation = Roo.SplitBar.HORIZONTAL;
31375 this.split.el.addClass("x-layout-split-h");
31377 var size = config.initialSize || config.width;
31378 if(typeof size != "undefined"){
31379 this.el.setWidth(size);
31382 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31383 orientation: Roo.SplitBar.HORIZONTAL,
31384 getBox : function(){
31385 if(this.collapsed){
31386 return this.collapsedEl.getBox();
31388 var box = this.el.getBox();
31390 var sw = this.split.el.getWidth();
31397 updateBox : function(box){
31398 if(this.split && !this.collapsed){
31399 var sw = this.split.el.getWidth();
31401 this.split.el.setLeft(box.x);
31402 this.split.el.setTop(box.y);
31403 this.split.el.setHeight(box.height);
31406 if(this.collapsed){
31407 this.updateBody(null, box.height);
31409 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31413 Roo.WestLayoutRegion = function(mgr, config){
31414 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31416 this.split.placement = Roo.SplitBar.LEFT;
31417 this.split.orientation = Roo.SplitBar.HORIZONTAL;
31418 this.split.el.addClass("x-layout-split-h");
31420 var size = config.initialSize || config.width;
31421 if(typeof size != "undefined"){
31422 this.el.setWidth(size);
31425 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31426 orientation: Roo.SplitBar.HORIZONTAL,
31427 getBox : function(){
31428 if(this.collapsed){
31429 return this.collapsedEl.getBox();
31431 var box = this.el.getBox();
31433 box.width += this.split.el.getWidth();
31438 updateBox : function(box){
31439 if(this.split && !this.collapsed){
31440 var sw = this.split.el.getWidth();
31442 this.split.el.setLeft(box.x+box.width);
31443 this.split.el.setTop(box.y);
31444 this.split.el.setHeight(box.height);
31446 if(this.collapsed){
31447 this.updateBody(null, box.height);
31449 Roo.LayoutRegion.prototype.updateBox.call(this, box);
31454 * Ext JS Library 1.1.1
31455 * Copyright(c) 2006-2007, Ext JS, LLC.
31457 * Originally Released Under LGPL - original licence link has changed is not relivant.
31460 * <script type="text/javascript">
31465 * Private internal class for reading and applying state
31467 Roo.LayoutStateManager = function(layout){
31468 // default empty state
31477 Roo.LayoutStateManager.prototype = {
31478 init : function(layout, provider){
31479 this.provider = provider;
31480 var state = provider.get(layout.id+"-layout-state");
31482 var wasUpdating = layout.isUpdating();
31484 layout.beginUpdate();
31486 for(var key in state){
31487 if(typeof state[key] != "function"){
31488 var rstate = state[key];
31489 var r = layout.getRegion(key);
31492 r.resizeTo(rstate.size);
31494 if(rstate.collapsed == true){
31497 r.expand(null, true);
31503 layout.endUpdate();
31505 this.state = state;
31507 this.layout = layout;
31508 layout.on("regionresized", this.onRegionResized, this);
31509 layout.on("regioncollapsed", this.onRegionCollapsed, this);
31510 layout.on("regionexpanded", this.onRegionExpanded, this);
31513 storeState : function(){
31514 this.provider.set(this.layout.id+"-layout-state", this.state);
31517 onRegionResized : function(region, newSize){
31518 this.state[region.getPosition()].size = newSize;
31522 onRegionCollapsed : function(region){
31523 this.state[region.getPosition()].collapsed = true;
31527 onRegionExpanded : function(region){
31528 this.state[region.getPosition()].collapsed = false;
31533 * Ext JS Library 1.1.1
31534 * Copyright(c) 2006-2007, Ext JS, LLC.
31536 * Originally Released Under LGPL - original licence link has changed is not relivant.
31539 * <script type="text/javascript">
31542 * @class Roo.ContentPanel
31543 * @extends Roo.util.Observable
31544 * A basic ContentPanel element.
31545 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
31546 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
31547 * @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
31548 * @cfg {Boolean} closable True if the panel can be closed/removed
31549 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
31550 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31551 * @cfg {Toolbar} toolbar A toolbar for this panel
31552 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
31553 * @cfg {String} title The title for this panel
31554 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31555 * @cfg {String} url Calls {@link #setUrl} with this value
31556 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31557 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
31558 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
31559 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
31562 * Create a new ContentPanel.
31563 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31564 * @param {String/Object} config A string to set only the title or a config object
31565 * @param {String} content (optional) Set the HTML content for this panel
31566 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31568 Roo.ContentPanel = function(el, config, content){
31572 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31576 if (config && config.parentLayout) {
31577 el = config.parentLayout.el.createChild();
31580 if(el.autoCreate){ // xtype is available if this is called from factory
31584 this.el = Roo.get(el);
31585 if(!this.el && config && config.autoCreate){
31586 if(typeof config.autoCreate == "object"){
31587 if(!config.autoCreate.id){
31588 config.autoCreate.id = config.id||el;
31590 this.el = Roo.DomHelper.append(document.body,
31591 config.autoCreate, true);
31593 this.el = Roo.DomHelper.append(document.body,
31594 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31597 this.closable = false;
31598 this.loaded = false;
31599 this.active = false;
31600 if(typeof config == "string"){
31601 this.title = config;
31603 Roo.apply(this, config);
31606 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31607 this.wrapEl = this.el.wrap();
31608 this.toolbar.container = this.el.insertSibling(false, 'before');
31609 this.toolbar = new Roo.Toolbar(this.toolbar);
31612 // xtype created footer. - not sure if will work as we normally have to render first..
31613 if (this.footer && !this.footer.el && this.footer.xtype) {
31614 if (!this.wrapEl) {
31615 this.wrapEl = this.el.wrap();
31618 this.footer.container = this.wrapEl.createChild();
31620 this.footer = Roo.factory(this.footer, Roo);
31625 this.resizeEl = Roo.get(this.resizeEl, true);
31627 this.resizeEl = this.el;
31629 // handle view.xtype
31637 * Fires when this panel is activated.
31638 * @param {Roo.ContentPanel} this
31642 * @event deactivate
31643 * Fires when this panel is activated.
31644 * @param {Roo.ContentPanel} this
31646 "deactivate" : true,
31650 * Fires when this panel is resized if fitToFrame is true.
31651 * @param {Roo.ContentPanel} this
31652 * @param {Number} width The width after any component adjustments
31653 * @param {Number} height The height after any component adjustments
31659 * Fires when this tab is created
31660 * @param {Roo.ContentPanel} this
31670 if(this.autoScroll){
31671 this.resizeEl.setStyle("overflow", "auto");
31673 // fix randome scrolling
31674 this.el.on('scroll', function() {
31675 Roo.log('fix random scolling');
31676 this.scrollTo('top',0);
31679 content = content || this.content;
31681 this.setContent(content);
31683 if(config && config.url){
31684 this.setUrl(this.url, this.params, this.loadOnce);
31689 Roo.ContentPanel.superclass.constructor.call(this);
31691 if (this.view && typeof(this.view.xtype) != 'undefined') {
31692 this.view.el = this.el.appendChild(document.createElement("div"));
31693 this.view = Roo.factory(this.view);
31694 this.view.render && this.view.render(false, '');
31698 this.fireEvent('render', this);
31701 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31703 setRegion : function(region){
31704 this.region = region;
31706 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31708 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31713 * Returns the toolbar for this Panel if one was configured.
31714 * @return {Roo.Toolbar}
31716 getToolbar : function(){
31717 return this.toolbar;
31720 setActiveState : function(active){
31721 this.active = active;
31723 this.fireEvent("deactivate", this);
31725 this.fireEvent("activate", this);
31729 * Updates this panel's element
31730 * @param {String} content The new content
31731 * @param {Boolean} loadScripts (optional) true to look for and process scripts
31733 setContent : function(content, loadScripts){
31734 this.el.update(content, loadScripts);
31737 ignoreResize : function(w, h){
31738 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31741 this.lastSize = {width: w, height: h};
31746 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31747 * @return {Roo.UpdateManager} The UpdateManager
31749 getUpdateManager : function(){
31750 return this.el.getUpdateManager();
31753 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31754 * @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:
31757 url: "your-url.php",
31758 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31759 callback: yourFunction,
31760 scope: yourObject, //(optional scope)
31763 text: "Loading...",
31768 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31769 * 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.
31770 * @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}
31771 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31772 * @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.
31773 * @return {Roo.ContentPanel} this
31776 var um = this.el.getUpdateManager();
31777 um.update.apply(um, arguments);
31783 * 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.
31784 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31785 * @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)
31786 * @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)
31787 * @return {Roo.UpdateManager} The UpdateManager
31789 setUrl : function(url, params, loadOnce){
31790 if(this.refreshDelegate){
31791 this.removeListener("activate", this.refreshDelegate);
31793 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31794 this.on("activate", this.refreshDelegate);
31795 return this.el.getUpdateManager();
31798 _handleRefresh : function(url, params, loadOnce){
31799 if(!loadOnce || !this.loaded){
31800 var updater = this.el.getUpdateManager();
31801 updater.update(url, params, this._setLoaded.createDelegate(this));
31805 _setLoaded : function(){
31806 this.loaded = true;
31810 * Returns this panel's id
31813 getId : function(){
31818 * Returns this panel's element - used by regiosn to add.
31819 * @return {Roo.Element}
31821 getEl : function(){
31822 return this.wrapEl || this.el;
31825 adjustForComponents : function(width, height)
31827 //Roo.log('adjustForComponents ');
31828 if(this.resizeEl != this.el){
31829 width -= this.el.getFrameWidth('lr');
31830 height -= this.el.getFrameWidth('tb');
31833 var te = this.toolbar.getEl();
31834 height -= te.getHeight();
31835 te.setWidth(width);
31838 var te = this.footer.getEl();
31839 //Roo.log("footer:" + te.getHeight());
31841 height -= te.getHeight();
31842 te.setWidth(width);
31846 if(this.adjustments){
31847 width += this.adjustments[0];
31848 height += this.adjustments[1];
31850 return {"width": width, "height": height};
31853 setSize : function(width, height){
31854 if(this.fitToFrame && !this.ignoreResize(width, height)){
31855 if(this.fitContainer && this.resizeEl != this.el){
31856 this.el.setSize(width, height);
31858 var size = this.adjustForComponents(width, height);
31859 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31860 this.fireEvent('resize', this, size.width, size.height);
31865 * Returns this panel's title
31868 getTitle : function(){
31873 * Set this panel's title
31874 * @param {String} title
31876 setTitle : function(title){
31877 this.title = title;
31879 this.region.updatePanelTitle(this, title);
31884 * Returns true is this panel was configured to be closable
31885 * @return {Boolean}
31887 isClosable : function(){
31888 return this.closable;
31891 beforeSlide : function(){
31893 this.resizeEl.clip();
31896 afterSlide : function(){
31898 this.resizeEl.unclip();
31902 * Force a content refresh from the URL specified in the {@link #setUrl} method.
31903 * Will fail silently if the {@link #setUrl} method has not been called.
31904 * This does not activate the panel, just updates its content.
31906 refresh : function(){
31907 if(this.refreshDelegate){
31908 this.loaded = false;
31909 this.refreshDelegate();
31914 * Destroys this panel
31916 destroy : function(){
31917 this.el.removeAllListeners();
31918 var tempEl = document.createElement("span");
31919 tempEl.appendChild(this.el.dom);
31920 tempEl.innerHTML = "";
31926 * form - if the content panel contains a form - this is a reference to it.
31927 * @type {Roo.form.Form}
31931 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31932 * This contains a reference to it.
31938 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31948 * @param {Object} cfg Xtype definition of item to add.
31951 addxtype : function(cfg) {
31953 if (cfg.xtype.match(/^Form$/)) {
31956 //if (this.footer) {
31957 // el = this.footer.container.insertSibling(false, 'before');
31959 el = this.el.createChild();
31962 this.form = new Roo.form.Form(cfg);
31965 if ( this.form.allItems.length) {
31966 this.form.render(el.dom);
31970 // should only have one of theses..
31971 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31972 // views.. should not be just added - used named prop 'view''
31974 cfg.el = this.el.appendChild(document.createElement("div"));
31977 var ret = new Roo.factory(cfg);
31979 ret.render && ret.render(false, ''); // render blank..
31988 * @class Roo.GridPanel
31989 * @extends Roo.ContentPanel
31991 * Create a new GridPanel.
31992 * @param {Roo.grid.Grid} grid The grid for this panel
31993 * @param {String/Object} config A string to set only the panel's title, or a config object
31995 Roo.GridPanel = function(grid, config){
31998 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31999 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
32001 this.wrapper.dom.appendChild(grid.getGridEl().dom);
32003 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
32006 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
32008 // xtype created footer. - not sure if will work as we normally have to render first..
32009 if (this.footer && !this.footer.el && this.footer.xtype) {
32011 this.footer.container = this.grid.getView().getFooterPanel(true);
32012 this.footer.dataSource = this.grid.dataSource;
32013 this.footer = Roo.factory(this.footer, Roo);
32017 grid.monitorWindowResize = false; // turn off autosizing
32018 grid.autoHeight = false;
32019 grid.autoWidth = false;
32021 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
32024 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
32025 getId : function(){
32026 return this.grid.id;
32030 * Returns the grid for this panel
32031 * @return {Roo.grid.Grid}
32033 getGrid : function(){
32037 setSize : function(width, height){
32038 if(!this.ignoreResize(width, height)){
32039 var grid = this.grid;
32040 var size = this.adjustForComponents(width, height);
32041 grid.getGridEl().setSize(size.width, size.height);
32046 beforeSlide : function(){
32047 this.grid.getView().scroller.clip();
32050 afterSlide : function(){
32051 this.grid.getView().scroller.unclip();
32054 destroy : function(){
32055 this.grid.destroy();
32057 Roo.GridPanel.superclass.destroy.call(this);
32063 * @class Roo.NestedLayoutPanel
32064 * @extends Roo.ContentPanel
32066 * Create a new NestedLayoutPanel.
32069 * @param {Roo.BorderLayout} layout The layout for this panel
32070 * @param {String/Object} config A string to set only the title or a config object
32072 Roo.NestedLayoutPanel = function(layout, config)
32074 // construct with only one argument..
32075 /* FIXME - implement nicer consturctors
32076 if (layout.layout) {
32078 layout = config.layout;
32079 delete config.layout;
32081 if (layout.xtype && !layout.getEl) {
32082 // then layout needs constructing..
32083 layout = Roo.factory(layout, Roo);
32088 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
32090 layout.monitorWindowResize = false; // turn off autosizing
32091 this.layout = layout;
32092 this.layout.getEl().addClass("x-layout-nested-layout");
32099 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
32101 setSize : function(width, height){
32102 if(!this.ignoreResize(width, height)){
32103 var size = this.adjustForComponents(width, height);
32104 var el = this.layout.getEl();
32105 el.setSize(size.width, size.height);
32106 var touch = el.dom.offsetWidth;
32107 this.layout.layout();
32108 // ie requires a double layout on the first pass
32109 if(Roo.isIE && !this.initialized){
32110 this.initialized = true;
32111 this.layout.layout();
32116 // activate all subpanels if not currently active..
32118 setActiveState : function(active){
32119 this.active = active;
32121 this.fireEvent("deactivate", this);
32125 this.fireEvent("activate", this);
32126 // not sure if this should happen before or after..
32127 if (!this.layout) {
32128 return; // should not happen..
32131 for (var r in this.layout.regions) {
32132 reg = this.layout.getRegion(r);
32133 if (reg.getActivePanel()) {
32134 //reg.showPanel(reg.getActivePanel()); // force it to activate..
32135 reg.setActivePanel(reg.getActivePanel());
32138 if (!reg.panels.length) {
32141 reg.showPanel(reg.getPanel(0));
32150 * Returns the nested BorderLayout for this panel
32151 * @return {Roo.BorderLayout}
32153 getLayout : function(){
32154 return this.layout;
32158 * Adds a xtype elements to the layout of the nested panel
32162 xtype : 'ContentPanel',
32169 xtype : 'NestedLayoutPanel',
32175 items : [ ... list of content panels or nested layout panels.. ]
32179 * @param {Object} cfg Xtype definition of item to add.
32181 addxtype : function(cfg) {
32182 return this.layout.addxtype(cfg);
32187 Roo.ScrollPanel = function(el, config, content){
32188 config = config || {};
32189 config.fitToFrame = true;
32190 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
32192 this.el.dom.style.overflow = "hidden";
32193 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
32194 this.el.removeClass("x-layout-inactive-content");
32195 this.el.on("mousewheel", this.onWheel, this);
32197 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
32198 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
32199 up.unselectable(); down.unselectable();
32200 up.on("click", this.scrollUp, this);
32201 down.on("click", this.scrollDown, this);
32202 up.addClassOnOver("x-scroller-btn-over");
32203 down.addClassOnOver("x-scroller-btn-over");
32204 up.addClassOnClick("x-scroller-btn-click");
32205 down.addClassOnClick("x-scroller-btn-click");
32206 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
32208 this.resizeEl = this.el;
32209 this.el = wrap; this.up = up; this.down = down;
32212 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
32214 wheelIncrement : 5,
32215 scrollUp : function(){
32216 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
32219 scrollDown : function(){
32220 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
32223 afterScroll : function(){
32224 var el = this.resizeEl;
32225 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
32226 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32227 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32230 setSize : function(){
32231 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
32232 this.afterScroll();
32235 onWheel : function(e){
32236 var d = e.getWheelDelta();
32237 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
32238 this.afterScroll();
32242 setContent : function(content, loadScripts){
32243 this.resizeEl.update(content, loadScripts);
32257 * @class Roo.TreePanel
32258 * @extends Roo.ContentPanel
32260 * Create a new TreePanel. - defaults to fit/scoll contents.
32261 * @param {String/Object} config A string to set only the panel's title, or a config object
32262 * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32264 Roo.TreePanel = function(config){
32265 var el = config.el;
32266 var tree = config.tree;
32267 delete config.tree;
32268 delete config.el; // hopefull!
32270 // wrapper for IE7 strict & safari scroll issue
32272 var treeEl = el.createChild();
32273 config.resizeEl = treeEl;
32277 Roo.TreePanel.superclass.constructor.call(this, el, config);
32280 this.tree = new Roo.tree.TreePanel(treeEl , tree);
32281 //console.log(tree);
32282 this.on('activate', function()
32284 if (this.tree.rendered) {
32287 //console.log('render tree');
32288 this.tree.render();
32290 // this should not be needed.. - it's actually the 'el' that resizes?
32291 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32293 //this.on('resize', function (cp, w, h) {
32294 // this.tree.innerCt.setWidth(w);
32295 // this.tree.innerCt.setHeight(h);
32296 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
32303 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
32320 * Ext JS Library 1.1.1
32321 * Copyright(c) 2006-2007, Ext JS, LLC.
32323 * Originally Released Under LGPL - original licence link has changed is not relivant.
32326 * <script type="text/javascript">
32331 * @class Roo.ReaderLayout
32332 * @extends Roo.BorderLayout
32333 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
32334 * center region containing two nested regions (a top one for a list view and one for item preview below),
32335 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32336 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32337 * expedites the setup of the overall layout and regions for this common application style.
32340 var reader = new Roo.ReaderLayout();
32341 var CP = Roo.ContentPanel; // shortcut for adding
32343 reader.beginUpdate();
32344 reader.add("north", new CP("north", "North"));
32345 reader.add("west", new CP("west", {title: "West"}));
32346 reader.add("east", new CP("east", {title: "East"}));
32348 reader.regions.listView.add(new CP("listView", "List"));
32349 reader.regions.preview.add(new CP("preview", "Preview"));
32350 reader.endUpdate();
32353 * Create a new ReaderLayout
32354 * @param {Object} config Configuration options
32355 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32356 * document.body if omitted)
32358 Roo.ReaderLayout = function(config, renderTo){
32359 var c = config || {size:{}};
32360 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32361 north: c.north !== false ? Roo.apply({
32365 }, c.north) : false,
32366 west: c.west !== false ? Roo.apply({
32374 margins:{left:5,right:0,bottom:5,top:5},
32375 cmargins:{left:5,right:5,bottom:5,top:5}
32376 }, c.west) : false,
32377 east: c.east !== false ? Roo.apply({
32385 margins:{left:0,right:5,bottom:5,top:5},
32386 cmargins:{left:5,right:5,bottom:5,top:5}
32387 }, c.east) : false,
32388 center: Roo.apply({
32389 tabPosition: 'top',
32393 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32397 this.el.addClass('x-reader');
32399 this.beginUpdate();
32401 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32402 south: c.preview !== false ? Roo.apply({
32409 cmargins:{top:5,left:0, right:0, bottom:0}
32410 }, c.preview) : false,
32411 center: Roo.apply({
32417 this.add('center', new Roo.NestedLayoutPanel(inner,
32418 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32422 this.regions.preview = inner.getRegion('south');
32423 this.regions.listView = inner.getRegion('center');
32426 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32428 * Ext JS Library 1.1.1
32429 * Copyright(c) 2006-2007, Ext JS, LLC.
32431 * Originally Released Under LGPL - original licence link has changed is not relivant.
32434 * <script type="text/javascript">
32438 * @class Roo.grid.Grid
32439 * @extends Roo.util.Observable
32440 * This class represents the primary interface of a component based grid control.
32441 * <br><br>Usage:<pre><code>
32442 var grid = new Roo.grid.Grid("my-container-id", {
32445 selModel: mySelectionModel,
32446 autoSizeColumns: true,
32447 monitorWindowResize: false,
32448 trackMouseOver: true
32453 * <b>Common Problems:</b><br/>
32454 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32455 * element will correct this<br/>
32456 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32457 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32458 * are unpredictable.<br/>
32459 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32460 * grid to calculate dimensions/offsets.<br/>
32462 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32463 * The container MUST have some type of size defined for the grid to fill. The container will be
32464 * automatically set to position relative if it isn't already.
32465 * @param {Object} config A config object that sets properties on this grid.
32467 Roo.grid.Grid = function(container, config){
32468 // initialize the container
32469 this.container = Roo.get(container);
32470 this.container.update("");
32471 this.container.setStyle("overflow", "hidden");
32472 this.container.addClass('x-grid-container');
32474 this.id = this.container.id;
32476 Roo.apply(this, config);
32477 // check and correct shorthanded configs
32479 this.dataSource = this.ds;
32483 this.colModel = this.cm;
32487 this.selModel = this.sm;
32491 if (this.selModel) {
32492 this.selModel = Roo.factory(this.selModel, Roo.grid);
32493 this.sm = this.selModel;
32494 this.sm.xmodule = this.xmodule || false;
32496 if (typeof(this.colModel.config) == 'undefined') {
32497 this.colModel = new Roo.grid.ColumnModel(this.colModel);
32498 this.cm = this.colModel;
32499 this.cm.xmodule = this.xmodule || false;
32501 if (this.dataSource) {
32502 this.dataSource= Roo.factory(this.dataSource, Roo.data);
32503 this.ds = this.dataSource;
32504 this.ds.xmodule = this.xmodule || false;
32511 this.container.setWidth(this.width);
32515 this.container.setHeight(this.height);
32522 * The raw click event for the entire grid.
32523 * @param {Roo.EventObject} e
32528 * The raw dblclick event for the entire grid.
32529 * @param {Roo.EventObject} e
32533 * @event contextmenu
32534 * The raw contextmenu event for the entire grid.
32535 * @param {Roo.EventObject} e
32537 "contextmenu" : true,
32540 * The raw mousedown event for the entire grid.
32541 * @param {Roo.EventObject} e
32543 "mousedown" : true,
32546 * The raw mouseup event for the entire grid.
32547 * @param {Roo.EventObject} e
32552 * The raw mouseover event for the entire grid.
32553 * @param {Roo.EventObject} e
32555 "mouseover" : true,
32558 * The raw mouseout event for the entire grid.
32559 * @param {Roo.EventObject} e
32564 * The raw keypress event for the entire grid.
32565 * @param {Roo.EventObject} e
32570 * The raw keydown event for the entire grid.
32571 * @param {Roo.EventObject} e
32579 * Fires when a cell is clicked
32580 * @param {Grid} this
32581 * @param {Number} rowIndex
32582 * @param {Number} columnIndex
32583 * @param {Roo.EventObject} e
32585 "cellclick" : true,
32587 * @event celldblclick
32588 * Fires when a cell is double clicked
32589 * @param {Grid} this
32590 * @param {Number} rowIndex
32591 * @param {Number} columnIndex
32592 * @param {Roo.EventObject} e
32594 "celldblclick" : true,
32597 * Fires when a row is clicked
32598 * @param {Grid} this
32599 * @param {Number} rowIndex
32600 * @param {Roo.EventObject} e
32604 * @event rowdblclick
32605 * Fires when a row is double clicked
32606 * @param {Grid} this
32607 * @param {Number} rowIndex
32608 * @param {Roo.EventObject} e
32610 "rowdblclick" : true,
32612 * @event headerclick
32613 * Fires when a header is clicked
32614 * @param {Grid} this
32615 * @param {Number} columnIndex
32616 * @param {Roo.EventObject} e
32618 "headerclick" : true,
32620 * @event headerdblclick
32621 * Fires when a header cell is double clicked
32622 * @param {Grid} this
32623 * @param {Number} columnIndex
32624 * @param {Roo.EventObject} e
32626 "headerdblclick" : true,
32628 * @event rowcontextmenu
32629 * Fires when a row is right clicked
32630 * @param {Grid} this
32631 * @param {Number} rowIndex
32632 * @param {Roo.EventObject} e
32634 "rowcontextmenu" : true,
32636 * @event cellcontextmenu
32637 * Fires when a cell is right clicked
32638 * @param {Grid} this
32639 * @param {Number} rowIndex
32640 * @param {Number} cellIndex
32641 * @param {Roo.EventObject} e
32643 "cellcontextmenu" : true,
32645 * @event headercontextmenu
32646 * Fires when a header is right clicked
32647 * @param {Grid} this
32648 * @param {Number} columnIndex
32649 * @param {Roo.EventObject} e
32651 "headercontextmenu" : true,
32653 * @event bodyscroll
32654 * Fires when the body element is scrolled
32655 * @param {Number} scrollLeft
32656 * @param {Number} scrollTop
32658 "bodyscroll" : true,
32660 * @event columnresize
32661 * Fires when the user resizes a column
32662 * @param {Number} columnIndex
32663 * @param {Number} newSize
32665 "columnresize" : true,
32667 * @event columnmove
32668 * Fires when the user moves a column
32669 * @param {Number} oldIndex
32670 * @param {Number} newIndex
32672 "columnmove" : true,
32675 * Fires when row(s) start being dragged
32676 * @param {Grid} this
32677 * @param {Roo.GridDD} dd The drag drop object
32678 * @param {event} e The raw browser event
32680 "startdrag" : true,
32683 * Fires when a drag operation is complete
32684 * @param {Grid} this
32685 * @param {Roo.GridDD} dd The drag drop object
32686 * @param {event} e The raw browser event
32691 * Fires when dragged row(s) are dropped on a valid DD target
32692 * @param {Grid} this
32693 * @param {Roo.GridDD} dd The drag drop object
32694 * @param {String} targetId The target drag drop object
32695 * @param {event} e The raw browser event
32700 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32701 * @param {Grid} this
32702 * @param {Roo.GridDD} dd The drag drop object
32703 * @param {String} targetId The target drag drop object
32704 * @param {event} e The raw browser event
32709 * Fires when the dragged row(s) first cross another DD target while being dragged
32710 * @param {Grid} this
32711 * @param {Roo.GridDD} dd The drag drop object
32712 * @param {String} targetId The target drag drop object
32713 * @param {event} e The raw browser event
32715 "dragenter" : true,
32718 * Fires when the dragged row(s) leave another DD target while being dragged
32719 * @param {Grid} this
32720 * @param {Roo.GridDD} dd The drag drop object
32721 * @param {String} targetId The target drag drop object
32722 * @param {event} e The raw browser event
32727 * Fires when a row is rendered, so you can change add a style to it.
32728 * @param {GridView} gridview The grid view
32729 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
32735 * Fires when the grid is rendered
32736 * @param {Grid} grid
32741 Roo.grid.Grid.superclass.constructor.call(this);
32743 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32746 * @cfg {String} ddGroup - drag drop group.
32750 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32752 minColumnWidth : 25,
32755 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32756 * <b>on initial render.</b> It is more efficient to explicitly size the columns
32757 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
32759 autoSizeColumns : false,
32762 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32764 autoSizeHeaders : true,
32767 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32769 monitorWindowResize : true,
32772 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32773 * rows measured to get a columns size. Default is 0 (all rows).
32775 maxRowsToMeasure : 0,
32778 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32780 trackMouseOver : true,
32783 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
32787 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32789 enableDragDrop : false,
32792 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32794 enableColumnMove : true,
32797 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32799 enableColumnHide : true,
32802 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32804 enableRowHeightSync : false,
32807 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
32812 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32814 autoHeight : false,
32817 * @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.
32819 autoExpandColumn : false,
32822 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32825 autoExpandMin : 50,
32828 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32830 autoExpandMax : 1000,
32833 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32838 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32842 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32852 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32853 * of a fixed width. Default is false.
32856 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32859 * Called once after all setup has been completed and the grid is ready to be rendered.
32860 * @return {Roo.grid.Grid} this
32862 render : function()
32864 var c = this.container;
32865 // try to detect autoHeight/width mode
32866 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32867 this.autoHeight = true;
32869 var view = this.getView();
32872 c.on("click", this.onClick, this);
32873 c.on("dblclick", this.onDblClick, this);
32874 c.on("contextmenu", this.onContextMenu, this);
32875 c.on("keydown", this.onKeyDown, this);
32877 c.on("touchstart", this.onTouchStart, this);
32880 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32882 this.getSelectionModel().init(this);
32887 this.loadMask = new Roo.LoadMask(this.container,
32888 Roo.apply({store:this.dataSource}, this.loadMask));
32892 if (this.toolbar && this.toolbar.xtype) {
32893 this.toolbar.container = this.getView().getHeaderPanel(true);
32894 this.toolbar = new Roo.Toolbar(this.toolbar);
32896 if (this.footer && this.footer.xtype) {
32897 this.footer.dataSource = this.getDataSource();
32898 this.footer.container = this.getView().getFooterPanel(true);
32899 this.footer = Roo.factory(this.footer, Roo);
32901 if (this.dropTarget && this.dropTarget.xtype) {
32902 delete this.dropTarget.xtype;
32903 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32907 this.rendered = true;
32908 this.fireEvent('render', this);
32913 * Reconfigures the grid to use a different Store and Column Model.
32914 * The View will be bound to the new objects and refreshed.
32915 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32916 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32918 reconfigure : function(dataSource, colModel){
32920 this.loadMask.destroy();
32921 this.loadMask = new Roo.LoadMask(this.container,
32922 Roo.apply({store:dataSource}, this.loadMask));
32924 this.view.bind(dataSource, colModel);
32925 this.dataSource = dataSource;
32926 this.colModel = colModel;
32927 this.view.refresh(true);
32931 onKeyDown : function(e){
32932 this.fireEvent("keydown", e);
32936 * Destroy this grid.
32937 * @param {Boolean} removeEl True to remove the element
32939 destroy : function(removeEl, keepListeners){
32941 this.loadMask.destroy();
32943 var c = this.container;
32944 c.removeAllListeners();
32945 this.view.destroy();
32946 this.colModel.purgeListeners();
32947 if(!keepListeners){
32948 this.purgeListeners();
32951 if(removeEl === true){
32957 processEvent : function(name, e){
32958 // does this fire select???
32959 //Roo.log('grid:processEvent ' + name);
32961 if (name != 'touchstart' ) {
32962 this.fireEvent(name, e);
32965 var t = e.getTarget();
32967 var header = v.findHeaderIndex(t);
32968 if(header !== false){
32969 var ename = name == 'touchstart' ? 'click' : name;
32971 this.fireEvent("header" + ename, this, header, e);
32973 var row = v.findRowIndex(t);
32974 var cell = v.findCellIndex(t);
32975 if (name == 'touchstart') {
32976 // first touch is always a click.
32977 // hopefull this happens after selection is updated.?
32980 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32981 var cs = this.selModel.getSelectedCell();
32982 if (row == cs[0] && cell == cs[1]){
32986 if (typeof(this.selModel.getSelections) != 'undefined') {
32987 var cs = this.selModel.getSelections();
32988 var ds = this.dataSource;
32989 if (cs.length == 1 && ds.getAt(row) == cs[0]){
33000 this.fireEvent("row" + name, this, row, e);
33001 if(cell !== false){
33002 this.fireEvent("cell" + name, this, row, cell, e);
33009 onClick : function(e){
33010 this.processEvent("click", e);
33013 onTouchStart : function(e){
33014 this.processEvent("touchstart", e);
33018 onContextMenu : function(e, t){
33019 this.processEvent("contextmenu", e);
33023 onDblClick : function(e){
33024 this.processEvent("dblclick", e);
33028 walkCells : function(row, col, step, fn, scope){
33029 var cm = this.colModel, clen = cm.getColumnCount();
33030 var ds = this.dataSource, rlen = ds.getCount(), first = true;
33042 if(fn.call(scope || this, row, col, cm) === true){
33060 if(fn.call(scope || this, row, col, cm) === true){
33072 getSelections : function(){
33073 return this.selModel.getSelections();
33077 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
33078 * but if manual update is required this method will initiate it.
33080 autoSize : function(){
33082 this.view.layout();
33083 if(this.view.adjustForScroll){
33084 this.view.adjustForScroll();
33090 * Returns the grid's underlying element.
33091 * @return {Element} The element
33093 getGridEl : function(){
33094 return this.container;
33097 // private for compatibility, overridden by editor grid
33098 stopEditing : function(){},
33101 * Returns the grid's SelectionModel.
33102 * @return {SelectionModel}
33104 getSelectionModel : function(){
33105 if(!this.selModel){
33106 this.selModel = new Roo.grid.RowSelectionModel();
33108 return this.selModel;
33112 * Returns the grid's DataSource.
33113 * @return {DataSource}
33115 getDataSource : function(){
33116 return this.dataSource;
33120 * Returns the grid's ColumnModel.
33121 * @return {ColumnModel}
33123 getColumnModel : function(){
33124 return this.colModel;
33128 * Returns the grid's GridView object.
33129 * @return {GridView}
33131 getView : function(){
33133 this.view = new Roo.grid.GridView(this.viewConfig);
33138 * Called to get grid's drag proxy text, by default returns this.ddText.
33141 getDragDropText : function(){
33142 var count = this.selModel.getCount();
33143 return String.format(this.ddText, count, count == 1 ? '' : 's');
33147 * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
33148 * %0 is replaced with the number of selected rows.
33151 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
33153 * Ext JS Library 1.1.1
33154 * Copyright(c) 2006-2007, Ext JS, LLC.
33156 * Originally Released Under LGPL - original licence link has changed is not relivant.
33159 * <script type="text/javascript">
33162 Roo.grid.AbstractGridView = function(){
33166 "beforerowremoved" : true,
33167 "beforerowsinserted" : true,
33168 "beforerefresh" : true,
33169 "rowremoved" : true,
33170 "rowsinserted" : true,
33171 "rowupdated" : true,
33174 Roo.grid.AbstractGridView.superclass.constructor.call(this);
33177 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
33178 rowClass : "x-grid-row",
33179 cellClass : "x-grid-cell",
33180 tdClass : "x-grid-td",
33181 hdClass : "x-grid-hd",
33182 splitClass : "x-grid-hd-split",
33184 init: function(grid){
33186 var cid = this.grid.getGridEl().id;
33187 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33188 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33189 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33190 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33193 getColumnRenderers : function(){
33194 var renderers = [];
33195 var cm = this.grid.colModel;
33196 var colCount = cm.getColumnCount();
33197 for(var i = 0; i < colCount; i++){
33198 renderers[i] = cm.getRenderer(i);
33203 getColumnIds : function(){
33205 var cm = this.grid.colModel;
33206 var colCount = cm.getColumnCount();
33207 for(var i = 0; i < colCount; i++){
33208 ids[i] = cm.getColumnId(i);
33213 getDataIndexes : function(){
33214 if(!this.indexMap){
33215 this.indexMap = this.buildIndexMap();
33217 return this.indexMap.colToData;
33220 getColumnIndexByDataIndex : function(dataIndex){
33221 if(!this.indexMap){
33222 this.indexMap = this.buildIndexMap();
33224 return this.indexMap.dataToCol[dataIndex];
33228 * Set a css style for a column dynamically.
33229 * @param {Number} colIndex The index of the column
33230 * @param {String} name The css property name
33231 * @param {String} value The css value
33233 setCSSStyle : function(colIndex, name, value){
33234 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33235 Roo.util.CSS.updateRule(selector, name, value);
33238 generateRules : function(cm){
33239 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33240 Roo.util.CSS.removeStyleSheet(rulesId);
33241 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33242 var cid = cm.getColumnId(i);
33243 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33244 this.tdSelector, cid, " {\n}\n",
33245 this.hdSelector, cid, " {\n}\n",
33246 this.splitSelector, cid, " {\n}\n");
33248 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33252 * Ext JS Library 1.1.1
33253 * Copyright(c) 2006-2007, Ext JS, LLC.
33255 * Originally Released Under LGPL - original licence link has changed is not relivant.
33258 * <script type="text/javascript">
33262 // This is a support class used internally by the Grid components
33263 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33265 this.view = grid.getView();
33266 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33267 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33269 this.setHandleElId(Roo.id(hd));
33270 this.setOuterHandleElId(Roo.id(hd2));
33272 this.scroll = false;
33274 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33276 getDragData : function(e){
33277 var t = Roo.lib.Event.getTarget(e);
33278 var h = this.view.findHeaderCell(t);
33280 return {ddel: h.firstChild, header:h};
33285 onInitDrag : function(e){
33286 this.view.headersDisabled = true;
33287 var clone = this.dragData.ddel.cloneNode(true);
33288 clone.id = Roo.id();
33289 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33290 this.proxy.update(clone);
33294 afterValidDrop : function(){
33296 setTimeout(function(){
33297 v.headersDisabled = false;
33301 afterInvalidDrop : function(){
33303 setTimeout(function(){
33304 v.headersDisabled = false;
33310 * Ext JS Library 1.1.1
33311 * Copyright(c) 2006-2007, Ext JS, LLC.
33313 * Originally Released Under LGPL - original licence link has changed is not relivant.
33316 * <script type="text/javascript">
33319 // This is a support class used internally by the Grid components
33320 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33322 this.view = grid.getView();
33323 // split the proxies so they don't interfere with mouse events
33324 this.proxyTop = Roo.DomHelper.append(document.body, {
33325 cls:"col-move-top", html:" "
33327 this.proxyBottom = Roo.DomHelper.append(document.body, {
33328 cls:"col-move-bottom", html:" "
33330 this.proxyTop.hide = this.proxyBottom.hide = function(){
33331 this.setLeftTop(-100,-100);
33332 this.setStyle("visibility", "hidden");
33334 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33335 // temporarily disabled
33336 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33337 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33339 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33340 proxyOffsets : [-4, -9],
33341 fly: Roo.Element.fly,
33343 getTargetFromEvent : function(e){
33344 var t = Roo.lib.Event.getTarget(e);
33345 var cindex = this.view.findCellIndex(t);
33346 if(cindex !== false){
33347 return this.view.getHeaderCell(cindex);
33352 nextVisible : function(h){
33353 var v = this.view, cm = this.grid.colModel;
33356 if(!cm.isHidden(v.getCellIndex(h))){
33364 prevVisible : function(h){
33365 var v = this.view, cm = this.grid.colModel;
33368 if(!cm.isHidden(v.getCellIndex(h))){
33376 positionIndicator : function(h, n, e){
33377 var x = Roo.lib.Event.getPageX(e);
33378 var r = Roo.lib.Dom.getRegion(n.firstChild);
33379 var px, pt, py = r.top + this.proxyOffsets[1];
33380 if((r.right - x) <= (r.right-r.left)/2){
33381 px = r.right+this.view.borderWidth;
33387 var oldIndex = this.view.getCellIndex(h);
33388 var newIndex = this.view.getCellIndex(n);
33390 if(this.grid.colModel.isFixed(newIndex)){
33394 var locked = this.grid.colModel.isLocked(newIndex);
33399 if(oldIndex < newIndex){
33402 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33405 px += this.proxyOffsets[0];
33406 this.proxyTop.setLeftTop(px, py);
33407 this.proxyTop.show();
33408 if(!this.bottomOffset){
33409 this.bottomOffset = this.view.mainHd.getHeight();
33411 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33412 this.proxyBottom.show();
33416 onNodeEnter : function(n, dd, e, data){
33417 if(data.header != n){
33418 this.positionIndicator(data.header, n, e);
33422 onNodeOver : function(n, dd, e, data){
33423 var result = false;
33424 if(data.header != n){
33425 result = this.positionIndicator(data.header, n, e);
33428 this.proxyTop.hide();
33429 this.proxyBottom.hide();
33431 return result ? this.dropAllowed : this.dropNotAllowed;
33434 onNodeOut : function(n, dd, e, data){
33435 this.proxyTop.hide();
33436 this.proxyBottom.hide();
33439 onNodeDrop : function(n, dd, e, data){
33440 var h = data.header;
33442 var cm = this.grid.colModel;
33443 var x = Roo.lib.Event.getPageX(e);
33444 var r = Roo.lib.Dom.getRegion(n.firstChild);
33445 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33446 var oldIndex = this.view.getCellIndex(h);
33447 var newIndex = this.view.getCellIndex(n);
33448 var locked = cm.isLocked(newIndex);
33452 if(oldIndex < newIndex){
33455 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33458 cm.setLocked(oldIndex, locked, true);
33459 cm.moveColumn(oldIndex, newIndex);
33460 this.grid.fireEvent("columnmove", oldIndex, newIndex);
33468 * Ext JS Library 1.1.1
33469 * Copyright(c) 2006-2007, Ext JS, LLC.
33471 * Originally Released Under LGPL - original licence link has changed is not relivant.
33474 * <script type="text/javascript">
33478 * @class Roo.grid.GridView
33479 * @extends Roo.util.Observable
33482 * @param {Object} config
33484 Roo.grid.GridView = function(config){
33485 Roo.grid.GridView.superclass.constructor.call(this);
33488 Roo.apply(this, config);
33491 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33493 unselectable : 'unselectable="on"',
33494 unselectableCls : 'x-unselectable',
33497 rowClass : "x-grid-row",
33499 cellClass : "x-grid-col",
33501 tdClass : "x-grid-td",
33503 hdClass : "x-grid-hd",
33505 splitClass : "x-grid-split",
33507 sortClasses : ["sort-asc", "sort-desc"],
33509 enableMoveAnim : false,
33513 dh : Roo.DomHelper,
33515 fly : Roo.Element.fly,
33517 css : Roo.util.CSS,
33523 scrollIncrement : 22,
33525 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33527 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33529 bind : function(ds, cm){
33531 this.ds.un("load", this.onLoad, this);
33532 this.ds.un("datachanged", this.onDataChange, this);
33533 this.ds.un("add", this.onAdd, this);
33534 this.ds.un("remove", this.onRemove, this);
33535 this.ds.un("update", this.onUpdate, this);
33536 this.ds.un("clear", this.onClear, this);
33539 ds.on("load", this.onLoad, this);
33540 ds.on("datachanged", this.onDataChange, this);
33541 ds.on("add", this.onAdd, this);
33542 ds.on("remove", this.onRemove, this);
33543 ds.on("update", this.onUpdate, this);
33544 ds.on("clear", this.onClear, this);
33549 this.cm.un("widthchange", this.onColWidthChange, this);
33550 this.cm.un("headerchange", this.onHeaderChange, this);
33551 this.cm.un("hiddenchange", this.onHiddenChange, this);
33552 this.cm.un("columnmoved", this.onColumnMove, this);
33553 this.cm.un("columnlockchange", this.onColumnLock, this);
33556 this.generateRules(cm);
33557 cm.on("widthchange", this.onColWidthChange, this);
33558 cm.on("headerchange", this.onHeaderChange, this);
33559 cm.on("hiddenchange", this.onHiddenChange, this);
33560 cm.on("columnmoved", this.onColumnMove, this);
33561 cm.on("columnlockchange", this.onColumnLock, this);
33566 init: function(grid){
33567 Roo.grid.GridView.superclass.init.call(this, grid);
33569 this.bind(grid.dataSource, grid.colModel);
33571 grid.on("headerclick", this.handleHeaderClick, this);
33573 if(grid.trackMouseOver){
33574 grid.on("mouseover", this.onRowOver, this);
33575 grid.on("mouseout", this.onRowOut, this);
33577 grid.cancelTextSelection = function(){};
33578 this.gridId = grid.id;
33580 var tpls = this.templates || {};
33583 tpls.master = new Roo.Template(
33584 '<div class="x-grid" hidefocus="true">',
33585 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33586 '<div class="x-grid-topbar"></div>',
33587 '<div class="x-grid-scroller"><div></div></div>',
33588 '<div class="x-grid-locked">',
33589 '<div class="x-grid-header">{lockedHeader}</div>',
33590 '<div class="x-grid-body">{lockedBody}</div>',
33592 '<div class="x-grid-viewport">',
33593 '<div class="x-grid-header">{header}</div>',
33594 '<div class="x-grid-body">{body}</div>',
33596 '<div class="x-grid-bottombar"></div>',
33598 '<div class="x-grid-resize-proxy"> </div>',
33601 tpls.master.disableformats = true;
33605 tpls.header = new Roo.Template(
33606 '<table border="0" cellspacing="0" cellpadding="0">',
33607 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33610 tpls.header.disableformats = true;
33612 tpls.header.compile();
33615 tpls.hcell = new Roo.Template(
33616 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33617 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33620 tpls.hcell.disableFormats = true;
33622 tpls.hcell.compile();
33625 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33626 this.unselectableCls + '" ' + this.unselectable +'> </div>');
33627 tpls.hsplit.disableFormats = true;
33629 tpls.hsplit.compile();
33632 tpls.body = new Roo.Template(
33633 '<table border="0" cellspacing="0" cellpadding="0">',
33634 "<tbody>{rows}</tbody>",
33637 tpls.body.disableFormats = true;
33639 tpls.body.compile();
33642 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33643 tpls.row.disableFormats = true;
33645 tpls.row.compile();
33648 tpls.cell = new Roo.Template(
33649 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33650 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33651 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33654 tpls.cell.disableFormats = true;
33656 tpls.cell.compile();
33658 this.templates = tpls;
33661 // remap these for backwards compat
33662 onColWidthChange : function(){
33663 this.updateColumns.apply(this, arguments);
33665 onHeaderChange : function(){
33666 this.updateHeaders.apply(this, arguments);
33668 onHiddenChange : function(){
33669 this.handleHiddenChange.apply(this, arguments);
33671 onColumnMove : function(){
33672 this.handleColumnMove.apply(this, arguments);
33674 onColumnLock : function(){
33675 this.handleLockChange.apply(this, arguments);
33678 onDataChange : function(){
33680 this.updateHeaderSortState();
33683 onClear : function(){
33687 onUpdate : function(ds, record){
33688 this.refreshRow(record);
33691 refreshRow : function(record){
33692 var ds = this.ds, index;
33693 if(typeof record == 'number'){
33695 record = ds.getAt(index);
33697 index = ds.indexOf(record);
33699 this.insertRows(ds, index, index, true);
33700 this.onRemove(ds, record, index+1, true);
33701 this.syncRowHeights(index, index);
33703 this.fireEvent("rowupdated", this, index, record);
33706 onAdd : function(ds, records, index){
33707 this.insertRows(ds, index, index + (records.length-1));
33710 onRemove : function(ds, record, index, isUpdate){
33711 if(isUpdate !== true){
33712 this.fireEvent("beforerowremoved", this, index, record);
33714 var bt = this.getBodyTable(), lt = this.getLockedTable();
33715 if(bt.rows[index]){
33716 bt.firstChild.removeChild(bt.rows[index]);
33718 if(lt.rows[index]){
33719 lt.firstChild.removeChild(lt.rows[index]);
33721 if(isUpdate !== true){
33722 this.stripeRows(index);
33723 this.syncRowHeights(index, index);
33725 this.fireEvent("rowremoved", this, index, record);
33729 onLoad : function(){
33730 this.scrollToTop();
33734 * Scrolls the grid to the top
33736 scrollToTop : function(){
33738 this.scroller.dom.scrollTop = 0;
33744 * Gets a panel in the header of the grid that can be used for toolbars etc.
33745 * After modifying the contents of this panel a call to grid.autoSize() may be
33746 * required to register any changes in size.
33747 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33748 * @return Roo.Element
33750 getHeaderPanel : function(doShow){
33752 this.headerPanel.show();
33754 return this.headerPanel;
33758 * Gets a panel in the footer of the grid that can be used for toolbars etc.
33759 * After modifying the contents of this panel a call to grid.autoSize() may be
33760 * required to register any changes in size.
33761 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33762 * @return Roo.Element
33764 getFooterPanel : function(doShow){
33766 this.footerPanel.show();
33768 return this.footerPanel;
33771 initElements : function(){
33772 var E = Roo.Element;
33773 var el = this.grid.getGridEl().dom.firstChild;
33774 var cs = el.childNodes;
33776 this.el = new E(el);
33778 this.focusEl = new E(el.firstChild);
33779 this.focusEl.swallowEvent("click", true);
33781 this.headerPanel = new E(cs[1]);
33782 this.headerPanel.enableDisplayMode("block");
33784 this.scroller = new E(cs[2]);
33785 this.scrollSizer = new E(this.scroller.dom.firstChild);
33787 this.lockedWrap = new E(cs[3]);
33788 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33789 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33791 this.mainWrap = new E(cs[4]);
33792 this.mainHd = new E(this.mainWrap.dom.firstChild);
33793 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33795 this.footerPanel = new E(cs[5]);
33796 this.footerPanel.enableDisplayMode("block");
33798 this.resizeProxy = new E(cs[6]);
33800 this.headerSelector = String.format(
33801 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33802 this.lockedHd.id, this.mainHd.id
33805 this.splitterSelector = String.format(
33806 '#{0} div.x-grid-split, #{1} div.x-grid-split',
33807 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33810 idToCssName : function(s)
33812 return s.replace(/[^a-z0-9]+/ig, '-');
33815 getHeaderCell : function(index){
33816 return Roo.DomQuery.select(this.headerSelector)[index];
33819 getHeaderCellMeasure : function(index){
33820 return this.getHeaderCell(index).firstChild;
33823 getHeaderCellText : function(index){
33824 return this.getHeaderCell(index).firstChild.firstChild;
33827 getLockedTable : function(){
33828 return this.lockedBody.dom.firstChild;
33831 getBodyTable : function(){
33832 return this.mainBody.dom.firstChild;
33835 getLockedRow : function(index){
33836 return this.getLockedTable().rows[index];
33839 getRow : function(index){
33840 return this.getBodyTable().rows[index];
33843 getRowComposite : function(index){
33845 this.rowEl = new Roo.CompositeElementLite();
33847 var els = [], lrow, mrow;
33848 if(lrow = this.getLockedRow(index)){
33851 if(mrow = this.getRow(index)){
33854 this.rowEl.elements = els;
33858 * Gets the 'td' of the cell
33860 * @param {Integer} rowIndex row to select
33861 * @param {Integer} colIndex column to select
33865 getCell : function(rowIndex, colIndex){
33866 var locked = this.cm.getLockedCount();
33868 if(colIndex < locked){
33869 source = this.lockedBody.dom.firstChild;
33871 source = this.mainBody.dom.firstChild;
33872 colIndex -= locked;
33874 return source.rows[rowIndex].childNodes[colIndex];
33877 getCellText : function(rowIndex, colIndex){
33878 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33881 getCellBox : function(cell){
33882 var b = this.fly(cell).getBox();
33883 if(Roo.isOpera){ // opera fails to report the Y
33884 b.y = cell.offsetTop + this.mainBody.getY();
33889 getCellIndex : function(cell){
33890 var id = String(cell.className).match(this.cellRE);
33892 return parseInt(id[1], 10);
33897 findHeaderIndex : function(n){
33898 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33899 return r ? this.getCellIndex(r) : false;
33902 findHeaderCell : function(n){
33903 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33904 return r ? r : false;
33907 findRowIndex : function(n){
33911 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33912 return r ? r.rowIndex : false;
33915 findCellIndex : function(node){
33916 var stop = this.el.dom;
33917 while(node && node != stop){
33918 if(this.findRE.test(node.className)){
33919 return this.getCellIndex(node);
33921 node = node.parentNode;
33926 getColumnId : function(index){
33927 return this.cm.getColumnId(index);
33930 getSplitters : function()
33932 if(this.splitterSelector){
33933 return Roo.DomQuery.select(this.splitterSelector);
33939 getSplitter : function(index){
33940 return this.getSplitters()[index];
33943 onRowOver : function(e, t){
33945 if((row = this.findRowIndex(t)) !== false){
33946 this.getRowComposite(row).addClass("x-grid-row-over");
33950 onRowOut : function(e, t){
33952 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33953 this.getRowComposite(row).removeClass("x-grid-row-over");
33957 renderHeaders : function(){
33959 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33960 var cb = [], lb = [], sb = [], lsb = [], p = {};
33961 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33962 p.cellId = "x-grid-hd-0-" + i;
33963 p.splitId = "x-grid-csplit-0-" + i;
33964 p.id = cm.getColumnId(i);
33965 p.value = cm.getColumnHeader(i) || "";
33966 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
33967 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33968 if(!cm.isLocked(i)){
33969 cb[cb.length] = ct.apply(p);
33970 sb[sb.length] = st.apply(p);
33972 lb[lb.length] = ct.apply(p);
33973 lsb[lsb.length] = st.apply(p);
33976 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33977 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33980 updateHeaders : function(){
33981 var html = this.renderHeaders();
33982 this.lockedHd.update(html[0]);
33983 this.mainHd.update(html[1]);
33987 * Focuses the specified row.
33988 * @param {Number} row The row index
33990 focusRow : function(row)
33992 //Roo.log('GridView.focusRow');
33993 var x = this.scroller.dom.scrollLeft;
33994 this.focusCell(row, 0, false);
33995 this.scroller.dom.scrollLeft = x;
33999 * Focuses the specified cell.
34000 * @param {Number} row The row index
34001 * @param {Number} col The column index
34002 * @param {Boolean} hscroll false to disable horizontal scrolling
34004 focusCell : function(row, col, hscroll)
34006 //Roo.log('GridView.focusCell');
34007 var el = this.ensureVisible(row, col, hscroll);
34008 this.focusEl.alignTo(el, "tl-tl");
34010 this.focusEl.focus();
34012 this.focusEl.focus.defer(1, this.focusEl);
34017 * Scrolls the specified cell into view
34018 * @param {Number} row The row index
34019 * @param {Number} col The column index
34020 * @param {Boolean} hscroll false to disable horizontal scrolling
34022 ensureVisible : function(row, col, hscroll)
34024 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
34025 //return null; //disable for testing.
34026 if(typeof row != "number"){
34027 row = row.rowIndex;
34029 if(row < 0 && row >= this.ds.getCount()){
34032 col = (col !== undefined ? col : 0);
34033 var cm = this.grid.colModel;
34034 while(cm.isHidden(col)){
34038 var el = this.getCell(row, col);
34042 var c = this.scroller.dom;
34044 var ctop = parseInt(el.offsetTop, 10);
34045 var cleft = parseInt(el.offsetLeft, 10);
34046 var cbot = ctop + el.offsetHeight;
34047 var cright = cleft + el.offsetWidth;
34049 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
34050 var stop = parseInt(c.scrollTop, 10);
34051 var sleft = parseInt(c.scrollLeft, 10);
34052 var sbot = stop + ch;
34053 var sright = sleft + c.clientWidth;
34055 Roo.log('GridView.ensureVisible:' +
34057 ' c.clientHeight:' + c.clientHeight +
34058 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
34066 c.scrollTop = ctop;
34067 //Roo.log("set scrolltop to ctop DISABLE?");
34068 }else if(cbot > sbot){
34069 //Roo.log("set scrolltop to cbot-ch");
34070 c.scrollTop = cbot-ch;
34073 if(hscroll !== false){
34075 c.scrollLeft = cleft;
34076 }else if(cright > sright){
34077 c.scrollLeft = cright-c.clientWidth;
34084 updateColumns : function(){
34085 this.grid.stopEditing();
34086 var cm = this.grid.colModel, colIds = this.getColumnIds();
34087 //var totalWidth = cm.getTotalWidth();
34089 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34090 //if(cm.isHidden(i)) continue;
34091 var w = cm.getColumnWidth(i);
34092 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34093 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34095 this.updateSplitters();
34098 generateRules : function(cm){
34099 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
34100 Roo.util.CSS.removeStyleSheet(rulesId);
34101 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34102 var cid = cm.getColumnId(i);
34104 if(cm.config[i].align){
34105 align = 'text-align:'+cm.config[i].align+';';
34108 if(cm.isHidden(i)){
34109 hidden = 'display:none;';
34111 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
34113 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
34114 this.hdSelector, cid, " {\n", align, width, "}\n",
34115 this.tdSelector, cid, " {\n",hidden,"\n}\n",
34116 this.splitSelector, cid, " {\n", hidden , "\n}\n");
34118 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34121 updateSplitters : function(){
34122 var cm = this.cm, s = this.getSplitters();
34123 if(s){ // splitters not created yet
34124 var pos = 0, locked = true;
34125 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34126 if(cm.isHidden(i)) {
34129 var w = cm.getColumnWidth(i); // make sure it's a number
34130 if(!cm.isLocked(i) && locked){
34135 s[i].style.left = (pos-this.splitOffset) + "px";
34140 handleHiddenChange : function(colModel, colIndex, hidden){
34142 this.hideColumn(colIndex);
34144 this.unhideColumn(colIndex);
34148 hideColumn : function(colIndex){
34149 var cid = this.getColumnId(colIndex);
34150 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
34151 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
34153 this.updateHeaders();
34155 this.updateSplitters();
34159 unhideColumn : function(colIndex){
34160 var cid = this.getColumnId(colIndex);
34161 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
34162 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
34165 this.updateHeaders();
34167 this.updateSplitters();
34171 insertRows : function(dm, firstRow, lastRow, isUpdate){
34172 if(firstRow == 0 && lastRow == dm.getCount()-1){
34176 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
34178 var s = this.getScrollState();
34179 var markup = this.renderRows(firstRow, lastRow);
34180 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
34181 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
34182 this.restoreScroll(s);
34184 this.fireEvent("rowsinserted", this, firstRow, lastRow);
34185 this.syncRowHeights(firstRow, lastRow);
34186 this.stripeRows(firstRow);
34192 bufferRows : function(markup, target, index){
34193 var before = null, trows = target.rows, tbody = target.tBodies[0];
34194 if(index < trows.length){
34195 before = trows[index];
34197 var b = document.createElement("div");
34198 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34199 var rows = b.firstChild.rows;
34200 for(var i = 0, len = rows.length; i < len; i++){
34202 tbody.insertBefore(rows[0], before);
34204 tbody.appendChild(rows[0]);
34211 deleteRows : function(dm, firstRow, lastRow){
34212 if(dm.getRowCount()<1){
34213 this.fireEvent("beforerefresh", this);
34214 this.mainBody.update("");
34215 this.lockedBody.update("");
34216 this.fireEvent("refresh", this);
34218 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34219 var bt = this.getBodyTable();
34220 var tbody = bt.firstChild;
34221 var rows = bt.rows;
34222 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34223 tbody.removeChild(rows[firstRow]);
34225 this.stripeRows(firstRow);
34226 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34230 updateRows : function(dataSource, firstRow, lastRow){
34231 var s = this.getScrollState();
34233 this.restoreScroll(s);
34236 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34240 this.updateHeaderSortState();
34243 getScrollState : function(){
34245 var sb = this.scroller.dom;
34246 return {left: sb.scrollLeft, top: sb.scrollTop};
34249 stripeRows : function(startRow){
34250 if(!this.grid.stripeRows || this.ds.getCount() < 1){
34253 startRow = startRow || 0;
34254 var rows = this.getBodyTable().rows;
34255 var lrows = this.getLockedTable().rows;
34256 var cls = ' x-grid-row-alt ';
34257 for(var i = startRow, len = rows.length; i < len; i++){
34258 var row = rows[i], lrow = lrows[i];
34259 var isAlt = ((i+1) % 2 == 0);
34260 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34261 if(isAlt == hasAlt){
34265 row.className += " x-grid-row-alt";
34267 row.className = row.className.replace("x-grid-row-alt", "");
34270 lrow.className = row.className;
34275 restoreScroll : function(state){
34276 //Roo.log('GridView.restoreScroll');
34277 var sb = this.scroller.dom;
34278 sb.scrollLeft = state.left;
34279 sb.scrollTop = state.top;
34283 syncScroll : function(){
34284 //Roo.log('GridView.syncScroll');
34285 var sb = this.scroller.dom;
34286 var sh = this.mainHd.dom;
34287 var bs = this.mainBody.dom;
34288 var lv = this.lockedBody.dom;
34289 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34290 lv.scrollTop = bs.scrollTop = sb.scrollTop;
34293 handleScroll : function(e){
34295 var sb = this.scroller.dom;
34296 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34300 handleWheel : function(e){
34301 var d = e.getWheelDelta();
34302 this.scroller.dom.scrollTop -= d*22;
34303 // set this here to prevent jumpy scrolling on large tables
34304 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34308 renderRows : function(startRow, endRow){
34309 // pull in all the crap needed to render rows
34310 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34311 var colCount = cm.getColumnCount();
34313 if(ds.getCount() < 1){
34317 // build a map for all the columns
34319 for(var i = 0; i < colCount; i++){
34320 var name = cm.getDataIndex(i);
34322 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34323 renderer : cm.getRenderer(i),
34324 id : cm.getColumnId(i),
34325 locked : cm.isLocked(i),
34326 has_editor : cm.isCellEditable(i)
34330 startRow = startRow || 0;
34331 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34333 // records to render
34334 var rs = ds.getRange(startRow, endRow);
34336 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34339 // As much as I hate to duplicate code, this was branched because FireFox really hates
34340 // [].join("") on strings. The performance difference was substantial enough to
34341 // branch this function
34342 doRender : Roo.isGecko ?
34343 function(cs, rs, ds, startRow, colCount, stripe){
34344 var ts = this.templates, ct = ts.cell, rt = ts.row;
34346 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34348 var hasListener = this.grid.hasListener('rowclass');
34350 for(var j = 0, len = rs.length; j < len; j++){
34351 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34352 for(var i = 0; i < colCount; i++){
34354 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34356 p.css = p.attr = "";
34357 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34358 if(p.value == undefined || p.value === "") {
34359 p.value = " ";
34362 p.css += ' x-grid-editable-cell';
34364 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34365 p.css += ' x-grid-dirty-cell';
34367 var markup = ct.apply(p);
34375 if(stripe && ((rowIndex+1) % 2 == 0)){
34376 alt.push("x-grid-row-alt")
34379 alt.push( " x-grid-dirty-row");
34382 if(this.getRowClass){
34383 alt.push(this.getRowClass(r, rowIndex));
34389 rowIndex : rowIndex,
34392 this.grid.fireEvent('rowclass', this, rowcfg);
34393 alt.push(rowcfg.rowClass);
34395 rp.alt = alt.join(" ");
34396 lbuf+= rt.apply(rp);
34398 buf+= rt.apply(rp);
34400 return [lbuf, buf];
34402 function(cs, rs, ds, startRow, colCount, stripe){
34403 var ts = this.templates, ct = ts.cell, rt = ts.row;
34405 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34406 var hasListener = this.grid.hasListener('rowclass');
34409 for(var j = 0, len = rs.length; j < len; j++){
34410 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34411 for(var i = 0; i < colCount; i++){
34413 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34415 p.css = p.attr = "";
34416 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34417 if(p.value == undefined || p.value === "") {
34418 p.value = " ";
34422 p.css += ' x-grid-editable-cell';
34424 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34425 p.css += ' x-grid-dirty-cell'
34428 var markup = ct.apply(p);
34430 cb[cb.length] = markup;
34432 lcb[lcb.length] = markup;
34436 if(stripe && ((rowIndex+1) % 2 == 0)){
34437 alt.push( "x-grid-row-alt");
34440 alt.push(" x-grid-dirty-row");
34443 if(this.getRowClass){
34444 alt.push( this.getRowClass(r, rowIndex));
34450 rowIndex : rowIndex,
34453 this.grid.fireEvent('rowclass', this, rowcfg);
34454 alt.push(rowcfg.rowClass);
34457 rp.alt = alt.join(" ");
34458 rp.cells = lcb.join("");
34459 lbuf[lbuf.length] = rt.apply(rp);
34460 rp.cells = cb.join("");
34461 buf[buf.length] = rt.apply(rp);
34463 return [lbuf.join(""), buf.join("")];
34466 renderBody : function(){
34467 var markup = this.renderRows();
34468 var bt = this.templates.body;
34469 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34473 * Refreshes the grid
34474 * @param {Boolean} headersToo
34476 refresh : function(headersToo){
34477 this.fireEvent("beforerefresh", this);
34478 this.grid.stopEditing();
34479 var result = this.renderBody();
34480 this.lockedBody.update(result[0]);
34481 this.mainBody.update(result[1]);
34482 if(headersToo === true){
34483 this.updateHeaders();
34484 this.updateColumns();
34485 this.updateSplitters();
34486 this.updateHeaderSortState();
34488 this.syncRowHeights();
34490 this.fireEvent("refresh", this);
34493 handleColumnMove : function(cm, oldIndex, newIndex){
34494 this.indexMap = null;
34495 var s = this.getScrollState();
34496 this.refresh(true);
34497 this.restoreScroll(s);
34498 this.afterMove(newIndex);
34501 afterMove : function(colIndex){
34502 if(this.enableMoveAnim && Roo.enableFx){
34503 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34505 // if multisort - fix sortOrder, and reload..
34506 if (this.grid.dataSource.multiSort) {
34507 // the we can call sort again..
34508 var dm = this.grid.dataSource;
34509 var cm = this.grid.colModel;
34511 for(var i = 0; i < cm.config.length; i++ ) {
34513 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34514 continue; // dont' bother, it's not in sort list or being set.
34517 so.push(cm.config[i].dataIndex);
34520 dm.load(dm.lastOptions);
34527 updateCell : function(dm, rowIndex, dataIndex){
34528 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34529 if(typeof colIndex == "undefined"){ // not present in grid
34532 var cm = this.grid.colModel;
34533 var cell = this.getCell(rowIndex, colIndex);
34534 var cellText = this.getCellText(rowIndex, colIndex);
34537 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34538 id : cm.getColumnId(colIndex),
34539 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34541 var renderer = cm.getRenderer(colIndex);
34542 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34543 if(typeof val == "undefined" || val === "") {
34546 cellText.innerHTML = val;
34547 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34548 this.syncRowHeights(rowIndex, rowIndex);
34551 calcColumnWidth : function(colIndex, maxRowsToMeasure){
34553 if(this.grid.autoSizeHeaders){
34554 var h = this.getHeaderCellMeasure(colIndex);
34555 maxWidth = Math.max(maxWidth, h.scrollWidth);
34558 if(this.cm.isLocked(colIndex)){
34559 tb = this.getLockedTable();
34562 tb = this.getBodyTable();
34563 index = colIndex - this.cm.getLockedCount();
34566 var rows = tb.rows;
34567 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34568 for(var i = 0; i < stopIndex; i++){
34569 var cell = rows[i].childNodes[index].firstChild;
34570 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34573 return maxWidth + /*margin for error in IE*/ 5;
34576 * Autofit a column to its content.
34577 * @param {Number} colIndex
34578 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34580 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34581 if(this.cm.isHidden(colIndex)){
34582 return; // can't calc a hidden column
34585 var cid = this.cm.getColumnId(colIndex);
34586 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34587 if(this.grid.autoSizeHeaders){
34588 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34591 var newWidth = this.calcColumnWidth(colIndex);
34592 this.cm.setColumnWidth(colIndex,
34593 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34594 if(!suppressEvent){
34595 this.grid.fireEvent("columnresize", colIndex, newWidth);
34600 * Autofits all columns to their content and then expands to fit any extra space in the grid
34602 autoSizeColumns : function(){
34603 var cm = this.grid.colModel;
34604 var colCount = cm.getColumnCount();
34605 for(var i = 0; i < colCount; i++){
34606 this.autoSizeColumn(i, true, true);
34608 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34611 this.updateColumns();
34617 * Autofits all columns to the grid's width proportionate with their current size
34618 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34620 fitColumns : function(reserveScrollSpace){
34621 var cm = this.grid.colModel;
34622 var colCount = cm.getColumnCount();
34626 for (i = 0; i < colCount; i++){
34627 if(!cm.isHidden(i) && !cm.isFixed(i)){
34628 w = cm.getColumnWidth(i);
34634 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34635 if(reserveScrollSpace){
34638 var frac = (avail - cm.getTotalWidth())/width;
34639 while (cols.length){
34642 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34644 this.updateColumns();
34648 onRowSelect : function(rowIndex){
34649 var row = this.getRowComposite(rowIndex);
34650 row.addClass("x-grid-row-selected");
34653 onRowDeselect : function(rowIndex){
34654 var row = this.getRowComposite(rowIndex);
34655 row.removeClass("x-grid-row-selected");
34658 onCellSelect : function(row, col){
34659 var cell = this.getCell(row, col);
34661 Roo.fly(cell).addClass("x-grid-cell-selected");
34665 onCellDeselect : function(row, col){
34666 var cell = this.getCell(row, col);
34668 Roo.fly(cell).removeClass("x-grid-cell-selected");
34672 updateHeaderSortState : function(){
34674 // sort state can be single { field: xxx, direction : yyy}
34675 // or { xxx=>ASC , yyy : DESC ..... }
34678 if (!this.ds.multiSort) {
34679 var state = this.ds.getSortState();
34683 mstate[state.field] = state.direction;
34684 // FIXME... - this is not used here.. but might be elsewhere..
34685 this.sortState = state;
34688 mstate = this.ds.sortToggle;
34690 //remove existing sort classes..
34692 var sc = this.sortClasses;
34693 var hds = this.el.select(this.headerSelector).removeClass(sc);
34695 for(var f in mstate) {
34697 var sortColumn = this.cm.findColumnIndex(f);
34699 if(sortColumn != -1){
34700 var sortDir = mstate[f];
34701 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34710 handleHeaderClick : function(g, index,e){
34712 Roo.log("header click");
34715 // touch events on header are handled by context
34716 this.handleHdCtx(g,index,e);
34721 if(this.headersDisabled){
34724 var dm = g.dataSource, cm = g.colModel;
34725 if(!cm.isSortable(index)){
34730 if (dm.multiSort) {
34731 // update the sortOrder
34733 for(var i = 0; i < cm.config.length; i++ ) {
34735 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34736 continue; // dont' bother, it's not in sort list or being set.
34739 so.push(cm.config[i].dataIndex);
34745 dm.sort(cm.getDataIndex(index));
34749 destroy : function(){
34751 this.colMenu.removeAll();
34752 Roo.menu.MenuMgr.unregister(this.colMenu);
34753 this.colMenu.getEl().remove();
34754 delete this.colMenu;
34757 this.hmenu.removeAll();
34758 Roo.menu.MenuMgr.unregister(this.hmenu);
34759 this.hmenu.getEl().remove();
34762 if(this.grid.enableColumnMove){
34763 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34765 for(var dd in dds){
34766 if(!dds[dd].config.isTarget && dds[dd].dragElId){
34767 var elid = dds[dd].dragElId;
34769 Roo.get(elid).remove();
34770 } else if(dds[dd].config.isTarget){
34771 dds[dd].proxyTop.remove();
34772 dds[dd].proxyBottom.remove();
34775 if(Roo.dd.DDM.locationCache[dd]){
34776 delete Roo.dd.DDM.locationCache[dd];
34779 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34782 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34783 this.bind(null, null);
34784 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34787 handleLockChange : function(){
34788 this.refresh(true);
34791 onDenyColumnLock : function(){
34795 onDenyColumnHide : function(){
34799 handleHdMenuClick : function(item){
34800 var index = this.hdCtxIndex;
34801 var cm = this.cm, ds = this.ds;
34804 ds.sort(cm.getDataIndex(index), "ASC");
34807 ds.sort(cm.getDataIndex(index), "DESC");
34810 var lc = cm.getLockedCount();
34811 if(cm.getColumnCount(true) <= lc+1){
34812 this.onDenyColumnLock();
34816 cm.setLocked(index, true, true);
34817 cm.moveColumn(index, lc);
34818 this.grid.fireEvent("columnmove", index, lc);
34820 cm.setLocked(index, true);
34824 var lc = cm.getLockedCount();
34825 if((lc-1) != index){
34826 cm.setLocked(index, false, true);
34827 cm.moveColumn(index, lc-1);
34828 this.grid.fireEvent("columnmove", index, lc-1);
34830 cm.setLocked(index, false);
34833 case 'wider': // used to expand cols on touch..
34835 var cw = cm.getColumnWidth(index);
34836 cw += (item.id == 'wider' ? 1 : -1) * 50;
34837 cw = Math.max(0, cw);
34838 cw = Math.min(cw,4000);
34839 cm.setColumnWidth(index, cw);
34843 index = cm.getIndexById(item.id.substr(4));
34845 if(item.checked && cm.getColumnCount(true) <= 1){
34846 this.onDenyColumnHide();
34849 cm.setHidden(index, item.checked);
34855 beforeColMenuShow : function(){
34856 var cm = this.cm, colCount = cm.getColumnCount();
34857 this.colMenu.removeAll();
34858 for(var i = 0; i < colCount; i++){
34859 this.colMenu.add(new Roo.menu.CheckItem({
34860 id: "col-"+cm.getColumnId(i),
34861 text: cm.getColumnHeader(i),
34862 checked: !cm.isHidden(i),
34868 handleHdCtx : function(g, index, e){
34870 var hd = this.getHeaderCell(index);
34871 this.hdCtxIndex = index;
34872 var ms = this.hmenu.items, cm = this.cm;
34873 ms.get("asc").setDisabled(!cm.isSortable(index));
34874 ms.get("desc").setDisabled(!cm.isSortable(index));
34875 if(this.grid.enableColLock !== false){
34876 ms.get("lock").setDisabled(cm.isLocked(index));
34877 ms.get("unlock").setDisabled(!cm.isLocked(index));
34879 this.hmenu.show(hd, "tl-bl");
34882 handleHdOver : function(e){
34883 var hd = this.findHeaderCell(e.getTarget());
34884 if(hd && !this.headersDisabled){
34885 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34886 this.fly(hd).addClass("x-grid-hd-over");
34891 handleHdOut : function(e){
34892 var hd = this.findHeaderCell(e.getTarget());
34894 this.fly(hd).removeClass("x-grid-hd-over");
34898 handleSplitDblClick : function(e, t){
34899 var i = this.getCellIndex(t);
34900 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34901 this.autoSizeColumn(i, true);
34906 render : function(){
34909 var colCount = cm.getColumnCount();
34911 if(this.grid.monitorWindowResize === true){
34912 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34914 var header = this.renderHeaders();
34915 var body = this.templates.body.apply({rows:""});
34916 var html = this.templates.master.apply({
34919 lockedHeader: header[0],
34923 //this.updateColumns();
34925 this.grid.getGridEl().dom.innerHTML = html;
34927 this.initElements();
34929 // a kludge to fix the random scolling effect in webkit
34930 this.el.on("scroll", function() {
34931 this.el.dom.scrollTop=0; // hopefully not recursive..
34934 this.scroller.on("scroll", this.handleScroll, this);
34935 this.lockedBody.on("mousewheel", this.handleWheel, this);
34936 this.mainBody.on("mousewheel", this.handleWheel, this);
34938 this.mainHd.on("mouseover", this.handleHdOver, this);
34939 this.mainHd.on("mouseout", this.handleHdOut, this);
34940 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34941 {delegate: "."+this.splitClass});
34943 this.lockedHd.on("mouseover", this.handleHdOver, this);
34944 this.lockedHd.on("mouseout", this.handleHdOut, this);
34945 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34946 {delegate: "."+this.splitClass});
34948 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34949 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34952 this.updateSplitters();
34954 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34955 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34956 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34959 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34960 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34962 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34963 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34965 if(this.grid.enableColLock !== false){
34966 this.hmenu.add('-',
34967 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34968 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34972 this.hmenu.add('-',
34973 {id:"wider", text: this.columnsWiderText},
34974 {id:"narrow", text: this.columnsNarrowText }
34980 if(this.grid.enableColumnHide !== false){
34982 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34983 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34984 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34986 this.hmenu.add('-',
34987 {id:"columns", text: this.columnsText, menu: this.colMenu}
34990 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34992 this.grid.on("headercontextmenu", this.handleHdCtx, this);
34995 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34996 this.dd = new Roo.grid.GridDragZone(this.grid, {
34997 ddGroup : this.grid.ddGroup || 'GridDD'
35003 for(var i = 0; i < colCount; i++){
35004 if(cm.isHidden(i)){
35005 this.hideColumn(i);
35007 if(cm.config[i].align){
35008 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
35009 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
35013 this.updateHeaderSortState();
35015 this.beforeInitialResize();
35018 // two part rendering gives faster view to the user
35019 this.renderPhase2.defer(1, this);
35022 renderPhase2 : function(){
35023 // render the rows now
35025 if(this.grid.autoSizeColumns){
35026 this.autoSizeColumns();
35030 beforeInitialResize : function(){
35034 onColumnSplitterMoved : function(i, w){
35035 this.userResized = true;
35036 var cm = this.grid.colModel;
35037 cm.setColumnWidth(i, w, true);
35038 var cid = cm.getColumnId(i);
35039 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35040 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35041 this.updateSplitters();
35043 this.grid.fireEvent("columnresize", i, w);
35046 syncRowHeights : function(startIndex, endIndex){
35047 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
35048 startIndex = startIndex || 0;
35049 var mrows = this.getBodyTable().rows;
35050 var lrows = this.getLockedTable().rows;
35051 var len = mrows.length-1;
35052 endIndex = Math.min(endIndex || len, len);
35053 for(var i = startIndex; i <= endIndex; i++){
35054 var m = mrows[i], l = lrows[i];
35055 var h = Math.max(m.offsetHeight, l.offsetHeight);
35056 m.style.height = l.style.height = h + "px";
35061 layout : function(initialRender, is2ndPass){
35063 var auto = g.autoHeight;
35064 var scrollOffset = 16;
35065 var c = g.getGridEl(), cm = this.cm,
35066 expandCol = g.autoExpandColumn,
35068 //c.beginMeasure();
35070 if(!c.dom.offsetWidth){ // display:none?
35072 this.lockedWrap.show();
35073 this.mainWrap.show();
35078 var hasLock = this.cm.isLocked(0);
35080 var tbh = this.headerPanel.getHeight();
35081 var bbh = this.footerPanel.getHeight();
35084 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
35085 var newHeight = ch + c.getBorderWidth("tb");
35087 newHeight = Math.min(g.maxHeight, newHeight);
35089 c.setHeight(newHeight);
35093 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
35096 var s = this.scroller;
35098 var csize = c.getSize(true);
35100 this.el.setSize(csize.width, csize.height);
35102 this.headerPanel.setWidth(csize.width);
35103 this.footerPanel.setWidth(csize.width);
35105 var hdHeight = this.mainHd.getHeight();
35106 var vw = csize.width;
35107 var vh = csize.height - (tbh + bbh);
35111 var bt = this.getBodyTable();
35113 if(cm.getLockedCount() == cm.config.length){
35114 bt = this.getLockedTable();
35117 var ltWidth = hasLock ?
35118 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
35120 var scrollHeight = bt.offsetHeight;
35121 var scrollWidth = ltWidth + bt.offsetWidth;
35122 var vscroll = false, hscroll = false;
35124 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
35126 var lw = this.lockedWrap, mw = this.mainWrap;
35127 var lb = this.lockedBody, mb = this.mainBody;
35129 setTimeout(function(){
35130 var t = s.dom.offsetTop;
35131 var w = s.dom.clientWidth,
35132 h = s.dom.clientHeight;
35135 lw.setSize(ltWidth, h);
35137 mw.setLeftTop(ltWidth, t);
35138 mw.setSize(w-ltWidth, h);
35140 lb.setHeight(h-hdHeight);
35141 mb.setHeight(h-hdHeight);
35143 if(is2ndPass !== true && !gv.userResized && expandCol){
35144 // high speed resize without full column calculation
35146 var ci = cm.getIndexById(expandCol);
35148 ci = cm.findColumnIndex(expandCol);
35150 ci = Math.max(0, ci); // make sure it's got at least the first col.
35151 var expandId = cm.getColumnId(ci);
35152 var tw = cm.getTotalWidth(false);
35153 var currentWidth = cm.getColumnWidth(ci);
35154 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
35155 if(currentWidth != cw){
35156 cm.setColumnWidth(ci, cw, true);
35157 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35158 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35159 gv.updateSplitters();
35160 gv.layout(false, true);
35172 onWindowResize : function(){
35173 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
35179 appendFooter : function(parentEl){
35183 sortAscText : "Sort Ascending",
35184 sortDescText : "Sort Descending",
35185 lockText : "Lock Column",
35186 unlockText : "Unlock Column",
35187 columnsText : "Columns",
35189 columnsWiderText : "Wider",
35190 columnsNarrowText : "Thinner"
35194 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35195 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35196 this.proxy.el.addClass('x-grid3-col-dd');
35199 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35200 handleMouseDown : function(e){
35204 callHandleMouseDown : function(e){
35205 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35210 * Ext JS Library 1.1.1
35211 * Copyright(c) 2006-2007, Ext JS, LLC.
35213 * Originally Released Under LGPL - original licence link has changed is not relivant.
35216 * <script type="text/javascript">
35220 // This is a support class used internally by the Grid components
35221 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35223 this.view = grid.getView();
35224 this.proxy = this.view.resizeProxy;
35225 Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
35226 "gridSplitters" + this.grid.getGridEl().id, {
35227 dragElId : Roo.id(this.proxy.dom), resizeFrame:false
35229 this.setHandleElId(Roo.id(hd));
35230 this.setOuterHandleElId(Roo.id(hd2));
35231 this.scroll = false;
35233 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35234 fly: Roo.Element.fly,
35236 b4StartDrag : function(x, y){
35237 this.view.headersDisabled = true;
35238 this.proxy.setHeight(this.view.mainWrap.getHeight());
35239 var w = this.cm.getColumnWidth(this.cellIndex);
35240 var minw = Math.max(w-this.grid.minColumnWidth, 0);
35241 this.resetConstraints();
35242 this.setXConstraint(minw, 1000);
35243 this.setYConstraint(0, 0);
35244 this.minX = x - minw;
35245 this.maxX = x + 1000;
35247 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35251 handleMouseDown : function(e){
35252 ev = Roo.EventObject.setEvent(e);
35253 var t = this.fly(ev.getTarget());
35254 if(t.hasClass("x-grid-split")){
35255 this.cellIndex = this.view.getCellIndex(t.dom);
35256 this.split = t.dom;
35257 this.cm = this.grid.colModel;
35258 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35259 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35264 endDrag : function(e){
35265 this.view.headersDisabled = false;
35266 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35267 var diff = endX - this.startPos;
35268 this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
35271 autoOffset : function(){
35272 this.setDelta(0,0);
35276 * Ext JS Library 1.1.1
35277 * Copyright(c) 2006-2007, Ext JS, LLC.
35279 * Originally Released Under LGPL - original licence link has changed is not relivant.
35282 * <script type="text/javascript">
35286 // This is a support class used internally by the Grid components
35287 Roo.grid.GridDragZone = function(grid, config){
35288 this.view = grid.getView();
35289 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35290 if(this.view.lockedBody){
35291 this.setHandleElId(Roo.id(this.view.mainBody.dom));
35292 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35294 this.scroll = false;
35296 this.ddel = document.createElement('div');
35297 this.ddel.className = 'x-grid-dd-wrap';
35300 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35301 ddGroup : "GridDD",
35303 getDragData : function(e){
35304 var t = Roo.lib.Event.getTarget(e);
35305 var rowIndex = this.view.findRowIndex(t);
35306 var sm = this.grid.selModel;
35308 //Roo.log(rowIndex);
35310 if (sm.getSelectedCell) {
35311 // cell selection..
35312 if (!sm.getSelectedCell()) {
35315 if (rowIndex != sm.getSelectedCell()[0]) {
35321 if(rowIndex !== false){
35326 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35328 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35331 if (e.hasModifier()){
35332 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35335 Roo.log("getDragData");
35340 rowIndex: rowIndex,
35341 selections:sm.getSelections ? sm.getSelections() : (
35342 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
35349 onInitDrag : function(e){
35350 var data = this.dragData;
35351 this.ddel.innerHTML = this.grid.getDragDropText();
35352 this.proxy.update(this.ddel);
35353 // fire start drag?
35356 afterRepair : function(){
35357 this.dragging = false;
35360 getRepairXY : function(e, data){
35364 onEndDrag : function(data, e){
35368 onValidDrop : function(dd, e, id){
35373 beforeInvalidDrop : function(e, id){
35378 * Ext JS Library 1.1.1
35379 * Copyright(c) 2006-2007, Ext JS, LLC.
35381 * Originally Released Under LGPL - original licence link has changed is not relivant.
35384 * <script type="text/javascript">
35389 * @class Roo.grid.ColumnModel
35390 * @extends Roo.util.Observable
35391 * This is the default implementation of a ColumnModel used by the Grid. It defines
35392 * the columns in the grid.
35395 var colModel = new Roo.grid.ColumnModel([
35396 {header: "Ticker", width: 60, sortable: true, locked: true},
35397 {header: "Company Name", width: 150, sortable: true},
35398 {header: "Market Cap.", width: 100, sortable: true},
35399 {header: "$ Sales", width: 100, sortable: true, renderer: money},
35400 {header: "Employees", width: 100, sortable: true, resizable: false}
35405 * The config options listed for this class are options which may appear in each
35406 * individual column definition.
35407 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35409 * @param {Object} config An Array of column config objects. See this class's
35410 * config objects for details.
35412 Roo.grid.ColumnModel = function(config){
35414 * The config passed into the constructor
35416 this.config = config;
35419 // if no id, create one
35420 // if the column does not have a dataIndex mapping,
35421 // map it to the order it is in the config
35422 for(var i = 0, len = config.length; i < len; i++){
35424 if(typeof c.dataIndex == "undefined"){
35427 if(typeof c.renderer == "string"){
35428 c.renderer = Roo.util.Format[c.renderer];
35430 if(typeof c.id == "undefined"){
35433 if(c.editor && c.editor.xtype){
35434 c.editor = Roo.factory(c.editor, Roo.grid);
35436 if(c.editor && c.editor.isFormField){
35437 c.editor = new Roo.grid.GridEditor(c.editor);
35439 this.lookup[c.id] = c;
35443 * The width of columns which have no width specified (defaults to 100)
35446 this.defaultWidth = 100;
35449 * Default sortable of columns which have no sortable specified (defaults to false)
35452 this.defaultSortable = false;
35456 * @event widthchange
35457 * Fires when the width of a column changes.
35458 * @param {ColumnModel} this
35459 * @param {Number} columnIndex The column index
35460 * @param {Number} newWidth The new width
35462 "widthchange": true,
35464 * @event headerchange
35465 * Fires when the text of a header changes.
35466 * @param {ColumnModel} this
35467 * @param {Number} columnIndex The column index
35468 * @param {Number} newText The new header text
35470 "headerchange": true,
35472 * @event hiddenchange
35473 * Fires when a column is hidden or "unhidden".
35474 * @param {ColumnModel} this
35475 * @param {Number} columnIndex The column index
35476 * @param {Boolean} hidden true if hidden, false otherwise
35478 "hiddenchange": true,
35480 * @event columnmoved
35481 * Fires when a column is moved.
35482 * @param {ColumnModel} this
35483 * @param {Number} oldIndex
35484 * @param {Number} newIndex
35486 "columnmoved" : true,
35488 * @event columlockchange
35489 * Fires when a column's locked state is changed
35490 * @param {ColumnModel} this
35491 * @param {Number} colIndex
35492 * @param {Boolean} locked true if locked
35494 "columnlockchange" : true
35496 Roo.grid.ColumnModel.superclass.constructor.call(this);
35498 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35500 * @cfg {String} header The header text to display in the Grid view.
35503 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35504 * {@link Roo.data.Record} definition from which to draw the column's value. If not
35505 * specified, the column's index is used as an index into the Record's data Array.
35508 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35509 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35512 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35513 * Defaults to the value of the {@link #defaultSortable} property.
35514 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35517 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
35520 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
35523 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35526 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35529 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35530 * given the cell's data value. See {@link #setRenderer}. If not specified, the
35531 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35532 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35535 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
35538 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
35541 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
35544 * @cfg {String} cursor (Optional)
35547 * @cfg {String} tooltip (Optional)
35550 * @cfg {Number} xs (Optional)
35553 * @cfg {Number} sm (Optional)
35556 * @cfg {Number} md (Optional)
35559 * @cfg {Number} lg (Optional)
35562 * Returns the id of the column at the specified index.
35563 * @param {Number} index The column index
35564 * @return {String} the id
35566 getColumnId : function(index){
35567 return this.config[index].id;
35571 * Returns the column for a specified id.
35572 * @param {String} id The column id
35573 * @return {Object} the column
35575 getColumnById : function(id){
35576 return this.lookup[id];
35581 * Returns the column for a specified dataIndex.
35582 * @param {String} dataIndex The column dataIndex
35583 * @return {Object|Boolean} the column or false if not found
35585 getColumnByDataIndex: function(dataIndex){
35586 var index = this.findColumnIndex(dataIndex);
35587 return index > -1 ? this.config[index] : false;
35591 * Returns the index for a specified column id.
35592 * @param {String} id The column id
35593 * @return {Number} the index, or -1 if not found
35595 getIndexById : function(id){
35596 for(var i = 0, len = this.config.length; i < len; i++){
35597 if(this.config[i].id == id){
35605 * Returns the index for a specified column dataIndex.
35606 * @param {String} dataIndex The column dataIndex
35607 * @return {Number} the index, or -1 if not found
35610 findColumnIndex : function(dataIndex){
35611 for(var i = 0, len = this.config.length; i < len; i++){
35612 if(this.config[i].dataIndex == dataIndex){
35620 moveColumn : function(oldIndex, newIndex){
35621 var c = this.config[oldIndex];
35622 this.config.splice(oldIndex, 1);
35623 this.config.splice(newIndex, 0, c);
35624 this.dataMap = null;
35625 this.fireEvent("columnmoved", this, oldIndex, newIndex);
35628 isLocked : function(colIndex){
35629 return this.config[colIndex].locked === true;
35632 setLocked : function(colIndex, value, suppressEvent){
35633 if(this.isLocked(colIndex) == value){
35636 this.config[colIndex].locked = value;
35637 if(!suppressEvent){
35638 this.fireEvent("columnlockchange", this, colIndex, value);
35642 getTotalLockedWidth : function(){
35643 var totalWidth = 0;
35644 for(var i = 0; i < this.config.length; i++){
35645 if(this.isLocked(i) && !this.isHidden(i)){
35646 this.totalWidth += this.getColumnWidth(i);
35652 getLockedCount : function(){
35653 for(var i = 0, len = this.config.length; i < len; i++){
35654 if(!this.isLocked(i)){
35659 return this.config.length;
35663 * Returns the number of columns.
35666 getColumnCount : function(visibleOnly){
35667 if(visibleOnly === true){
35669 for(var i = 0, len = this.config.length; i < len; i++){
35670 if(!this.isHidden(i)){
35676 return this.config.length;
35680 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35681 * @param {Function} fn
35682 * @param {Object} scope (optional)
35683 * @return {Array} result
35685 getColumnsBy : function(fn, scope){
35687 for(var i = 0, len = this.config.length; i < len; i++){
35688 var c = this.config[i];
35689 if(fn.call(scope||this, c, i) === true){
35697 * Returns true if the specified column is sortable.
35698 * @param {Number} col The column index
35699 * @return {Boolean}
35701 isSortable : function(col){
35702 if(typeof this.config[col].sortable == "undefined"){
35703 return this.defaultSortable;
35705 return this.config[col].sortable;
35709 * Returns the rendering (formatting) function defined for the column.
35710 * @param {Number} col The column index.
35711 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35713 getRenderer : function(col){
35714 if(!this.config[col].renderer){
35715 return Roo.grid.ColumnModel.defaultRenderer;
35717 return this.config[col].renderer;
35721 * Sets the rendering (formatting) function for a column.
35722 * @param {Number} col The column index
35723 * @param {Function} fn The function to use to process the cell's raw data
35724 * to return HTML markup for the grid view. The render function is called with
35725 * the following parameters:<ul>
35726 * <li>Data value.</li>
35727 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35728 * <li>css A CSS style string to apply to the table cell.</li>
35729 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35730 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35731 * <li>Row index</li>
35732 * <li>Column index</li>
35733 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35735 setRenderer : function(col, fn){
35736 this.config[col].renderer = fn;
35740 * Returns the width for the specified column.
35741 * @param {Number} col The column index
35744 getColumnWidth : function(col){
35745 return this.config[col].width * 1 || this.defaultWidth;
35749 * Sets the width for a column.
35750 * @param {Number} col The column index
35751 * @param {Number} width The new width
35753 setColumnWidth : function(col, width, suppressEvent){
35754 this.config[col].width = width;
35755 this.totalWidth = null;
35756 if(!suppressEvent){
35757 this.fireEvent("widthchange", this, col, width);
35762 * Returns the total width of all columns.
35763 * @param {Boolean} includeHidden True to include hidden column widths
35766 getTotalWidth : function(includeHidden){
35767 if(!this.totalWidth){
35768 this.totalWidth = 0;
35769 for(var i = 0, len = this.config.length; i < len; i++){
35770 if(includeHidden || !this.isHidden(i)){
35771 this.totalWidth += this.getColumnWidth(i);
35775 return this.totalWidth;
35779 * Returns the header for the specified column.
35780 * @param {Number} col The column index
35783 getColumnHeader : function(col){
35784 return this.config[col].header;
35788 * Sets the header for a column.
35789 * @param {Number} col The column index
35790 * @param {String} header The new header
35792 setColumnHeader : function(col, header){
35793 this.config[col].header = header;
35794 this.fireEvent("headerchange", this, col, header);
35798 * Returns the tooltip for the specified column.
35799 * @param {Number} col The column index
35802 getColumnTooltip : function(col){
35803 return this.config[col].tooltip;
35806 * Sets the tooltip for a column.
35807 * @param {Number} col The column index
35808 * @param {String} tooltip The new tooltip
35810 setColumnTooltip : function(col, tooltip){
35811 this.config[col].tooltip = tooltip;
35815 * Returns the dataIndex for the specified column.
35816 * @param {Number} col The column index
35819 getDataIndex : function(col){
35820 return this.config[col].dataIndex;
35824 * Sets the dataIndex for a column.
35825 * @param {Number} col The column index
35826 * @param {Number} dataIndex The new dataIndex
35828 setDataIndex : function(col, dataIndex){
35829 this.config[col].dataIndex = dataIndex;
35835 * Returns true if the cell is editable.
35836 * @param {Number} colIndex The column index
35837 * @param {Number} rowIndex The row index - this is nto actually used..?
35838 * @return {Boolean}
35840 isCellEditable : function(colIndex, rowIndex){
35841 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35845 * Returns the editor defined for the cell/column.
35846 * return false or null to disable editing.
35847 * @param {Number} colIndex The column index
35848 * @param {Number} rowIndex The row index
35851 getCellEditor : function(colIndex, rowIndex){
35852 return this.config[colIndex].editor;
35856 * Sets if a column is editable.
35857 * @param {Number} col The column index
35858 * @param {Boolean} editable True if the column is editable
35860 setEditable : function(col, editable){
35861 this.config[col].editable = editable;
35866 * Returns true if the column is hidden.
35867 * @param {Number} colIndex The column index
35868 * @return {Boolean}
35870 isHidden : function(colIndex){
35871 return this.config[colIndex].hidden;
35876 * Returns true if the column width cannot be changed
35878 isFixed : function(colIndex){
35879 return this.config[colIndex].fixed;
35883 * Returns true if the column can be resized
35884 * @return {Boolean}
35886 isResizable : function(colIndex){
35887 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35890 * Sets if a column is hidden.
35891 * @param {Number} colIndex The column index
35892 * @param {Boolean} hidden True if the column is hidden
35894 setHidden : function(colIndex, hidden){
35895 this.config[colIndex].hidden = hidden;
35896 this.totalWidth = null;
35897 this.fireEvent("hiddenchange", this, colIndex, hidden);
35901 * Sets the editor for a column.
35902 * @param {Number} col The column index
35903 * @param {Object} editor The editor object
35905 setEditor : function(col, editor){
35906 this.config[col].editor = editor;
35910 Roo.grid.ColumnModel.defaultRenderer = function(value)
35912 if(typeof value == "object") {
35915 if(typeof value == "string" && value.length < 1){
35919 return String.format("{0}", value);
35922 // Alias for backwards compatibility
35923 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35926 * Ext JS Library 1.1.1
35927 * Copyright(c) 2006-2007, Ext JS, LLC.
35929 * Originally Released Under LGPL - original licence link has changed is not relivant.
35932 * <script type="text/javascript">
35936 * @class Roo.grid.AbstractSelectionModel
35937 * @extends Roo.util.Observable
35938 * Abstract base class for grid SelectionModels. It provides the interface that should be
35939 * implemented by descendant classes. This class should not be directly instantiated.
35942 Roo.grid.AbstractSelectionModel = function(){
35943 this.locked = false;
35944 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35947 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
35948 /** @ignore Called by the grid automatically. Do not call directly. */
35949 init : function(grid){
35955 * Locks the selections.
35958 this.locked = true;
35962 * Unlocks the selections.
35964 unlock : function(){
35965 this.locked = false;
35969 * Returns true if the selections are locked.
35970 * @return {Boolean}
35972 isLocked : function(){
35973 return this.locked;
35977 * Ext JS Library 1.1.1
35978 * Copyright(c) 2006-2007, Ext JS, LLC.
35980 * Originally Released Under LGPL - original licence link has changed is not relivant.
35983 * <script type="text/javascript">
35986 * @extends Roo.grid.AbstractSelectionModel
35987 * @class Roo.grid.RowSelectionModel
35988 * The default SelectionModel used by {@link Roo.grid.Grid}.
35989 * It supports multiple selections and keyboard selection/navigation.
35991 * @param {Object} config
35993 Roo.grid.RowSelectionModel = function(config){
35994 Roo.apply(this, config);
35995 this.selections = new Roo.util.MixedCollection(false, function(o){
36000 this.lastActive = false;
36004 * @event selectionchange
36005 * Fires when the selection changes
36006 * @param {SelectionModel} this
36008 "selectionchange" : true,
36010 * @event afterselectionchange
36011 * Fires after the selection changes (eg. by key press or clicking)
36012 * @param {SelectionModel} this
36014 "afterselectionchange" : true,
36016 * @event beforerowselect
36017 * Fires when a row is selected being selected, return false to cancel.
36018 * @param {SelectionModel} this
36019 * @param {Number} rowIndex The selected index
36020 * @param {Boolean} keepExisting False if other selections will be cleared
36022 "beforerowselect" : true,
36025 * Fires when a row is selected.
36026 * @param {SelectionModel} this
36027 * @param {Number} rowIndex The selected index
36028 * @param {Roo.data.Record} r The record
36030 "rowselect" : true,
36032 * @event rowdeselect
36033 * Fires when a row is deselected.
36034 * @param {SelectionModel} this
36035 * @param {Number} rowIndex The selected index
36037 "rowdeselect" : true
36039 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36040 this.locked = false;
36043 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
36045 * @cfg {Boolean} singleSelect
36046 * True to allow selection of only one row at a time (defaults to false)
36048 singleSelect : false,
36051 initEvents : function(){
36053 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36054 this.grid.on("mousedown", this.handleMouseDown, this);
36055 }else{ // allow click to work like normal
36056 this.grid.on("rowclick", this.handleDragableRowClick, this);
36059 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36060 "up" : function(e){
36062 this.selectPrevious(e.shiftKey);
36063 }else if(this.last !== false && this.lastActive !== false){
36064 var last = this.last;
36065 this.selectRange(this.last, this.lastActive-1);
36066 this.grid.getView().focusRow(this.lastActive);
36067 if(last !== false){
36071 this.selectFirstRow();
36073 this.fireEvent("afterselectionchange", this);
36075 "down" : function(e){
36077 this.selectNext(e.shiftKey);
36078 }else if(this.last !== false && this.lastActive !== false){
36079 var last = this.last;
36080 this.selectRange(this.last, this.lastActive+1);
36081 this.grid.getView().focusRow(this.lastActive);
36082 if(last !== false){
36086 this.selectFirstRow();
36088 this.fireEvent("afterselectionchange", this);
36093 var view = this.grid.view;
36094 view.on("refresh", this.onRefresh, this);
36095 view.on("rowupdated", this.onRowUpdated, this);
36096 view.on("rowremoved", this.onRemove, this);
36100 onRefresh : function(){
36101 var ds = this.grid.dataSource, i, v = this.grid.view;
36102 var s = this.selections;
36103 s.each(function(r){
36104 if((i = ds.indexOfId(r.id)) != -1){
36106 s.add(ds.getAt(i)); // updating the selection relate data
36114 onRemove : function(v, index, r){
36115 this.selections.remove(r);
36119 onRowUpdated : function(v, index, r){
36120 if(this.isSelected(r)){
36121 v.onRowSelect(index);
36127 * @param {Array} records The records to select
36128 * @param {Boolean} keepExisting (optional) True to keep existing selections
36130 selectRecords : function(records, keepExisting){
36132 this.clearSelections();
36134 var ds = this.grid.dataSource;
36135 for(var i = 0, len = records.length; i < len; i++){
36136 this.selectRow(ds.indexOf(records[i]), true);
36141 * Gets the number of selected rows.
36144 getCount : function(){
36145 return this.selections.length;
36149 * Selects the first row in the grid.
36151 selectFirstRow : function(){
36156 * Select the last row.
36157 * @param {Boolean} keepExisting (optional) True to keep existing selections
36159 selectLastRow : function(keepExisting){
36160 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
36164 * Selects the row immediately following the last selected row.
36165 * @param {Boolean} keepExisting (optional) True to keep existing selections
36167 selectNext : function(keepExisting){
36168 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
36169 this.selectRow(this.last+1, keepExisting);
36170 this.grid.getView().focusRow(this.last);
36175 * Selects the row that precedes the last selected row.
36176 * @param {Boolean} keepExisting (optional) True to keep existing selections
36178 selectPrevious : function(keepExisting){
36180 this.selectRow(this.last-1, keepExisting);
36181 this.grid.getView().focusRow(this.last);
36186 * Returns the selected records
36187 * @return {Array} Array of selected records
36189 getSelections : function(){
36190 return [].concat(this.selections.items);
36194 * Returns the first selected record.
36197 getSelected : function(){
36198 return this.selections.itemAt(0);
36203 * Clears all selections.
36205 clearSelections : function(fast){
36210 var ds = this.grid.dataSource;
36211 var s = this.selections;
36212 s.each(function(r){
36213 this.deselectRow(ds.indexOfId(r.id));
36217 this.selections.clear();
36224 * Selects all rows.
36226 selectAll : function(){
36230 this.selections.clear();
36231 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
36232 this.selectRow(i, true);
36237 * Returns True if there is a selection.
36238 * @return {Boolean}
36240 hasSelection : function(){
36241 return this.selections.length > 0;
36245 * Returns True if the specified row is selected.
36246 * @param {Number/Record} record The record or index of the record to check
36247 * @return {Boolean}
36249 isSelected : function(index){
36250 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
36251 return (r && this.selections.key(r.id) ? true : false);
36255 * Returns True if the specified record id is selected.
36256 * @param {String} id The id of record to check
36257 * @return {Boolean}
36259 isIdSelected : function(id){
36260 return (this.selections.key(id) ? true : false);
36264 handleMouseDown : function(e, t){
36265 var view = this.grid.getView(), rowIndex;
36266 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36269 if(e.shiftKey && this.last !== false){
36270 var last = this.last;
36271 this.selectRange(last, rowIndex, e.ctrlKey);
36272 this.last = last; // reset the last
36273 view.focusRow(rowIndex);
36275 var isSelected = this.isSelected(rowIndex);
36276 if(e.button !== 0 && isSelected){
36277 view.focusRow(rowIndex);
36278 }else if(e.ctrlKey && isSelected){
36279 this.deselectRow(rowIndex);
36280 }else if(!isSelected){
36281 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36282 view.focusRow(rowIndex);
36285 this.fireEvent("afterselectionchange", this);
36288 handleDragableRowClick : function(grid, rowIndex, e)
36290 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36291 this.selectRow(rowIndex, false);
36292 grid.view.focusRow(rowIndex);
36293 this.fireEvent("afterselectionchange", this);
36298 * Selects multiple rows.
36299 * @param {Array} rows Array of the indexes of the row to select
36300 * @param {Boolean} keepExisting (optional) True to keep existing selections
36302 selectRows : function(rows, keepExisting){
36304 this.clearSelections();
36306 for(var i = 0, len = rows.length; i < len; i++){
36307 this.selectRow(rows[i], true);
36312 * Selects a range of rows. All rows in between startRow and endRow are also selected.
36313 * @param {Number} startRow The index of the first row in the range
36314 * @param {Number} endRow The index of the last row in the range
36315 * @param {Boolean} keepExisting (optional) True to retain existing selections
36317 selectRange : function(startRow, endRow, keepExisting){
36322 this.clearSelections();
36324 if(startRow <= endRow){
36325 for(var i = startRow; i <= endRow; i++){
36326 this.selectRow(i, true);
36329 for(var i = startRow; i >= endRow; i--){
36330 this.selectRow(i, true);
36336 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36337 * @param {Number} startRow The index of the first row in the range
36338 * @param {Number} endRow The index of the last row in the range
36340 deselectRange : function(startRow, endRow, preventViewNotify){
36344 for(var i = startRow; i <= endRow; i++){
36345 this.deselectRow(i, preventViewNotify);
36351 * @param {Number} row The index of the row to select
36352 * @param {Boolean} keepExisting (optional) True to keep existing selections
36354 selectRow : function(index, keepExisting, preventViewNotify){
36355 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
36358 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36359 if(!keepExisting || this.singleSelect){
36360 this.clearSelections();
36362 var r = this.grid.dataSource.getAt(index);
36363 this.selections.add(r);
36364 this.last = this.lastActive = index;
36365 if(!preventViewNotify){
36366 this.grid.getView().onRowSelect(index);
36368 this.fireEvent("rowselect", this, index, r);
36369 this.fireEvent("selectionchange", this);
36375 * @param {Number} row The index of the row to deselect
36377 deselectRow : function(index, preventViewNotify){
36381 if(this.last == index){
36384 if(this.lastActive == index){
36385 this.lastActive = false;
36387 var r = this.grid.dataSource.getAt(index);
36388 this.selections.remove(r);
36389 if(!preventViewNotify){
36390 this.grid.getView().onRowDeselect(index);
36392 this.fireEvent("rowdeselect", this, index);
36393 this.fireEvent("selectionchange", this);
36397 restoreLast : function(){
36399 this.last = this._last;
36404 acceptsNav : function(row, col, cm){
36405 return !cm.isHidden(col) && cm.isCellEditable(col, row);
36409 onEditorKey : function(field, e){
36410 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36415 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36417 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36419 }else if(k == e.ENTER && !e.ctrlKey){
36423 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36425 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36427 }else if(k == e.ESC){
36431 g.startEditing(newCell[0], newCell[1]);
36436 * Ext JS Library 1.1.1
36437 * Copyright(c) 2006-2007, Ext JS, LLC.
36439 * Originally Released Under LGPL - original licence link has changed is not relivant.
36442 * <script type="text/javascript">
36445 * @class Roo.grid.CellSelectionModel
36446 * @extends Roo.grid.AbstractSelectionModel
36447 * This class provides the basic implementation for cell selection in a grid.
36449 * @param {Object} config The object containing the configuration of this model.
36450 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36452 Roo.grid.CellSelectionModel = function(config){
36453 Roo.apply(this, config);
36455 this.selection = null;
36459 * @event beforerowselect
36460 * Fires before a cell is selected.
36461 * @param {SelectionModel} this
36462 * @param {Number} rowIndex The selected row index
36463 * @param {Number} colIndex The selected cell index
36465 "beforecellselect" : true,
36467 * @event cellselect
36468 * Fires when a cell is selected.
36469 * @param {SelectionModel} this
36470 * @param {Number} rowIndex The selected row index
36471 * @param {Number} colIndex The selected cell index
36473 "cellselect" : true,
36475 * @event selectionchange
36476 * Fires when the active selection changes.
36477 * @param {SelectionModel} this
36478 * @param {Object} selection null for no selection or an object (o) with two properties
36480 <li>o.record: the record object for the row the selection is in</li>
36481 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36484 "selectionchange" : true,
36487 * Fires when the tab (or enter) was pressed on the last editable cell
36488 * You can use this to trigger add new row.
36489 * @param {SelectionModel} this
36493 * @event beforeeditnext
36494 * Fires before the next editable sell is made active
36495 * You can use this to skip to another cell or fire the tabend
36496 * if you set cell to false
36497 * @param {Object} eventdata object : { cell : [ row, col ] }
36499 "beforeeditnext" : true
36501 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36504 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
36506 enter_is_tab: false,
36509 initEvents : function(){
36510 this.grid.on("mousedown", this.handleMouseDown, this);
36511 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36512 var view = this.grid.view;
36513 view.on("refresh", this.onViewChange, this);
36514 view.on("rowupdated", this.onRowUpdated, this);
36515 view.on("beforerowremoved", this.clearSelections, this);
36516 view.on("beforerowsinserted", this.clearSelections, this);
36517 if(this.grid.isEditor){
36518 this.grid.on("beforeedit", this.beforeEdit, this);
36523 beforeEdit : function(e){
36524 this.select(e.row, e.column, false, true, e.record);
36528 onRowUpdated : function(v, index, r){
36529 if(this.selection && this.selection.record == r){
36530 v.onCellSelect(index, this.selection.cell[1]);
36535 onViewChange : function(){
36536 this.clearSelections(true);
36540 * Returns the currently selected cell,.
36541 * @return {Array} The selected cell (row, column) or null if none selected.
36543 getSelectedCell : function(){
36544 return this.selection ? this.selection.cell : null;
36548 * Clears all selections.
36549 * @param {Boolean} true to prevent the gridview from being notified about the change.
36551 clearSelections : function(preventNotify){
36552 var s = this.selection;
36554 if(preventNotify !== true){
36555 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36557 this.selection = null;
36558 this.fireEvent("selectionchange", this, null);
36563 * Returns true if there is a selection.
36564 * @return {Boolean}
36566 hasSelection : function(){
36567 return this.selection ? true : false;
36571 handleMouseDown : function(e, t){
36572 var v = this.grid.getView();
36573 if(this.isLocked()){
36576 var row = v.findRowIndex(t);
36577 var cell = v.findCellIndex(t);
36578 if(row !== false && cell !== false){
36579 this.select(row, cell);
36585 * @param {Number} rowIndex
36586 * @param {Number} collIndex
36588 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36589 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36590 this.clearSelections();
36591 r = r || this.grid.dataSource.getAt(rowIndex);
36594 cell : [rowIndex, colIndex]
36596 if(!preventViewNotify){
36597 var v = this.grid.getView();
36598 v.onCellSelect(rowIndex, colIndex);
36599 if(preventFocus !== true){
36600 v.focusCell(rowIndex, colIndex);
36603 this.fireEvent("cellselect", this, rowIndex, colIndex);
36604 this.fireEvent("selectionchange", this, this.selection);
36609 isSelectable : function(rowIndex, colIndex, cm){
36610 return !cm.isHidden(colIndex);
36614 handleKeyDown : function(e){
36615 //Roo.log('Cell Sel Model handleKeyDown');
36616 if(!e.isNavKeyPress()){
36619 var g = this.grid, s = this.selection;
36622 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
36624 this.select(cell[0], cell[1]);
36629 var walk = function(row, col, step){
36630 return g.walkCells(row, col, step, sm.isSelectable, sm);
36632 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36639 // handled by onEditorKey
36640 if (g.isEditor && g.editing) {
36644 newCell = walk(r, c-1, -1);
36646 newCell = walk(r, c+1, 1);
36651 newCell = walk(r+1, c, 1);
36655 newCell = walk(r-1, c, -1);
36659 newCell = walk(r, c+1, 1);
36663 newCell = walk(r, c-1, -1);
36668 if(g.isEditor && !g.editing){
36669 g.startEditing(r, c);
36678 this.select(newCell[0], newCell[1]);
36684 acceptsNav : function(row, col, cm){
36685 return !cm.isHidden(col) && cm.isCellEditable(col, row);
36689 * @param {Number} field (not used) - as it's normally used as a listener
36690 * @param {Number} e - event - fake it by using
36692 * var e = Roo.EventObjectImpl.prototype;
36693 * e.keyCode = e.TAB
36697 onEditorKey : function(field, e){
36699 var k = e.getKey(),
36702 ed = g.activeEditor,
36704 ///Roo.log('onEditorKey' + k);
36707 if (this.enter_is_tab && k == e.ENTER) {
36713 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36715 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36721 } else if(k == e.ENTER && !e.ctrlKey){
36724 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36726 } else if(k == e.ESC){
36731 var ecall = { cell : newCell, forward : forward };
36732 this.fireEvent('beforeeditnext', ecall );
36733 newCell = ecall.cell;
36734 forward = ecall.forward;
36738 //Roo.log('next cell after edit');
36739 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36740 } else if (forward) {
36741 // tabbed past last
36742 this.fireEvent.defer(100, this, ['tabend',this]);
36747 * Ext JS Library 1.1.1
36748 * Copyright(c) 2006-2007, Ext JS, LLC.
36750 * Originally Released Under LGPL - original licence link has changed is not relivant.
36753 * <script type="text/javascript">
36757 * @class Roo.grid.EditorGrid
36758 * @extends Roo.grid.Grid
36759 * Class for creating and editable grid.
36760 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36761 * The container MUST have some type of size defined for the grid to fill. The container will be
36762 * automatically set to position relative if it isn't already.
36763 * @param {Object} dataSource The data model to bind to
36764 * @param {Object} colModel The column model with info about this grid's columns
36766 Roo.grid.EditorGrid = function(container, config){
36767 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36768 this.getGridEl().addClass("xedit-grid");
36770 if(!this.selModel){
36771 this.selModel = new Roo.grid.CellSelectionModel();
36774 this.activeEditor = null;
36778 * @event beforeedit
36779 * Fires before cell editing is triggered. The edit event object has the following properties <br />
36780 * <ul style="padding:5px;padding-left:16px;">
36781 * <li>grid - This grid</li>
36782 * <li>record - The record being edited</li>
36783 * <li>field - The field name being edited</li>
36784 * <li>value - The value for the field being edited.</li>
36785 * <li>row - The grid row index</li>
36786 * <li>column - The grid column index</li>
36787 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36789 * @param {Object} e An edit event (see above for description)
36791 "beforeedit" : true,
36794 * Fires after a cell is edited. <br />
36795 * <ul style="padding:5px;padding-left:16px;">
36796 * <li>grid - This grid</li>
36797 * <li>record - The record being edited</li>
36798 * <li>field - The field name being edited</li>
36799 * <li>value - The value being set</li>
36800 * <li>originalValue - The original value for the field, before the edit.</li>
36801 * <li>row - The grid row index</li>
36802 * <li>column - The grid column index</li>
36804 * @param {Object} e An edit event (see above for description)
36806 "afteredit" : true,
36808 * @event validateedit
36809 * Fires after a cell is edited, but before the value is set in the record.
36810 * You can use this to modify the value being set in the field, Return false
36811 * to cancel the change. The edit event object has the following properties <br />
36812 * <ul style="padding:5px;padding-left:16px;">
36813 * <li>editor - This editor</li>
36814 * <li>grid - This grid</li>
36815 * <li>record - The record being edited</li>
36816 * <li>field - The field name being edited</li>
36817 * <li>value - The value being set</li>
36818 * <li>originalValue - The original value for the field, before the edit.</li>
36819 * <li>row - The grid row index</li>
36820 * <li>column - The grid column index</li>
36821 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36823 * @param {Object} e An edit event (see above for description)
36825 "validateedit" : true
36827 this.on("bodyscroll", this.stopEditing, this);
36828 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
36831 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36833 * @cfg {Number} clicksToEdit
36834 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36841 trackMouseOver: false, // causes very odd FF errors
36843 onCellDblClick : function(g, row, col){
36844 this.startEditing(row, col);
36847 onEditComplete : function(ed, value, startValue){
36848 this.editing = false;
36849 this.activeEditor = null;
36850 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36852 var field = this.colModel.getDataIndex(ed.col);
36857 originalValue: startValue,
36864 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36867 if(String(value) !== String(startValue)){
36869 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36870 r.set(field, e.value);
36871 // if we are dealing with a combo box..
36872 // then we also set the 'name' colum to be the displayField
36873 if (ed.field.displayField && ed.field.name) {
36874 r.set(ed.field.name, ed.field.el.dom.value);
36877 delete e.cancel; //?? why!!!
36878 this.fireEvent("afteredit", e);
36881 this.fireEvent("afteredit", e); // always fire it!
36883 this.view.focusCell(ed.row, ed.col);
36887 * Starts editing the specified for the specified row/column
36888 * @param {Number} rowIndex
36889 * @param {Number} colIndex
36891 startEditing : function(row, col){
36892 this.stopEditing();
36893 if(this.colModel.isCellEditable(col, row)){
36894 this.view.ensureVisible(row, col, true);
36896 var r = this.dataSource.getAt(row);
36897 var field = this.colModel.getDataIndex(col);
36898 var cell = Roo.get(this.view.getCell(row,col));
36903 value: r.data[field],
36908 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36909 this.editing = true;
36910 var ed = this.colModel.getCellEditor(col, row);
36916 ed.render(ed.parentEl || document.body);
36922 (function(){ // complex but required for focus issues in safari, ie and opera
36926 ed.on("complete", this.onEditComplete, this, {single: true});
36927 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
36928 this.activeEditor = ed;
36929 var v = r.data[field];
36930 ed.startEdit(this.view.getCell(row, col), v);
36931 // combo's with 'displayField and name set
36932 if (ed.field.displayField && ed.field.name) {
36933 ed.field.el.dom.value = r.data[ed.field.name];
36937 }).defer(50, this);
36943 * Stops any active editing
36945 stopEditing : function(){
36946 if(this.activeEditor){
36947 this.activeEditor.completeEdit();
36949 this.activeEditor = null;
36953 * Called to get grid's drag proxy text, by default returns this.ddText.
36956 getDragDropText : function(){
36957 var count = this.selModel.getSelectedCell() ? 1 : 0;
36958 return String.format(this.ddText, count, count == 1 ? '' : 's');
36963 * Ext JS Library 1.1.1
36964 * Copyright(c) 2006-2007, Ext JS, LLC.
36966 * Originally Released Under LGPL - original licence link has changed is not relivant.
36969 * <script type="text/javascript">
36972 // private - not really -- you end up using it !
36973 // This is a support class used internally by the Grid components
36976 * @class Roo.grid.GridEditor
36977 * @extends Roo.Editor
36978 * Class for creating and editable grid elements.
36979 * @param {Object} config any settings (must include field)
36981 Roo.grid.GridEditor = function(field, config){
36982 if (!config && field.field) {
36984 field = Roo.factory(config.field, Roo.form);
36986 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36987 field.monitorTab = false;
36990 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36993 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36996 alignment: "tl-tl",
36999 cls: "x-small-editor x-grid-editor",
37004 * Ext JS Library 1.1.1
37005 * Copyright(c) 2006-2007, Ext JS, LLC.
37007 * Originally Released Under LGPL - original licence link has changed is not relivant.
37010 * <script type="text/javascript">
37015 Roo.grid.PropertyRecord = Roo.data.Record.create([
37016 {name:'name',type:'string'}, 'value'
37020 Roo.grid.PropertyStore = function(grid, source){
37022 this.store = new Roo.data.Store({
37023 recordType : Roo.grid.PropertyRecord
37025 this.store.on('update', this.onUpdate, this);
37027 this.setSource(source);
37029 Roo.grid.PropertyStore.superclass.constructor.call(this);
37034 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37035 setSource : function(o){
37037 this.store.removeAll();
37040 if(this.isEditableValue(o[k])){
37041 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37044 this.store.loadRecords({records: data}, {}, true);
37047 onUpdate : function(ds, record, type){
37048 if(type == Roo.data.Record.EDIT){
37049 var v = record.data['value'];
37050 var oldValue = record.modified['value'];
37051 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37052 this.source[record.id] = v;
37054 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37061 getProperty : function(row){
37062 return this.store.getAt(row);
37065 isEditableValue: function(val){
37066 if(val && val instanceof Date){
37068 }else if(typeof val == 'object' || typeof val == 'function'){
37074 setValue : function(prop, value){
37075 this.source[prop] = value;
37076 this.store.getById(prop).set('value', value);
37079 getSource : function(){
37080 return this.source;
37084 Roo.grid.PropertyColumnModel = function(grid, store){
37087 g.PropertyColumnModel.superclass.constructor.call(this, [
37088 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37089 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37091 this.store = store;
37092 this.bselect = Roo.DomHelper.append(document.body, {
37093 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37094 {tag: 'option', value: 'true', html: 'true'},
37095 {tag: 'option', value: 'false', html: 'false'}
37098 Roo.id(this.bselect);
37101 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37102 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37103 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37104 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37105 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37107 this.renderCellDelegate = this.renderCell.createDelegate(this);
37108 this.renderPropDelegate = this.renderProp.createDelegate(this);
37111 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37115 valueText : 'Value',
37117 dateFormat : 'm/j/Y',
37120 renderDate : function(dateVal){
37121 return dateVal.dateFormat(this.dateFormat);
37124 renderBool : function(bVal){
37125 return bVal ? 'true' : 'false';
37128 isCellEditable : function(colIndex, rowIndex){
37129 return colIndex == 1;
37132 getRenderer : function(col){
37134 this.renderCellDelegate : this.renderPropDelegate;
37137 renderProp : function(v){
37138 return this.getPropertyName(v);
37141 renderCell : function(val){
37143 if(val instanceof Date){
37144 rv = this.renderDate(val);
37145 }else if(typeof val == 'boolean'){
37146 rv = this.renderBool(val);
37148 return Roo.util.Format.htmlEncode(rv);
37151 getPropertyName : function(name){
37152 var pn = this.grid.propertyNames;
37153 return pn && pn[name] ? pn[name] : name;
37156 getCellEditor : function(colIndex, rowIndex){
37157 var p = this.store.getProperty(rowIndex);
37158 var n = p.data['name'], val = p.data['value'];
37160 if(typeof(this.grid.customEditors[n]) == 'string'){
37161 return this.editors[this.grid.customEditors[n]];
37163 if(typeof(this.grid.customEditors[n]) != 'undefined'){
37164 return this.grid.customEditors[n];
37166 if(val instanceof Date){
37167 return this.editors['date'];
37168 }else if(typeof val == 'number'){
37169 return this.editors['number'];
37170 }else if(typeof val == 'boolean'){
37171 return this.editors['boolean'];
37173 return this.editors['string'];
37179 * @class Roo.grid.PropertyGrid
37180 * @extends Roo.grid.EditorGrid
37181 * This class represents the interface of a component based property grid control.
37182 * <br><br>Usage:<pre><code>
37183 var grid = new Roo.grid.PropertyGrid("my-container-id", {
37191 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37192 * The container MUST have some type of size defined for the grid to fill. The container will be
37193 * automatically set to position relative if it isn't already.
37194 * @param {Object} config A config object that sets properties on this grid.
37196 Roo.grid.PropertyGrid = function(container, config){
37197 config = config || {};
37198 var store = new Roo.grid.PropertyStore(this);
37199 this.store = store;
37200 var cm = new Roo.grid.PropertyColumnModel(this, store);
37201 store.store.sort('name', 'ASC');
37202 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37205 enableColLock:false,
37206 enableColumnMove:false,
37208 trackMouseOver: false,
37211 this.getGridEl().addClass('x-props-grid');
37212 this.lastEditRow = null;
37213 this.on('columnresize', this.onColumnResize, this);
37216 * @event beforepropertychange
37217 * Fires before a property changes (return false to stop?)
37218 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37219 * @param {String} id Record Id
37220 * @param {String} newval New Value
37221 * @param {String} oldval Old Value
37223 "beforepropertychange": true,
37225 * @event propertychange
37226 * Fires after a property changes
37227 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37228 * @param {String} id Record Id
37229 * @param {String} newval New Value
37230 * @param {String} oldval Old Value
37232 "propertychange": true
37234 this.customEditors = this.customEditors || {};
37236 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37239 * @cfg {Object} customEditors map of colnames=> custom editors.
37240 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37241 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37242 * false disables editing of the field.
37246 * @cfg {Object} propertyNames map of property Names to their displayed value
37249 render : function(){
37250 Roo.grid.PropertyGrid.superclass.render.call(this);
37251 this.autoSize.defer(100, this);
37254 autoSize : function(){
37255 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37257 this.view.fitColumns();
37261 onColumnResize : function(){
37262 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37266 * Sets the data for the Grid
37267 * accepts a Key => Value object of all the elements avaiable.
37268 * @param {Object} data to appear in grid.
37270 setSource : function(source){
37271 this.store.setSource(source);
37275 * Gets all the data from the grid.
37276 * @return {Object} data data stored in grid
37278 getSource : function(){
37279 return this.store.getSource();
37288 * @class Roo.grid.Calendar
37289 * @extends Roo.util.Grid
37290 * This class extends the Grid to provide a calendar widget
37291 * <br><br>Usage:<pre><code>
37292 var grid = new Roo.grid.Calendar("my-container-id", {
37295 selModel: mySelectionModel,
37296 autoSizeColumns: true,
37297 monitorWindowResize: false,
37298 trackMouseOver: true
37299 eventstore : real data store..
37305 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37306 * The container MUST have some type of size defined for the grid to fill. The container will be
37307 * automatically set to position relative if it isn't already.
37308 * @param {Object} config A config object that sets properties on this grid.
37310 Roo.grid.Calendar = function(container, config){
37311 // initialize the container
37312 this.container = Roo.get(container);
37313 this.container.update("");
37314 this.container.setStyle("overflow", "hidden");
37315 this.container.addClass('x-grid-container');
37317 this.id = this.container.id;
37319 Roo.apply(this, config);
37320 // check and correct shorthanded configs
37324 for (var r = 0;r < 6;r++) {
37327 for (var c =0;c < 7;c++) {
37331 if (this.eventStore) {
37332 this.eventStore= Roo.factory(this.eventStore, Roo.data);
37333 this.eventStore.on('load',this.onLoad, this);
37334 this.eventStore.on('beforeload',this.clearEvents, this);
37338 this.dataSource = new Roo.data.Store({
37339 proxy: new Roo.data.MemoryProxy(rows),
37340 reader: new Roo.data.ArrayReader({}, [
37341 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37344 this.dataSource.load();
37345 this.ds = this.dataSource;
37346 this.ds.xmodule = this.xmodule || false;
37349 var cellRender = function(v,x,r)
37351 return String.format(
37352 '<div class="fc-day fc-widget-content"><div>' +
37353 '<div class="fc-event-container"></div>' +
37354 '<div class="fc-day-number">{0}</div>'+
37356 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37357 '</div></div>', v);
37362 this.colModel = new Roo.grid.ColumnModel( [
37364 xtype: 'ColumnModel',
37366 dataIndex : 'weekday0',
37368 renderer : cellRender
37371 xtype: 'ColumnModel',
37373 dataIndex : 'weekday1',
37375 renderer : cellRender
37378 xtype: 'ColumnModel',
37380 dataIndex : 'weekday2',
37381 header : 'Tuesday',
37382 renderer : cellRender
37385 xtype: 'ColumnModel',
37387 dataIndex : 'weekday3',
37388 header : 'Wednesday',
37389 renderer : cellRender
37392 xtype: 'ColumnModel',
37394 dataIndex : 'weekday4',
37395 header : 'Thursday',
37396 renderer : cellRender
37399 xtype: 'ColumnModel',
37401 dataIndex : 'weekday5',
37403 renderer : cellRender
37406 xtype: 'ColumnModel',
37408 dataIndex : 'weekday6',
37409 header : 'Saturday',
37410 renderer : cellRender
37413 this.cm = this.colModel;
37414 this.cm.xmodule = this.xmodule || false;
37418 //this.selModel = new Roo.grid.CellSelectionModel();
37419 //this.sm = this.selModel;
37420 //this.selModel.init(this);
37424 this.container.setWidth(this.width);
37428 this.container.setHeight(this.height);
37435 * The raw click event for the entire grid.
37436 * @param {Roo.EventObject} e
37441 * The raw dblclick event for the entire grid.
37442 * @param {Roo.EventObject} e
37446 * @event contextmenu
37447 * The raw contextmenu event for the entire grid.
37448 * @param {Roo.EventObject} e
37450 "contextmenu" : true,
37453 * The raw mousedown event for the entire grid.
37454 * @param {Roo.EventObject} e
37456 "mousedown" : true,
37459 * The raw mouseup event for the entire grid.
37460 * @param {Roo.EventObject} e
37465 * The raw mouseover event for the entire grid.
37466 * @param {Roo.EventObject} e
37468 "mouseover" : true,
37471 * The raw mouseout event for the entire grid.
37472 * @param {Roo.EventObject} e
37477 * The raw keypress event for the entire grid.
37478 * @param {Roo.EventObject} e
37483 * The raw keydown event for the entire grid.
37484 * @param {Roo.EventObject} e
37492 * Fires when a cell is clicked
37493 * @param {Grid} this
37494 * @param {Number} rowIndex
37495 * @param {Number} columnIndex
37496 * @param {Roo.EventObject} e
37498 "cellclick" : true,
37500 * @event celldblclick
37501 * Fires when a cell is double clicked
37502 * @param {Grid} this
37503 * @param {Number} rowIndex
37504 * @param {Number} columnIndex
37505 * @param {Roo.EventObject} e
37507 "celldblclick" : true,
37510 * Fires when a row is clicked
37511 * @param {Grid} this
37512 * @param {Number} rowIndex
37513 * @param {Roo.EventObject} e
37517 * @event rowdblclick
37518 * Fires when a row is double clicked
37519 * @param {Grid} this
37520 * @param {Number} rowIndex
37521 * @param {Roo.EventObject} e
37523 "rowdblclick" : true,
37525 * @event headerclick
37526 * Fires when a header is clicked
37527 * @param {Grid} this
37528 * @param {Number} columnIndex
37529 * @param {Roo.EventObject} e
37531 "headerclick" : true,
37533 * @event headerdblclick
37534 * Fires when a header cell is double clicked
37535 * @param {Grid} this
37536 * @param {Number} columnIndex
37537 * @param {Roo.EventObject} e
37539 "headerdblclick" : true,
37541 * @event rowcontextmenu
37542 * Fires when a row is right clicked
37543 * @param {Grid} this
37544 * @param {Number} rowIndex
37545 * @param {Roo.EventObject} e
37547 "rowcontextmenu" : true,
37549 * @event cellcontextmenu
37550 * Fires when a cell is right clicked
37551 * @param {Grid} this
37552 * @param {Number} rowIndex
37553 * @param {Number} cellIndex
37554 * @param {Roo.EventObject} e
37556 "cellcontextmenu" : true,
37558 * @event headercontextmenu
37559 * Fires when a header is right clicked
37560 * @param {Grid} this
37561 * @param {Number} columnIndex
37562 * @param {Roo.EventObject} e
37564 "headercontextmenu" : true,
37566 * @event bodyscroll
37567 * Fires when the body element is scrolled
37568 * @param {Number} scrollLeft
37569 * @param {Number} scrollTop
37571 "bodyscroll" : true,
37573 * @event columnresize
37574 * Fires when the user resizes a column
37575 * @param {Number} columnIndex
37576 * @param {Number} newSize
37578 "columnresize" : true,
37580 * @event columnmove
37581 * Fires when the user moves a column
37582 * @param {Number} oldIndex
37583 * @param {Number} newIndex
37585 "columnmove" : true,
37588 * Fires when row(s) start being dragged
37589 * @param {Grid} this
37590 * @param {Roo.GridDD} dd The drag drop object
37591 * @param {event} e The raw browser event
37593 "startdrag" : true,
37596 * Fires when a drag operation is complete
37597 * @param {Grid} this
37598 * @param {Roo.GridDD} dd The drag drop object
37599 * @param {event} e The raw browser event
37604 * Fires when dragged row(s) are dropped on a valid DD target
37605 * @param {Grid} this
37606 * @param {Roo.GridDD} dd The drag drop object
37607 * @param {String} targetId The target drag drop object
37608 * @param {event} e The raw browser event
37613 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37614 * @param {Grid} this
37615 * @param {Roo.GridDD} dd The drag drop object
37616 * @param {String} targetId The target drag drop object
37617 * @param {event} e The raw browser event
37622 * Fires when the dragged row(s) first cross another DD target while being dragged
37623 * @param {Grid} this
37624 * @param {Roo.GridDD} dd The drag drop object
37625 * @param {String} targetId The target drag drop object
37626 * @param {event} e The raw browser event
37628 "dragenter" : true,
37631 * Fires when the dragged row(s) leave another DD target while being dragged
37632 * @param {Grid} this
37633 * @param {Roo.GridDD} dd The drag drop object
37634 * @param {String} targetId The target drag drop object
37635 * @param {event} e The raw browser event
37640 * Fires when a row is rendered, so you can change add a style to it.
37641 * @param {GridView} gridview The grid view
37642 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
37648 * Fires when the grid is rendered
37649 * @param {Grid} grid
37654 * Fires when a date is selected
37655 * @param {DatePicker} this
37656 * @param {Date} date The selected date
37660 * @event monthchange
37661 * Fires when the displayed month changes
37662 * @param {DatePicker} this
37663 * @param {Date} date The selected month
37665 'monthchange': true,
37667 * @event evententer
37668 * Fires when mouse over an event
37669 * @param {Calendar} this
37670 * @param {event} Event
37672 'evententer': true,
37674 * @event eventleave
37675 * Fires when the mouse leaves an
37676 * @param {Calendar} this
37679 'eventleave': true,
37681 * @event eventclick
37682 * Fires when the mouse click an
37683 * @param {Calendar} this
37686 'eventclick': true,
37688 * @event eventrender
37689 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37690 * @param {Calendar} this
37691 * @param {data} data to be modified
37693 'eventrender': true
37697 Roo.grid.Grid.superclass.constructor.call(this);
37698 this.on('render', function() {
37699 this.view.el.addClass('x-grid-cal');
37701 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37705 if (!Roo.grid.Calendar.style) {
37706 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37709 '.x-grid-cal .x-grid-col' : {
37710 height: 'auto !important',
37711 'vertical-align': 'top'
37713 '.x-grid-cal .fc-event-hori' : {
37724 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37726 * @cfg {Store} eventStore The store that loads events.
37731 activeDate : false,
37734 monitorWindowResize : false,
37737 resizeColumns : function() {
37738 var col = (this.view.el.getWidth() / 7) - 3;
37739 // loop through cols, and setWidth
37740 for(var i =0 ; i < 7 ; i++){
37741 this.cm.setColumnWidth(i, col);
37744 setDate :function(date) {
37746 Roo.log('setDate?');
37748 this.resizeColumns();
37749 var vd = this.activeDate;
37750 this.activeDate = date;
37751 // if(vd && this.el){
37752 // var t = date.getTime();
37753 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37754 // Roo.log('using add remove');
37756 // this.fireEvent('monthchange', this, date);
37758 // this.cells.removeClass("fc-state-highlight");
37759 // this.cells.each(function(c){
37760 // if(c.dateValue == t){
37761 // c.addClass("fc-state-highlight");
37762 // setTimeout(function(){
37763 // try{c.dom.firstChild.focus();}catch(e){}
37773 var days = date.getDaysInMonth();
37775 var firstOfMonth = date.getFirstDateOfMonth();
37776 var startingPos = firstOfMonth.getDay()-this.startDay;
37778 if(startingPos < this.startDay){
37782 var pm = date.add(Date.MONTH, -1);
37783 var prevStart = pm.getDaysInMonth()-startingPos;
37787 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37789 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37790 //this.cells.addClassOnOver('fc-state-hover');
37792 var cells = this.cells.elements;
37793 var textEls = this.textNodes;
37795 //Roo.each(cells, function(cell){
37796 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37799 days += startingPos;
37801 // convert everything to numbers so it's fast
37802 var day = 86400000;
37803 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37806 //Roo.log(prevStart);
37808 var today = new Date().clearTime().getTime();
37809 var sel = date.clearTime().getTime();
37810 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37811 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37812 var ddMatch = this.disabledDatesRE;
37813 var ddText = this.disabledDatesText;
37814 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37815 var ddaysText = this.disabledDaysText;
37816 var format = this.format;
37818 var setCellClass = function(cal, cell){
37820 //Roo.log('set Cell Class');
37822 var t = d.getTime();
37827 cell.dateValue = t;
37829 cell.className += " fc-today";
37830 cell.className += " fc-state-highlight";
37831 cell.title = cal.todayText;
37834 // disable highlight in other month..
37835 cell.className += " fc-state-highlight";
37840 //cell.className = " fc-state-disabled";
37841 cell.title = cal.minText;
37845 //cell.className = " fc-state-disabled";
37846 cell.title = cal.maxText;
37850 if(ddays.indexOf(d.getDay()) != -1){
37851 // cell.title = ddaysText;
37852 // cell.className = " fc-state-disabled";
37855 if(ddMatch && format){
37856 var fvalue = d.dateFormat(format);
37857 if(ddMatch.test(fvalue)){
37858 cell.title = ddText.replace("%0", fvalue);
37859 cell.className = " fc-state-disabled";
37863 if (!cell.initialClassName) {
37864 cell.initialClassName = cell.dom.className;
37867 cell.dom.className = cell.initialClassName + ' ' + cell.className;
37872 for(; i < startingPos; i++) {
37873 cells[i].dayName = (++prevStart);
37874 Roo.log(textEls[i]);
37875 d.setDate(d.getDate()+1);
37877 //cells[i].className = "fc-past fc-other-month";
37878 setCellClass(this, cells[i]);
37883 for(; i < days; i++){
37884 intDay = i - startingPos + 1;
37885 cells[i].dayName = (intDay);
37886 d.setDate(d.getDate()+1);
37888 cells[i].className = ''; // "x-date-active";
37889 setCellClass(this, cells[i]);
37893 for(; i < 42; i++) {
37894 //textEls[i].innerHTML = (++extraDays);
37896 d.setDate(d.getDate()+1);
37897 cells[i].dayName = (++extraDays);
37898 cells[i].className = "fc-future fc-other-month";
37899 setCellClass(this, cells[i]);
37902 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37904 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37906 // this will cause all the cells to mis
37909 for (var r = 0;r < 6;r++) {
37910 for (var c =0;c < 7;c++) {
37911 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37915 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37916 for(i=0;i<cells.length;i++) {
37918 this.cells.elements[i].dayName = cells[i].dayName ;
37919 this.cells.elements[i].className = cells[i].className;
37920 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37921 this.cells.elements[i].title = cells[i].title ;
37922 this.cells.elements[i].dateValue = cells[i].dateValue ;
37928 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37929 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37931 ////if(totalRows != 6){
37932 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37933 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37936 this.fireEvent('monthchange', this, date);
37941 * Returns the grid's SelectionModel.
37942 * @return {SelectionModel}
37944 getSelectionModel : function(){
37945 if(!this.selModel){
37946 this.selModel = new Roo.grid.CellSelectionModel();
37948 return this.selModel;
37952 this.eventStore.load()
37958 findCell : function(dt) {
37959 dt = dt.clearTime().getTime();
37961 this.cells.each(function(c){
37962 //Roo.log("check " +c.dateValue + '?=' + dt);
37963 if(c.dateValue == dt){
37973 findCells : function(rec) {
37974 var s = rec.data.start_dt.clone().clearTime().getTime();
37976 var e= rec.data.end_dt.clone().clearTime().getTime();
37979 this.cells.each(function(c){
37980 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37982 if(c.dateValue > e){
37985 if(c.dateValue < s){
37994 findBestRow: function(cells)
37998 for (var i =0 ; i < cells.length;i++) {
37999 ret = Math.max(cells[i].rows || 0,ret);
38006 addItem : function(rec)
38008 // look for vertical location slot in
38009 var cells = this.findCells(rec);
38011 rec.row = this.findBestRow(cells);
38013 // work out the location.
38017 for(var i =0; i < cells.length; i++) {
38025 if (crow.start.getY() == cells[i].getY()) {
38027 crow.end = cells[i];
38043 for (var i = 0; i < cells.length;i++) {
38044 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
38051 clearEvents: function() {
38053 if (!this.eventStore.getCount()) {
38056 // reset number of rows in cells.
38057 Roo.each(this.cells.elements, function(c){
38061 this.eventStore.each(function(e) {
38062 this.clearEvent(e);
38067 clearEvent : function(ev)
38070 Roo.each(ev.els, function(el) {
38071 el.un('mouseenter' ,this.onEventEnter, this);
38072 el.un('mouseleave' ,this.onEventLeave, this);
38080 renderEvent : function(ev,ctr) {
38082 ctr = this.view.el.select('.fc-event-container',true).first();
38086 this.clearEvent(ev);
38092 var cells = ev.cells;
38093 var rows = ev.rows;
38094 this.fireEvent('eventrender', this, ev);
38096 for(var i =0; i < rows.length; i++) {
38100 cls += ' fc-event-start';
38102 if ((i+1) == rows.length) {
38103 cls += ' fc-event-end';
38106 //Roo.log(ev.data);
38107 // how many rows should it span..
38108 var cg = this.eventTmpl.append(ctr,Roo.apply({
38111 }, ev.data) , true);
38114 cg.on('mouseenter' ,this.onEventEnter, this, ev);
38115 cg.on('mouseleave' ,this.onEventLeave, this, ev);
38116 cg.on('click', this.onEventClick, this, ev);
38120 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38121 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38124 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
38125 cg.setWidth(ebox.right - sbox.x -2);
38129 renderEvents: function()
38131 // first make sure there is enough space..
38133 if (!this.eventTmpl) {
38134 this.eventTmpl = new Roo.Template(
38135 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
38136 '<div class="fc-event-inner">' +
38137 '<span class="fc-event-time">{time}</span>' +
38138 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38140 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
38148 this.cells.each(function(c) {
38149 //Roo.log(c.select('.fc-day-content div',true).first());
38150 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38153 var ctr = this.view.el.select('.fc-event-container',true).first();
38156 this.eventStore.each(function(ev){
38158 this.renderEvent(ev);
38162 this.view.layout();
38166 onEventEnter: function (e, el,event,d) {
38167 this.fireEvent('evententer', this, el, event);
38170 onEventLeave: function (e, el,event,d) {
38171 this.fireEvent('eventleave', this, el, event);
38174 onEventClick: function (e, el,event,d) {
38175 this.fireEvent('eventclick', this, el, event);
38178 onMonthChange: function () {
38182 onLoad: function () {
38184 //Roo.log('calendar onload');
38186 if(this.eventStore.getCount() > 0){
38190 this.eventStore.each(function(d){
38195 if (typeof(add.end_dt) == 'undefined') {
38196 Roo.log("Missing End time in calendar data: ");
38200 if (typeof(add.start_dt) == 'undefined') {
38201 Roo.log("Missing Start time in calendar data: ");
38205 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38206 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38207 add.id = add.id || d.id;
38208 add.title = add.title || '??';
38216 this.renderEvents();
38226 render : function ()
38230 if (!this.view.el.hasClass('course-timesheet')) {
38231 this.view.el.addClass('course-timesheet');
38233 if (this.tsStyle) {
38238 Roo.log(_this.grid.view.el.getWidth());
38241 this.tsStyle = Roo.util.CSS.createStyleSheet({
38242 '.course-timesheet .x-grid-row' : {
38245 '.x-grid-row td' : {
38246 'vertical-align' : 0
38248 '.course-edit-link' : {
38250 'text-overflow' : 'ellipsis',
38251 'overflow' : 'hidden',
38252 'white-space' : 'nowrap',
38253 'cursor' : 'pointer'
38258 '.de-act-sup-link' : {
38259 'color' : 'purple',
38260 'text-decoration' : 'line-through'
38264 'text-decoration' : 'line-through'
38266 '.course-timesheet .course-highlight' : {
38267 'border-top-style': 'dashed !important',
38268 'border-bottom-bottom': 'dashed !important'
38270 '.course-timesheet .course-item' : {
38271 'font-family' : 'tahoma, arial, helvetica',
38272 'font-size' : '11px',
38273 'overflow' : 'hidden',
38274 'padding-left' : '10px',
38275 'padding-right' : '10px',
38276 'padding-top' : '10px'
38284 monitorWindowResize : false,
38285 cellrenderer : function(v,x,r)
38290 xtype: 'CellSelectionModel',
38297 beforeload : function (_self, options)
38299 options.params = options.params || {};
38300 options.params._month = _this.monthField.getValue();
38301 options.params.limit = 9999;
38302 options.params['sort'] = 'when_dt';
38303 options.params['dir'] = 'ASC';
38304 this.proxy.loadResponse = this.loadResponse;
38306 //this.addColumns();
38308 load : function (_self, records, options)
38310 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38311 // if you click on the translation.. you can edit it...
38312 var el = Roo.get(this);
38313 var id = el.dom.getAttribute('data-id');
38314 var d = el.dom.getAttribute('data-date');
38315 var t = el.dom.getAttribute('data-time');
38316 //var id = this.child('span').dom.textContent;
38319 Pman.Dialog.CourseCalendar.show({
38323 productitem_active : id ? 1 : 0
38325 _this.grid.ds.load({});
38330 _this.panel.fireEvent('resize', [ '', '' ]);
38333 loadResponse : function(o, success, response){
38334 // this is overridden on before load..
38336 Roo.log("our code?");
38337 //Roo.log(success);
38338 //Roo.log(response)
38339 delete this.activeRequest;
38341 this.fireEvent("loadexception", this, o, response);
38342 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38347 result = o.reader.read(response);
38349 Roo.log("load exception?");
38350 this.fireEvent("loadexception", this, o, response, e);
38351 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38354 Roo.log("ready...");
38355 // loop through result.records;
38356 // and set this.tdate[date] = [] << array of records..
38358 Roo.each(result.records, function(r){
38360 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38361 _this.tdata[r.data.when_dt.format('j')] = [];
38363 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38366 //Roo.log(_this.tdata);
38368 result.records = [];
38369 result.totalRecords = 6;
38371 // let's generate some duumy records for the rows.
38372 //var st = _this.dateField.getValue();
38374 // work out monday..
38375 //st = st.add(Date.DAY, -1 * st.format('w'));
38377 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38379 var firstOfMonth = date.getFirstDayOfMonth();
38380 var days = date.getDaysInMonth();
38382 var firstAdded = false;
38383 for (var i = 0; i < result.totalRecords ; i++) {
38384 //var d= st.add(Date.DAY, i);
38387 for(var w = 0 ; w < 7 ; w++){
38388 if(!firstAdded && firstOfMonth != w){
38395 var dd = (d > 0 && d < 10) ? "0"+d : d;
38396 row['weekday'+w] = String.format(
38397 '<span style="font-size: 16px;"><b>{0}</b></span>'+
38398 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38400 date.format('Y-m-')+dd
38403 if(typeof(_this.tdata[d]) != 'undefined'){
38404 Roo.each(_this.tdata[d], function(r){
38408 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38409 if(r.parent_id*1>0){
38410 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38413 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38414 deactive = 'de-act-link';
38417 row['weekday'+w] += String.format(
38418 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38420 r.product_id_name, //1
38421 r.when_dt.format('h:ia'), //2
38431 // only do this if something added..
38433 result.records.push(_this.grid.dataSource.reader.newRow(row));
38437 // push it twice. (second one with an hour..
38441 this.fireEvent("load", this, o, o.request.arg);
38442 o.request.callback.call(o.request.scope, result, o.request.arg, true);
38444 sortInfo : {field: 'when_dt', direction : 'ASC' },
38446 xtype: 'HttpProxy',
38449 url : baseURL + '/Roo/Shop_course.php'
38452 xtype: 'JsonReader',
38469 'name': 'parent_id',
38473 'name': 'product_id',
38477 'name': 'productitem_id',
38495 click : function (_self, e)
38497 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38498 sd.setMonth(sd.getMonth()-1);
38499 _this.monthField.setValue(sd.format('Y-m-d'));
38500 _this.grid.ds.load({});
38506 xtype: 'Separator',
38510 xtype: 'MonthField',
38513 render : function (_self)
38515 _this.monthField = _self;
38516 // _this.monthField.set today
38518 select : function (combo, date)
38520 _this.grid.ds.load({});
38523 value : (function() { return new Date(); })()
38526 xtype: 'Separator',
38532 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38542 click : function (_self, e)
38544 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38545 sd.setMonth(sd.getMonth()+1);
38546 _this.monthField.setValue(sd.format('Y-m-d'));
38547 _this.grid.ds.load({});
38560 * Ext JS Library 1.1.1
38561 * Copyright(c) 2006-2007, Ext JS, LLC.
38563 * Originally Released Under LGPL - original licence link has changed is not relivant.
38566 * <script type="text/javascript">
38570 * @class Roo.LoadMask
38571 * A simple utility class for generically masking elements while loading data. If the element being masked has
38572 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38573 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
38574 * element's UpdateManager load indicator and will be destroyed after the initial load.
38576 * Create a new LoadMask
38577 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38578 * @param {Object} config The config object
38580 Roo.LoadMask = function(el, config){
38581 this.el = Roo.get(el);
38582 Roo.apply(this, config);
38584 this.store.on('beforeload', this.onBeforeLoad, this);
38585 this.store.on('load', this.onLoad, this);
38586 this.store.on('loadexception', this.onLoadException, this);
38587 this.removeMask = false;
38589 var um = this.el.getUpdateManager();
38590 um.showLoadIndicator = false; // disable the default indicator
38591 um.on('beforeupdate', this.onBeforeLoad, this);
38592 um.on('update', this.onLoad, this);
38593 um.on('failure', this.onLoad, this);
38594 this.removeMask = true;
38598 Roo.LoadMask.prototype = {
38600 * @cfg {Boolean} removeMask
38601 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38602 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
38605 * @cfg {String} msg
38606 * The text to display in a centered loading message box (defaults to 'Loading...')
38608 msg : 'Loading...',
38610 * @cfg {String} msgCls
38611 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38613 msgCls : 'x-mask-loading',
38616 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38622 * Disables the mask to prevent it from being displayed
38624 disable : function(){
38625 this.disabled = true;
38629 * Enables the mask so that it can be displayed
38631 enable : function(){
38632 this.disabled = false;
38635 onLoadException : function()
38637 Roo.log(arguments);
38639 if (typeof(arguments[3]) != 'undefined') {
38640 Roo.MessageBox.alert("Error loading",arguments[3]);
38644 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38645 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38652 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38655 onLoad : function()
38657 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38661 onBeforeLoad : function(){
38662 if(!this.disabled){
38663 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38668 destroy : function(){
38670 this.store.un('beforeload', this.onBeforeLoad, this);
38671 this.store.un('load', this.onLoad, this);
38672 this.store.un('loadexception', this.onLoadException, this);
38674 var um = this.el.getUpdateManager();
38675 um.un('beforeupdate', this.onBeforeLoad, this);
38676 um.un('update', this.onLoad, this);
38677 um.un('failure', this.onLoad, this);
38682 * Ext JS Library 1.1.1
38683 * Copyright(c) 2006-2007, Ext JS, LLC.
38685 * Originally Released Under LGPL - original licence link has changed is not relivant.
38688 * <script type="text/javascript">
38693 * @class Roo.XTemplate
38694 * @extends Roo.Template
38695 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38697 var t = new Roo.XTemplate(
38698 '<select name="{name}">',
38699 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
38703 // then append, applying the master template values
38706 * Supported features:
38711 {a_variable} - output encoded.
38712 {a_variable.format:("Y-m-d")} - call a method on the variable
38713 {a_variable:raw} - unencoded output
38714 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38715 {a_variable:this.method_on_template(...)} - call a method on the template object.
38720 <tpl for="a_variable or condition.."></tpl>
38721 <tpl if="a_variable or condition"></tpl>
38722 <tpl exec="some javascript"></tpl>
38723 <tpl name="named_template"></tpl> (experimental)
38725 <tpl for="."></tpl> - just iterate the property..
38726 <tpl for=".."></tpl> - iterates with the parent (probably the template)
38730 Roo.XTemplate = function()
38732 Roo.XTemplate.superclass.constructor.apply(this, arguments);
38739 Roo.extend(Roo.XTemplate, Roo.Template, {
38742 * The various sub templates
38747 * basic tag replacing syntax
38750 * // you can fake an object call by doing this
38754 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38757 * compile the template
38759 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38762 compile: function()
38766 s = ['<tpl>', s, '</tpl>'].join('');
38768 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38769 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38770 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
38771 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38772 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
38777 while(true == !!(m = s.match(re))){
38778 var forMatch = m[0].match(nameRe),
38779 ifMatch = m[0].match(ifRe),
38780 execMatch = m[0].match(execRe),
38781 namedMatch = m[0].match(namedRe),
38786 name = forMatch && forMatch[1] ? forMatch[1] : '';
38789 // if - puts fn into test..
38790 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38792 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38797 // exec - calls a function... returns empty if true is returned.
38798 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38800 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38808 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38809 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38810 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38813 var uid = namedMatch ? namedMatch[1] : id;
38817 id: namedMatch ? namedMatch[1] : id,
38824 s = s.replace(m[0], '');
38826 s = s.replace(m[0], '{xtpl'+ id + '}');
38831 for(var i = tpls.length-1; i >= 0; --i){
38832 this.compileTpl(tpls[i]);
38833 this.tpls[tpls[i].id] = tpls[i];
38835 this.master = tpls[tpls.length-1];
38839 * same as applyTemplate, except it's done to one of the subTemplates
38840 * when using named templates, you can do:
38842 * var str = pl.applySubTemplate('your-name', values);
38845 * @param {Number} id of the template
38846 * @param {Object} values to apply to template
38847 * @param {Object} parent (normaly the instance of this object)
38849 applySubTemplate : function(id, values, parent)
38853 var t = this.tpls[id];
38857 if(t.test && !t.test.call(this, values, parent)){
38861 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38862 Roo.log(e.toString());
38868 if(t.exec && t.exec.call(this, values, parent)){
38872 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38873 Roo.log(e.toString());
38878 var vs = t.target ? t.target.call(this, values, parent) : values;
38879 parent = t.target ? values : parent;
38880 if(t.target && vs instanceof Array){
38882 for(var i = 0, len = vs.length; i < len; i++){
38883 buf[buf.length] = t.compiled.call(this, vs[i], parent);
38885 return buf.join('');
38887 return t.compiled.call(this, vs, parent);
38889 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38890 Roo.log(e.toString());
38891 Roo.log(t.compiled);
38896 compileTpl : function(tpl)
38898 var fm = Roo.util.Format;
38899 var useF = this.disableFormats !== true;
38900 var sep = Roo.isGecko ? "+" : ",";
38901 var undef = function(str) {
38902 Roo.log("Property not found :" + str);
38906 var fn = function(m, name, format, args)
38908 //Roo.log(arguments);
38909 args = args ? args.replace(/\\'/g,"'") : args;
38910 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38911 if (typeof(format) == 'undefined') {
38912 format= 'htmlEncode';
38914 if (format == 'raw' ) {
38918 if(name.substr(0, 4) == 'xtpl'){
38919 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38922 // build an array of options to determine if value is undefined..
38924 // basically get 'xxxx.yyyy' then do
38925 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38926 // (function () { Roo.log("Property not found"); return ''; })() :
38931 Roo.each(name.split('.'), function(st) {
38932 lookfor += (lookfor.length ? '.': '') + st;
38933 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
38936 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38939 if(format && useF){
38941 args = args ? ',' + args : "";
38943 if(format.substr(0, 5) != "this."){
38944 format = "fm." + format + '(';
38946 format = 'this.call("'+ format.substr(5) + '", ';
38950 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
38954 // called with xxyx.yuu:(test,test)
38956 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
38958 // raw.. - :raw modifier..
38959 return "'"+ sep + udef_st + name + ")"+sep+"'";
38963 // branched to use + in gecko and [].join() in others
38965 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
38966 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38969 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
38970 body.push(tpl.body.replace(/(\r\n|\n)/g,
38971 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38972 body.push("'].join('');};};");
38973 body = body.join('');
38976 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38978 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
38984 applyTemplate : function(values){
38985 return this.master.compiled.call(this, values, {});
38986 //var s = this.subs;
38989 apply : function(){
38990 return this.applyTemplate.apply(this, arguments);
38995 Roo.XTemplate.from = function(el){
38996 el = Roo.getDom(el);
38997 return new Roo.XTemplate(el.value || el.innerHTML);