4 * Copyright(c) 2006-2007, Ext JS, LLC.
6 * Originally Released Under LGPL - original licence link has changed is not relivant.
9 * <script type="text/javascript">
14 * @class Roo.data.SortTypes
16 * Defines the default sorting (casting?) comparison functions used when sorting data.
18 Roo.data.SortTypes = {
20 * Default sort that does nothing
21 * @param {Mixed} s The value being converted
22 * @return {Mixed} The comparison value
29 * The regular expression used to strip tags
33 stripTagsRE : /<\/?[^>]+>/gi,
36 * Strips all HTML tags to sort on text only
37 * @param {Mixed} s The value being converted
38 * @return {String} The comparison value
41 return String(s).replace(this.stripTagsRE, "");
45 * Strips all HTML tags to sort on text only - Case insensitive
46 * @param {Mixed} s The value being converted
47 * @return {String} The comparison value
49 asUCText : function(s){
50 return String(s).toUpperCase().replace(this.stripTagsRE, "");
54 * Case insensitive string
55 * @param {Mixed} s The value being converted
56 * @return {String} The comparison value
58 asUCString : function(s) {
59 return String(s).toUpperCase();
64 * @param {Mixed} s The value being converted
65 * @return {Number} The comparison value
67 asDate : function(s) {
71 if(s instanceof Date){
74 return Date.parse(String(s));
79 * @param {Mixed} s The value being converted
80 * @return {Float} The comparison value
82 asFloat : function(s) {
83 var val = parseFloat(String(s).replace(/,/g, ""));
92 * @param {Mixed} s The value being converted
93 * @return {Number} The comparison value
96 var val = parseInt(String(s).replace(/,/g, ""));
104 * Ext JS Library 1.1.1
105 * Copyright(c) 2006-2007, Ext JS, LLC.
107 * Originally Released Under LGPL - original licence link has changed is not relivant.
110 * <script type="text/javascript">
114 * @class Roo.data.Record
115 * Instances of this class encapsulate both record <em>definition</em> information, and record
116 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117 * to access Records cached in an {@link Roo.data.Store} object.<br>
119 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
123 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
125 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126 * {@link #create}. The parameters are the same.
127 * @param {Array} data An associative Array of data values keyed by the field name.
128 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130 * not specified an integer id is generated.
132 Roo.data.Record = function(data, id){
133 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
138 * Generate a constructor for a specific record layout.
139 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141 * Each field definition object may contain the following properties: <ul>
142 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146 * is being used, then this is a string containing the javascript expression to reference the data relative to
147 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148 * to the data item relative to the record element. If the mapping expression is the same as the field name,
149 * this may be omitted.</p></li>
150 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151 * <ul><li>auto (Default, implies no conversion)</li>
156 * <li>date</li></ul></p></li>
157 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160 * by the Reader into an object that will be stored in the Record. It is passed the
161 * following parameters:<ul>
162 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
164 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
166 * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168 {name: 'title', mapping: 'topic_title'},
169 {name: 'author', mapping: 'username'},
170 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171 {name: 'lastPost', mapping: 'post_time', type: 'date'},
172 {name: 'lastPoster', mapping: 'user2'},
173 {name: 'excerpt', mapping: 'post_text'}
176 var myNewRecord = new TopicRecord({
177 title: 'Do my job please',
180 lastPost: new Date(),
181 lastPoster: 'Animal',
182 excerpt: 'No way dude!'
184 myStore.add(myNewRecord);
189 Roo.data.Record.create = function(o){
191 f.superclass.constructor.apply(this, arguments);
193 Roo.extend(f, Roo.data.Record);
195 p.fields = new Roo.util.MixedCollection(false, function(field){
198 for(var i = 0, len = o.length; i < len; i++){
199 p.fields.add(new Roo.data.Field(o[i]));
201 f.getField = function(name){
202 return p.fields.get(name);
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
212 Roo.data.Record.prototype = {
214 * Readonly flag - true if this record has been modified.
223 join : function(store){
228 * Set the named field to the specified value.
229 * @param {String} name The name of the field to set.
230 * @param {Object} value The value to set the field to.
232 set : function(name, value){
233 if(this.data[name] == value){
240 if(typeof this.modified[name] == 'undefined'){
241 this.modified[name] = this.data[name];
243 this.data[name] = value;
244 if(!this.editing && this.store){
245 this.store.afterEdit(this);
250 * Get the value of the named field.
251 * @param {String} name The name of the field to get the value of.
252 * @return {Object} The value of the field.
254 get : function(name){
255 return this.data[name];
259 beginEdit : function(){
265 cancelEdit : function(){
266 this.editing = false;
267 delete this.modified;
271 endEdit : function(){
272 this.editing = false;
273 if(this.dirty && this.store){
274 this.store.afterEdit(this);
279 * Usually called by the {@link Roo.data.Store} which owns the Record.
280 * Rejects all changes made to the Record since either creation, or the last commit operation.
281 * Modified fields are reverted to their original values.
283 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284 * of reject operations.
287 var m = this.modified;
289 if(typeof m[n] != "function"){
294 delete this.modified;
295 this.editing = false;
297 this.store.afterReject(this);
302 * Usually called by the {@link Roo.data.Store} which owns the Record.
303 * Commits all changes made to the Record since either creation, or the last commit operation.
305 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306 * of commit operations.
310 delete this.modified;
311 this.editing = false;
313 this.store.afterCommit(this);
318 hasError : function(){
319 return this.error != null;
323 clearError : function(){
328 * Creates a copy of this record.
329 * @param {String} id (optional) A new record id if you don't want to use this record's id
332 copy : function(newId) {
333 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
337 * Ext JS Library 1.1.1
338 * Copyright(c) 2006-2007, Ext JS, LLC.
340 * Originally Released Under LGPL - original licence link has changed is not relivant.
343 * <script type="text/javascript">
349 * @class Roo.data.Store
350 * @extends Roo.util.Observable
351 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
354 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355 * has no knowledge of the format of the data returned by the Proxy.<br>
357 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358 * instances from the data object. These records are cached and made available through accessor functions.
360 * Creates a new Store.
361 * @param {Object} config A config object containing the objects needed for the Store to access data,
362 * and read the data into Records.
364 Roo.data.Store = function(config){
365 this.data = new Roo.util.MixedCollection(false);
366 this.data.getKey = function(o){
369 this.baseParams = {};
376 "multisort" : "_multisort"
379 if(config && config.data){
380 this.inlineData = config.data;
384 Roo.apply(this, config);
386 if(this.reader){ // reader passed
387 this.reader = Roo.factory(this.reader, Roo.data);
388 this.reader.xmodule = this.xmodule || false;
389 if(!this.recordType){
390 this.recordType = this.reader.recordType;
392 if(this.reader.onMetaChange){
393 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
398 this.fields = this.recordType.prototype.fields;
405 * Fires when the data cache has changed, and a widget which is using this Store
406 * as a Record cache should refresh its view.
407 * @param {Store} this
412 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413 * @param {Store} this
414 * @param {Object} meta The JSON metadata
419 * Fires when Records have been added to the Store
420 * @param {Store} this
421 * @param {Roo.data.Record[]} records The array of Records added
422 * @param {Number} index The index at which the record(s) were added
427 * Fires when a Record has been removed from the Store
428 * @param {Store} this
429 * @param {Roo.data.Record} record The Record that was removed
430 * @param {Number} index The index at which the record was removed
435 * Fires when a Record has been updated
436 * @param {Store} this
437 * @param {Roo.data.Record} record The Record that was updated
438 * @param {String} operation The update operation being performed. Value may be one of:
441 Roo.data.Record.REJECT
442 Roo.data.Record.COMMIT
448 * Fires when the data cache has been cleared.
449 * @param {Store} this
454 * Fires before a request is made for a new data object. If the beforeload handler returns false
455 * the load action will be canceled.
456 * @param {Store} this
457 * @param {Object} options The loading options that were specified (see {@link #load} for details)
461 * @event beforeloadadd
462 * Fires after a new set of Records has been loaded.
463 * @param {Store} this
464 * @param {Roo.data.Record[]} records The Records that were loaded
465 * @param {Object} options The loading options that were specified (see {@link #load} for details)
467 beforeloadadd : true,
470 * Fires after a new set of Records has been loaded, before they are added to the store.
471 * @param {Store} this
472 * @param {Roo.data.Record[]} records The Records that were loaded
473 * @param {Object} options The loading options that were specified (see {@link #load} for details)
474 * @params {Object} return from reader
478 * @event loadexception
479 * Fires if an exception occurs in the Proxy during loading.
480 * Called with the signature of the Proxy's "loadexception" event.
481 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
484 * @param {Object} return from JsonData.reader() - success, totalRecords, records
485 * @param {Object} load options
486 * @param {Object} jsonData from your request (normally this contains the Exception)
492 this.proxy = Roo.factory(this.proxy, Roo.data);
493 this.proxy.xmodule = this.xmodule || false;
494 this.relayEvents(this.proxy, ["loadexception"]);
496 this.sortToggle = {};
497 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
499 Roo.data.Store.superclass.constructor.call(this);
502 this.loadData(this.inlineData);
503 delete this.inlineData;
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
509 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
510 * without a remote query - used by combo/forms at present.
514 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
517 * @cfg {Array} data Inline data to be loaded when the store is initialized.
520 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
521 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
524 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525 * on any HTTP request
528 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
531 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
535 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
541 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542 * loaded or when a record is removed. (defaults to false).
544 pruneModifiedRecords : false,
550 * Add Records to the Store and fires the add event.
551 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
553 add : function(records){
554 records = [].concat(records);
555 for(var i = 0, len = records.length; i < len; i++){
556 records[i].join(this);
558 var index = this.data.length;
559 this.data.addAll(records);
560 this.fireEvent("add", this, records, index);
564 * Remove a Record from the Store and fires the remove event.
565 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
567 remove : function(record){
568 var index = this.data.indexOf(record);
569 this.data.removeAt(index);
571 if(this.pruneModifiedRecords){
572 this.modified.remove(record);
574 this.fireEvent("remove", this, record, index);
578 * Remove all Records from the Store and fires the clear event.
580 removeAll : function(){
582 if(this.pruneModifiedRecords){
585 this.fireEvent("clear", this);
589 * Inserts Records to the Store at the given index and fires the add event.
590 * @param {Number} index The start index at which to insert the passed Records.
591 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
593 insert : function(index, records){
594 records = [].concat(records);
595 for(var i = 0, len = records.length; i < len; i++){
596 this.data.insert(index, records[i]);
597 records[i].join(this);
599 this.fireEvent("add", this, records, index);
603 * Get the index within the cache of the passed Record.
604 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605 * @return {Number} The index of the passed Record. Returns -1 if not found.
607 indexOf : function(record){
608 return this.data.indexOf(record);
612 * Get the index within the cache of the Record with the passed id.
613 * @param {String} id The id of the Record to find.
614 * @return {Number} The index of the Record. Returns -1 if not found.
616 indexOfId : function(id){
617 return this.data.indexOfKey(id);
621 * Get the Record with the specified id.
622 * @param {String} id The id of the Record to find.
623 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
625 getById : function(id){
626 return this.data.key(id);
630 * Get the Record at the specified index.
631 * @param {Number} index The index of the Record to find.
632 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
634 getAt : function(index){
635 return this.data.itemAt(index);
639 * Returns a range of Records between specified indices.
640 * @param {Number} startIndex (optional) The starting index (defaults to 0)
641 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642 * @return {Roo.data.Record[]} An array of Records
644 getRange : function(start, end){
645 return this.data.getRange(start, end);
649 storeOptions : function(o){
650 o = Roo.apply({}, o);
653 this.lastOptions = o;
657 * Loads the Record cache from the configured Proxy using the configured Reader.
659 * If using remote paging, then the first load call must specify the <em>start</em>
660 * and <em>limit</em> properties in the options.params property to establish the initial
661 * position within the dataset, and the number of Records to cache on each read from the Proxy.
663 * <strong>It is important to note that for remote data sources, loading is asynchronous,
664 * and this call will return before the new data has been loaded. Perform any post-processing
665 * in a callback function, or in a "load" event handler.</strong>
667 * @param {Object} options An object containing properties which control loading options:<ul>
668 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669 * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
672 data : data, // array of key=>value data like JsonReader
679 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
680 * passed the following arguments:<ul>
681 * <li>r : Roo.data.Record[]</li>
682 * <li>options: Options object from the load call</li>
683 * <li>success: Boolean success indicator</li></ul></li>
684 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
685 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
688 load : function(options){
689 options = options || {};
690 if(this.fireEvent("beforeload", this, options) !== false){
691 this.storeOptions(options);
692 var p = Roo.apply(options.params || {}, this.baseParams);
693 // if meta was not loaded from remote source.. try requesting it.
694 if (!this.reader.metaFromRemote) {
697 if(this.sortInfo && this.remoteSort){
698 var pn = this.paramNames;
699 p[pn["sort"]] = this.sortInfo.field;
700 p[pn["dir"]] = this.sortInfo.direction;
702 if (this.multiSort) {
703 var pn = this.paramNames;
704 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
707 this.proxy.load(p, this.reader, this.loadRecords, this, options);
712 * Reloads the Record cache from the configured Proxy using the configured Reader and
713 * the options from the last load operation performed.
714 * @param {Object} options (optional) An object containing properties which may override the options
715 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
716 * the most recently used options are reused).
718 reload : function(options){
719 this.load(Roo.applyIf(options||{}, this.lastOptions));
723 // Called as a callback by the Reader during a load operation.
724 loadRecords : function(o, options, success){
727 if(success !== false){
728 this.fireEvent("load", this, [], options, o);
730 if(options.callback){
731 options.callback.call(options.scope || this, [], options, false);
735 // if data returned failure - throw an exception.
736 if (o.success === false) {
737 // show a message if no listener is registered.
738 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
739 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
741 // loadmask wil be hooked into this..
742 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
745 var r = o.records, t = o.totalRecords || r.length;
747 this.fireEvent("beforeloadadd", this, r, options, o);
749 if(!options || options.add !== true){
750 if(this.pruneModifiedRecords){
753 for(var i = 0, len = r.length; i < len; i++){
757 this.data = this.snapshot;
758 delete this.snapshot;
762 this.totalLength = t;
764 this.fireEvent("datachanged", this);
766 this.totalLength = Math.max(t, this.data.length+r.length);
770 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
772 var e = new Roo.data.Record({});
774 e.set(this.parent.displayField, this.parent.emptyTitle);
775 e.set(this.parent.valueField, '');
780 this.fireEvent("load", this, r, options, o);
781 if(options.callback){
782 options.callback.call(options.scope || this, r, options, true);
788 * Loads data from a passed data block. A Reader which understands the format of the data
789 * must have been configured in the constructor.
790 * @param {Object} data The data block from which to read the Records. The format of the data expected
791 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
792 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
794 loadData : function(o, append){
795 var r = this.reader.readRecords(o);
796 this.loadRecords(r, {add: append}, true);
800 * using 'cn' the nested child reader read the child array into it's child stores.
801 * @param {Object} rec The record with a 'children array
803 loadDataFromChildren : function(rec)
805 this.loadData(this.reader.toLoadData(rec));
810 * Gets the number of cached records.
812 * <em>If using paging, this may not be the total size of the dataset. If the data object
813 * used by the Reader contains the dataset size, then the getTotalCount() function returns
814 * the data set size</em>
816 getCount : function(){
817 return this.data.length || 0;
821 * Gets the total number of records in the dataset as returned by the server.
823 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
824 * the dataset size</em>
826 getTotalCount : function(){
827 return this.totalLength || 0;
831 * Returns the sort state of the Store as an object with two properties:
833 field {String} The name of the field by which the Records are sorted
834 direction {String} The sort order, "ASC" or "DESC"
837 getSortState : function(){
838 return this.sortInfo;
842 applySort : function(){
843 if(this.sortInfo && !this.remoteSort){
844 var s = this.sortInfo, f = s.field;
845 var st = this.fields.get(f).sortType;
846 var fn = function(r1, r2){
847 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
848 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
850 this.data.sort(s.direction, fn);
851 if(this.snapshot && this.snapshot != this.data){
852 this.snapshot.sort(s.direction, fn);
858 * Sets the default sort column and order to be used by the next load operation.
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 setDefaultSort : function(field, dir){
863 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
868 * If remote sorting is used, the sort is performed on the server, and the cache is
869 * reloaded. If local sorting is used, the cache is sorted internally.
870 * @param {String} fieldName The name of the field to sort by.
871 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
873 sort : function(fieldName, dir){
874 var f = this.fields.get(fieldName);
876 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
878 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
879 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
884 this.sortToggle[f.name] = dir;
885 this.sortInfo = {field: f.name, direction: dir};
886 if(!this.remoteSort){
888 this.fireEvent("datachanged", this);
890 this.load(this.lastOptions);
895 * Calls the specified function for each of the Records in the cache.
896 * @param {Function} fn The function to call. The Record is passed as the first parameter.
897 * Returning <em>false</em> aborts and exits the iteration.
898 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
900 each : function(fn, scope){
901 this.data.each(fn, scope);
905 * Gets all records modified since the last commit. Modified records are persisted across load operations
906 * (e.g., during paging).
907 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
909 getModifiedRecords : function(){
910 return this.modified;
914 createFilterFn : function(property, value, anyMatch){
915 if(!value.exec){ // not a regex
916 value = String(value);
917 if(value.length == 0){
920 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
923 return value.test(r.data[property]);
928 * Sums the value of <i>property</i> for each record between start and end and returns the result.
929 * @param {String} property A field on your records
930 * @param {Number} start The record index to start at (defaults to 0)
931 * @param {Number} end The last record index to include (defaults to length - 1)
932 * @return {Number} The sum
934 sum : function(property, start, end){
935 var rs = this.data.items, v = 0;
937 end = (end || end === 0) ? end : rs.length-1;
939 for(var i = start; i <= end; i++){
940 v += (rs[i].data[property] || 0);
946 * Filter the records by a specified property.
947 * @param {String} field A field on your records
948 * @param {String/RegExp} value Either a string that the field
949 * should start with or a RegExp to test against the field
950 * @param {Boolean} anyMatch True to match any part not just the beginning
952 filter : function(property, value, anyMatch){
953 var fn = this.createFilterFn(property, value, anyMatch);
954 return fn ? this.filterBy(fn) : this.clearFilter();
958 * Filter by a function. The specified function will be called with each
959 * record in this data source. If the function returns true the record is included,
960 * otherwise it is filtered.
961 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
962 * @param {Object} scope (optional) The scope of the function (defaults to this)
964 filterBy : function(fn, scope){
965 this.snapshot = this.snapshot || this.data;
966 this.data = this.queryBy(fn, scope||this);
967 this.fireEvent("datachanged", this);
971 * Query the records by a specified property.
972 * @param {String} field A field on your records
973 * @param {String/RegExp} value Either a string that the field
974 * should start with or a RegExp to test against the field
975 * @param {Boolean} anyMatch True to match any part not just the beginning
976 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
978 query : function(property, value, anyMatch){
979 var fn = this.createFilterFn(property, value, anyMatch);
980 return fn ? this.queryBy(fn) : this.data.clone();
984 * Query by a function. The specified function will be called with each
985 * record in this data source. If the function returns true the record is included
987 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
988 * @param {Object} scope (optional) The scope of the function (defaults to this)
989 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
991 queryBy : function(fn, scope){
992 var data = this.snapshot || this.data;
993 return data.filterBy(fn, scope||this);
997 * Collects unique values for a particular dataIndex from this store.
998 * @param {String} dataIndex The property to collect
999 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
1000 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
1001 * @return {Array} An array of the unique values
1003 collect : function(dataIndex, allowNull, bypassFilter){
1004 var d = (bypassFilter === true && this.snapshot) ?
1005 this.snapshot.items : this.data.items;
1006 var v, sv, r = [], l = {};
1007 for(var i = 0, len = d.length; i < len; i++){
1008 v = d[i].data[dataIndex];
1010 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1019 * Revert to a view of the Record cache with no filtering applied.
1020 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1022 clearFilter : function(suppressEvent){
1023 if(this.snapshot && this.snapshot != this.data){
1024 this.data = this.snapshot;
1025 delete this.snapshot;
1026 if(suppressEvent !== true){
1027 this.fireEvent("datachanged", this);
1033 afterEdit : function(record){
1034 if(this.modified.indexOf(record) == -1){
1035 this.modified.push(record);
1037 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1041 afterReject : function(record){
1042 this.modified.remove(record);
1043 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1047 afterCommit : function(record){
1048 this.modified.remove(record);
1049 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1053 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1054 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1056 commitChanges : function(){
1057 var m = this.modified.slice(0);
1059 for(var i = 0, len = m.length; i < len; i++){
1065 * Cancel outstanding changes on all changed records.
1067 rejectChanges : function(){
1068 var m = this.modified.slice(0);
1070 for(var i = 0, len = m.length; i < len; i++){
1075 onMetaChange : function(meta, rtype, o){
1076 this.recordType = rtype;
1077 this.fields = rtype.prototype.fields;
1078 delete this.snapshot;
1079 this.sortInfo = meta.sortInfo || this.sortInfo;
1081 this.fireEvent('metachange', this, this.reader.meta);
1084 moveIndex : function(data, type)
1086 var index = this.indexOf(data);
1088 var newIndex = index + type;
1092 this.insert(newIndex, data);
1097 * Ext JS Library 1.1.1
1098 * Copyright(c) 2006-2007, Ext JS, LLC.
1100 * Originally Released Under LGPL - original licence link has changed is not relivant.
1103 * <script type="text/javascript">
1107 * @class Roo.data.SimpleStore
1108 * @extends Roo.data.Store
1109 * Small helper class to make creating Stores from Array data easier.
1110 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1111 * @cfg {Array} fields An array of field definition objects, or field name strings.
1112 * @cfg {Object} an existing reader (eg. copied from another store)
1113 * @cfg {Array} data The multi-dimensional array of data
1114 * @cfg {Roo.data.DataProxy} proxy [not-required]
1115 * @cfg {Roo.data.Reader} reader [not-required]
1117 * @param {Object} config
1119 Roo.data.SimpleStore = function(config)
1121 Roo.data.SimpleStore.superclass.constructor.call(this, {
1123 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1126 Roo.data.Record.create(config.fields)
1128 proxy : new Roo.data.MemoryProxy(config.data)
1132 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1134 * Ext JS Library 1.1.1
1135 * Copyright(c) 2006-2007, Ext JS, LLC.
1137 * Originally Released Under LGPL - original licence link has changed is not relivant.
1140 * <script type="text/javascript">
1145 * @extends Roo.data.Store
1146 * @class Roo.data.JsonStore
1147 * Small helper class to make creating Stores for JSON data easier. <br/>
1149 var store = new Roo.data.JsonStore({
1150 url: 'get-images.php',
1152 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1155 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1156 * JsonReader and HttpProxy (unless inline data is provided).</b>
1157 * @cfg {Array} fields An array of field definition objects, or field name strings.
1159 * @param {Object} config
1161 Roo.data.JsonStore = function(c){
1162 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1163 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1164 reader: new Roo.data.JsonReader(c, c.fields)
1167 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1169 * Ext JS Library 1.1.1
1170 * Copyright(c) 2006-2007, Ext JS, LLC.
1172 * Originally Released Under LGPL - original licence link has changed is not relivant.
1175 * <script type="text/javascript">
1179 Roo.data.Field = function(config){
1180 if(typeof config == "string"){
1181 config = {name: config};
1183 Roo.apply(this, config);
1189 var st = Roo.data.SortTypes;
1190 // named sortTypes are supported, here we look them up
1191 if(typeof this.sortType == "string"){
1192 this.sortType = st[this.sortType];
1195 // set default sortType for strings and dates
1199 this.sortType = st.asUCString;
1202 this.sortType = st.asDate;
1205 this.sortType = st.none;
1210 var stripRe = /[\$,%]/g;
1212 // prebuilt conversion function for this field, instead of
1213 // switching every time we're reading a value
1215 var cv, dateFormat = this.dateFormat;
1220 cv = function(v){ return v; };
1223 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1227 return v !== undefined && v !== null && v !== '' ?
1228 parseInt(String(v).replace(stripRe, ""), 10) : '';
1233 return v !== undefined && v !== null && v !== '' ?
1234 parseFloat(String(v).replace(stripRe, ""), 10) : '';
1239 cv = function(v){ return v === true || v === "true" || v == 1; };
1246 if(v instanceof Date){
1250 if(dateFormat == "timestamp"){
1251 return new Date(v*1000);
1253 return Date.parseDate(v, dateFormat);
1255 var parsed = Date.parse(v);
1256 return parsed ? new Date(parsed) : null;
1265 Roo.data.Field.prototype = {
1273 * Ext JS Library 1.1.1
1274 * Copyright(c) 2006-2007, Ext JS, LLC.
1276 * Originally Released Under LGPL - original licence link has changed is not relivant.
1279 * <script type="text/javascript">
1282 // Base class for reading structured data from a data source. This class is intended to be
1283 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1286 * @class Roo.data.DataReader
1288 * Base class for reading structured data from a data source. This class is intended to be
1289 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1292 Roo.data.DataReader = function(meta, recordType){
1296 this.recordType = recordType instanceof Array ?
1297 Roo.data.Record.create(recordType) : recordType;
1300 Roo.data.DataReader.prototype = {
1303 readerType : 'Data',
1305 * Create an empty record
1306 * @param {Object} data (optional) - overlay some values
1307 * @return {Roo.data.Record} record created.
1309 newRow : function(d) {
1311 this.recordType.prototype.fields.each(function(c) {
1313 case 'int' : da[c.name] = 0; break;
1314 case 'date' : da[c.name] = new Date(); break;
1315 case 'float' : da[c.name] = 0.0; break;
1316 case 'boolean' : da[c.name] = false; break;
1317 default : da[c.name] = ""; break;
1321 return new this.recordType(Roo.apply(da, d));
1327 * Ext JS Library 1.1.1
1328 * Copyright(c) 2006-2007, Ext JS, LLC.
1330 * Originally Released Under LGPL - original licence link has changed is not relivant.
1333 * <script type="text/javascript">
1337 * @class Roo.data.DataProxy
1338 * @extends Roo.util.Observable
1340 * This class is an abstract base class for implementations which provide retrieval of
1341 * unformatted data objects.<br>
1343 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1344 * (of the appropriate type which knows how to parse the data object) to provide a block of
1345 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1347 * Custom implementations must implement the load method as described in
1348 * {@link Roo.data.HttpProxy#load}.
1350 Roo.data.DataProxy = function(){
1354 * Fires before a network request is made to retrieve a data object.
1355 * @param {Object} This DataProxy object.
1356 * @param {Object} params The params parameter to the load function.
1361 * Fires before the load method's callback is called.
1362 * @param {Object} This DataProxy object.
1363 * @param {Object} o The data object.
1364 * @param {Object} arg The callback argument object passed to the load function.
1368 * @event loadexception
1369 * Fires if an Exception occurs during data retrieval.
1370 * @param {Object} This DataProxy object.
1371 * @param {Object} o The data object.
1372 * @param {Object} arg The callback argument object passed to the load function.
1373 * @param {Object} e The Exception.
1375 loadexception : true
1377 Roo.data.DataProxy.superclass.constructor.call(this);
1380 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1383 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1387 * Ext JS Library 1.1.1
1388 * Copyright(c) 2006-2007, Ext JS, LLC.
1390 * Originally Released Under LGPL - original licence link has changed is not relivant.
1393 * <script type="text/javascript">
1396 * @class Roo.data.MemoryProxy
1397 * @extends Roo.data.DataProxy
1398 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1399 * to the Reader when its load method is called.
1401 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1403 Roo.data.MemoryProxy = function(data){
1407 Roo.data.MemoryProxy.superclass.constructor.call(this);
1411 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1414 * Load data from the requested source (in this case an in-memory
1415 * data object passed to the constructor), read the data object into
1416 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1417 * process that block using the passed callback.
1418 * @param {Object} params This parameter is not used by the MemoryProxy class.
1419 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1420 * object into a block of Roo.data.Records.
1421 * @param {Function} callback The function into which to pass the block of Roo.data.records.
1422 * The function must be passed <ul>
1423 * <li>The Record block object</li>
1424 * <li>The "arg" argument from the load function</li>
1425 * <li>A boolean success indicator</li>
1427 * @param {Object} scope The scope in which to call the callback
1428 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1430 load : function(params, reader, callback, scope, arg){
1431 params = params || {};
1434 result = reader.readRecords(params.data ? params.data :this.data);
1436 this.fireEvent("loadexception", this, arg, null, e);
1437 callback.call(scope, null, arg, false);
1440 callback.call(scope, result, arg, true);
1444 update : function(params, records){
1449 * Ext JS Library 1.1.1
1450 * Copyright(c) 2006-2007, Ext JS, LLC.
1452 * Originally Released Under LGPL - original licence link has changed is not relivant.
1455 * <script type="text/javascript">
1458 * @class Roo.data.HttpProxy
1459 * @extends Roo.data.DataProxy
1460 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1461 * configured to reference a certain URL.<br><br>
1463 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1464 * from which the running page was served.<br><br>
1466 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1468 * Be aware that to enable the browser to parse an XML document, the server must set
1469 * the Content-Type header in the HTTP response to "text/xml".
1471 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1472 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
1473 * will be used to make the request.
1475 Roo.data.HttpProxy = function(conn){
1476 Roo.data.HttpProxy.superclass.constructor.call(this);
1477 // is conn a conn config or a real conn?
1479 this.useAjax = !conn || !conn.events;
1483 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1484 // thse are take from connection...
1487 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1490 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1491 * extra parameters to each request made by this object. (defaults to undefined)
1494 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1495 * to each request made by this object. (defaults to undefined)
1498 * @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)
1501 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1504 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1510 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1514 * Return the {@link Roo.data.Connection} object being used by this Proxy.
1515 * @return {Connection} The Connection object. This object may be used to subscribe to events on
1516 * a finer-grained basis than the DataProxy events.
1518 getConnection : function(){
1519 return this.useAjax ? Roo.Ajax : this.conn;
1523 * Load data from the configured {@link Roo.data.Connection}, read the data object into
1524 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1525 * process that block using the passed callback.
1526 * @param {Object} params An object containing properties which are to be used as HTTP parameters
1527 * for the request to the remote server.
1528 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1529 * object into a block of Roo.data.Records.
1530 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1531 * The function must be passed <ul>
1532 * <li>The Record block object</li>
1533 * <li>The "arg" argument from the load function</li>
1534 * <li>A boolean success indicator</li>
1536 * @param {Object} scope The scope in which to call the callback
1537 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1539 load : function(params, reader, callback, scope, arg){
1540 if(this.fireEvent("beforeload", this, params) !== false){
1542 params : params || {},
1544 callback : callback,
1549 callback : this.loadResponse,
1553 Roo.applyIf(o, this.conn);
1554 if(this.activeRequest){
1555 Roo.Ajax.abort(this.activeRequest);
1557 this.activeRequest = Roo.Ajax.request(o);
1559 this.conn.request(o);
1562 callback.call(scope||this, null, arg, false);
1567 loadResponse : function(o, success, response){
1568 delete this.activeRequest;
1570 this.fireEvent("loadexception", this, o, response);
1571 o.request.callback.call(o.request.scope, null, o.request.arg, false);
1576 result = o.reader.read(response);
1579 o.raw = { errorMsg : response.responseText };
1580 this.fireEvent("loadexception", this, o, response, e);
1581 o.request.callback.call(o.request.scope, o, o.request.arg, false);
1585 this.fireEvent("load", this, o, o.request.arg);
1586 o.request.callback.call(o.request.scope, result, o.request.arg, true);
1590 update : function(dataSet){
1595 updateResponse : function(dataSet){
1600 * Ext JS Library 1.1.1
1601 * Copyright(c) 2006-2007, Ext JS, LLC.
1603 * Originally Released Under LGPL - original licence link has changed is not relivant.
1606 * <script type="text/javascript">
1610 * @class Roo.data.ScriptTagProxy
1611 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1612 * other than the originating domain of the running page.<br><br>
1614 * <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
1615 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1617 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1618 * source code that is used as the source inside a <script> tag.<br><br>
1620 * In order for the browser to process the returned data, the server must wrap the data object
1621 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1622 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1623 * depending on whether the callback name was passed:
1626 boolean scriptTag = false;
1627 String cb = request.getParameter("callback");
1630 response.setContentType("text/javascript");
1632 response.setContentType("application/x-json");
1634 Writer out = response.getWriter();
1636 out.write(cb + "(");
1638 out.print(dataBlock.toJsonString());
1645 * @param {Object} config A configuration object.
1647 Roo.data.ScriptTagProxy = function(config){
1648 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1649 Roo.apply(this, config);
1650 this.head = document.getElementsByTagName("head")[0];
1653 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1655 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1657 * @cfg {String} url The URL from which to request the data object.
1660 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1664 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1665 * the server the name of the callback function set up by the load call to process the returned data object.
1666 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1667 * javascript output which calls this named function passing the data object as its only parameter.
1669 callbackParam : "callback",
1671 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1672 * name to the request.
1677 * Load data from the configured URL, read the data object into
1678 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1679 * process that block using the passed callback.
1680 * @param {Object} params An object containing properties which are to be used as HTTP parameters
1681 * for the request to the remote server.
1682 * @param {Roo.data.DataReader} reader The Reader object which converts the data
1683 * object into a block of Roo.data.Records.
1684 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1685 * The function must be passed <ul>
1686 * <li>The Record block object</li>
1687 * <li>The "arg" argument from the load function</li>
1688 * <li>A boolean success indicator</li>
1690 * @param {Object} scope The scope in which to call the callback
1691 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1693 load : function(params, reader, callback, scope, arg){
1694 if(this.fireEvent("beforeload", this, params) !== false){
1696 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1699 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1701 url += "&_dc=" + (new Date().getTime());
1703 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1706 cb : "stcCallback"+transId,
1707 scriptId : "stcScript"+transId,
1711 callback : callback,
1717 window[trans.cb] = function(o){
1718 conn.handleResponse(o, trans);
1721 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1723 if(this.autoAbort !== false){
1727 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1729 var script = document.createElement("script");
1730 script.setAttribute("src", url);
1731 script.setAttribute("type", "text/javascript");
1732 script.setAttribute("id", trans.scriptId);
1733 this.head.appendChild(script);
1737 callback.call(scope||this, null, arg, false);
1742 isLoading : function(){
1743 return this.trans ? true : false;
1747 * Abort the current server request.
1750 if(this.isLoading()){
1751 this.destroyTrans(this.trans);
1756 destroyTrans : function(trans, isLoaded){
1757 this.head.removeChild(document.getElementById(trans.scriptId));
1758 clearTimeout(trans.timeoutId);
1760 window[trans.cb] = undefined;
1762 delete window[trans.cb];
1765 // if hasn't been loaded, wait for load to remove it to prevent script error
1766 window[trans.cb] = function(){
1767 window[trans.cb] = undefined;
1769 delete window[trans.cb];
1776 handleResponse : function(o, trans){
1778 this.destroyTrans(trans, true);
1781 result = trans.reader.readRecords(o);
1783 this.fireEvent("loadexception", this, o, trans.arg, e);
1784 trans.callback.call(trans.scope||window, null, trans.arg, false);
1787 this.fireEvent("load", this, o, trans.arg);
1788 trans.callback.call(trans.scope||window, result, trans.arg, true);
1792 handleFailure : function(trans){
1794 this.destroyTrans(trans, false);
1795 this.fireEvent("loadexception", this, null, trans.arg);
1796 trans.callback.call(trans.scope||window, null, trans.arg, false);
1800 * Ext JS Library 1.1.1
1801 * Copyright(c) 2006-2007, Ext JS, LLC.
1803 * Originally Released Under LGPL - original licence link has changed is not relivant.
1806 * <script type="text/javascript">
1810 * @class Roo.data.JsonReader
1811 * @extends Roo.data.DataReader
1812 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1813 * based on mappings in a provided Roo.data.Record constructor.
1815 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1816 * in the reply previously.
1821 var RecordDef = Roo.data.Record.create([
1822 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
1823 {name: 'occupation'} // This field will use "occupation" as the mapping.
1825 var myReader = new Roo.data.JsonReader({
1826 totalProperty: "results", // The property which contains the total dataset size (optional)
1827 root: "rows", // The property which contains an Array of row objects
1828 id: "id" // The property within each row object that provides an ID for the record (optional)
1832 * This would consume a JSON file like this:
1834 { 'results': 2, 'rows': [
1835 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1836 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1839 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1840 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1841 * paged from the remote server.
1842 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1843 * @cfg {String} root name of the property which contains the Array of row objects.
1844 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1845 * @cfg {Array} fields Array of field definition objects
1847 * Create a new JsonReader
1848 * @param {Object} meta Metadata configuration options
1849 * @param {Object} recordType Either an Array of field definition objects,
1850 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1852 Roo.data.JsonReader = function(meta, recordType){
1855 // set some defaults:
1857 totalProperty: 'total',
1858 successProperty : 'success',
1863 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1865 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1867 readerType : 'Json',
1870 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
1871 * Used by Store query builder to append _requestMeta to params.
1874 metaFromRemote : false,
1876 * This method is only used by a DataProxy which has retrieved data from a remote server.
1877 * @param {Object} response The XHR object which contains the JSON data in its responseText.
1878 * @return {Object} data A data block which is used by an Roo.data.Store object as
1879 * a cache of Roo.data.Records.
1881 read : function(response){
1882 var json = response.responseText;
1884 var o = /* eval:var:o */ eval("("+json+")");
1886 throw {message: "JsonReader.read: Json object not found"};
1892 this.metaFromRemote = true;
1893 this.meta = o.metaData;
1894 this.recordType = Roo.data.Record.create(o.metaData.fields);
1895 this.onMetaChange(this.meta, this.recordType, o);
1897 return this.readRecords(o);
1900 // private function a store will implement
1901 onMetaChange : function(meta, recordType, o){
1908 simpleAccess: function(obj, subsc) {
1915 getJsonAccessor: function(){
1917 return function(expr) {
1919 return(re.test(expr))
1920 ? new Function("obj", "return obj." + expr)
1930 * Create a data block containing Roo.data.Records from an XML document.
1931 * @param {Object} o An object which contains an Array of row objects in the property specified
1932 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1933 * which contains the total size of the dataset.
1934 * @return {Object} data A data block which is used by an Roo.data.Store object as
1935 * a cache of Roo.data.Records.
1937 readRecords : function(o){
1939 * After any data loads, the raw JSON data is available for further custom processing.
1943 var s = this.meta, Record = this.recordType,
1944 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1946 // Generate extraction functions for the totalProperty, the root, the id, and for each field
1948 if(s.totalProperty) {
1949 this.getTotal = this.getJsonAccessor(s.totalProperty);
1951 if(s.successProperty) {
1952 this.getSuccess = this.getJsonAccessor(s.successProperty);
1954 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1956 var g = this.getJsonAccessor(s.id);
1957 this.getId = function(rec) {
1959 return (r === undefined || r === "") ? null : r;
1962 this.getId = function(){return null;};
1965 for(var jj = 0; jj < fl; jj++){
1967 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1968 this.ef[jj] = this.getJsonAccessor(map);
1972 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1973 if(s.totalProperty){
1974 var vt = parseInt(this.getTotal(o), 10);
1979 if(s.successProperty){
1980 var vs = this.getSuccess(o);
1981 if(vs === false || vs === 'false'){
1986 for(var i = 0; i < c; i++){
1989 var id = this.getId(n);
1990 for(var j = 0; j < fl; j++){
1992 var v = this.ef[j](n);
1994 Roo.log('missing convert for ' + f.name);
1998 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
2002 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
2008 var record = new Record(values, id);
2010 records[i] = record;
2016 totalRecords : totalRecords
2019 // used when loading children.. @see loadDataFromChildren
2020 toLoadData: function(rec)
2022 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2023 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2024 return { data : data, total : data.length };
2029 * Ext JS Library 1.1.1
2030 * Copyright(c) 2006-2007, Ext JS, LLC.
2032 * Originally Released Under LGPL - original licence link has changed is not relivant.
2035 * <script type="text/javascript">
2039 * @class Roo.data.XmlReader
2040 * @extends Roo.data.DataReader
2041 * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2042 * based on mappings in a provided Roo.data.Record constructor.<br><br>
2044 * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2045 * header in the HTTP response must be set to "text/xml".</em>
2049 var RecordDef = Roo.data.Record.create([
2050 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
2051 {name: 'occupation'} // This field will use "occupation" as the mapping.
2053 var myReader = new Roo.data.XmlReader({
2054 totalRecords: "results", // The element which contains the total dataset size (optional)
2055 record: "row", // The repeated element which contains row information
2056 id: "id" // The element within the row that provides an ID for the record (optional)
2060 * This would consume an XML file like this:
2064 <results>2</results>
2067 <name>Bill</name>
2068 <occupation>Gardener</occupation>
2072 <name>Ben</name>
2073 <occupation>Horticulturalist</occupation>
2077 * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2078 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2079 * paged from the remote server.
2080 * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2081 * @cfg {String} success The DomQuery path to the success attribute used by forms.
2082 * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2083 * a record identifier value.
2085 * Create a new XmlReader
2086 * @param {Object} meta Metadata configuration options
2087 * @param {Mixed} recordType The definition of the data record type to produce. This can be either a valid
2088 * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2089 * Roo.data.Record.create. See the {@link Roo.data.Record} class for more details.
2091 Roo.data.XmlReader = function(meta, recordType){
2093 Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2095 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2100 * This method is only used by a DataProxy which has retrieved data from a remote server.
2101 * @param {Object} response The XHR object which contains the parsed XML document. The response is expected
2102 * to contain a method called 'responseXML' that returns an XML document object.
2103 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2104 * a cache of Roo.data.Records.
2106 read : function(response){
2107 var doc = response.responseXML;
2109 throw {message: "XmlReader.read: XML Document not available"};
2111 return this.readRecords(doc);
2115 * Create a data block containing Roo.data.Records from an XML document.
2116 * @param {Object} doc A parsed XML document.
2117 * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2118 * a cache of Roo.data.Records.
2120 readRecords : function(doc){
2122 * After any data loads/reads, the raw XML Document is available for further custom processing.
2126 var root = doc.documentElement || doc;
2127 var q = Roo.DomQuery;
2128 var recordType = this.recordType, fields = recordType.prototype.fields;
2129 var sid = this.meta.id;
2130 var totalRecords = 0, success = true;
2131 if(this.meta.totalRecords){
2132 totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2135 if(this.meta.success){
2136 var sv = q.selectValue(this.meta.success, root, true);
2137 success = sv !== false && sv !== 'false';
2140 var ns = q.select(this.meta.record, root);
2141 for(var i = 0, len = ns.length; i < len; i++) {
2144 var id = sid ? q.selectValue(sid, n) : undefined;
2145 for(var j = 0, jlen = fields.length; j < jlen; j++){
2146 var f = fields.items[j];
2147 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2151 var record = new recordType(values, id);
2153 records[records.length] = record;
2159 totalRecords : totalRecords || records.length
2164 * Ext JS Library 1.1.1
2165 * Copyright(c) 2006-2007, Ext JS, LLC.
2167 * Originally Released Under LGPL - original licence link has changed is not relivant.
2170 * <script type="text/javascript">
2174 * @class Roo.data.ArrayReader
2175 * @extends Roo.data.DataReader
2176 * Data reader class to create an Array of Roo.data.Record objects from an Array.
2177 * Each element of that Array represents a row of data fields. The
2178 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2179 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2183 var RecordDef = Roo.data.Record.create([
2184 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
2185 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
2187 var myReader = new Roo.data.ArrayReader({
2188 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
2192 * This would consume an Array like this:
2194 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2198 * Create a new JsonReader
2199 * @param {Object} meta Metadata configuration options.
2200 * @param {Object|Array} recordType Either an Array of field definition objects
2202 * @cfg {Array} fields Array of field definition objects
2203 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2204 * as specified to {@link Roo.data.Record#create},
2205 * or an {@link Roo.data.Record} object
2208 * created using {@link Roo.data.Record#create}.
2210 Roo.data.ArrayReader = function(meta, recordType)
2212 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2215 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2218 * Create a data block containing Roo.data.Records from an XML document.
2219 * @param {Object} o An Array of row objects which represents the dataset.
2220 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2221 * a cache of Roo.data.Records.
2223 readRecords : function(o)
2225 var sid = this.meta ? this.meta.id : null;
2226 var recordType = this.recordType, fields = recordType.prototype.fields;
2229 for(var i = 0; i < root.length; i++){
2232 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2233 for(var j = 0, jlen = fields.length; j < jlen; j++){
2234 var f = fields.items[j];
2235 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2236 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2240 var record = new recordType(values, id);
2242 records[records.length] = record;
2246 totalRecords : records.length
2249 // used when loading children.. @see loadDataFromChildren
2250 toLoadData: function(rec)
2252 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2253 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2260 * Ext JS Library 1.1.1
2261 * Copyright(c) 2006-2007, Ext JS, LLC.
2263 * Originally Released Under LGPL - original licence link has changed is not relivant.
2266 * <script type="text/javascript">
2271 * @class Roo.data.Tree
2272 * @extends Roo.util.Observable
2273 * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2274 * in the tree have most standard DOM functionality.
2276 * @param {Node} root (optional) The root node
2278 Roo.data.Tree = function(root){
2281 * The root node for this tree
2286 this.setRootNode(root);
2291 * Fires when a new child node is appended to a node in this tree.
2292 * @param {Tree} tree The owner tree
2293 * @param {Node} parent The parent node
2294 * @param {Node} node The newly appended node
2295 * @param {Number} index The index of the newly appended node
2300 * Fires when a child node is removed from a node in this tree.
2301 * @param {Tree} tree The owner tree
2302 * @param {Node} parent The parent node
2303 * @param {Node} node The child node removed
2308 * Fires when a node is moved to a new location in the tree
2309 * @param {Tree} tree The owner tree
2310 * @param {Node} node The node moved
2311 * @param {Node} oldParent The old parent of this node
2312 * @param {Node} newParent The new parent of this node
2313 * @param {Number} index The index it was moved to
2318 * Fires when a new child node is inserted in a node in this tree.
2319 * @param {Tree} tree The owner tree
2320 * @param {Node} parent The parent node
2321 * @param {Node} node The child node inserted
2322 * @param {Node} refNode The child node the node was inserted before
2326 * @event beforeappend
2327 * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2328 * @param {Tree} tree The owner tree
2329 * @param {Node} parent The parent node
2330 * @param {Node} node The child node to be appended
2332 "beforeappend" : true,
2334 * @event beforeremove
2335 * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2336 * @param {Tree} tree The owner tree
2337 * @param {Node} parent The parent node
2338 * @param {Node} node The child node to be removed
2340 "beforeremove" : true,
2343 * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2344 * @param {Tree} tree The owner tree
2345 * @param {Node} node The node being moved
2346 * @param {Node} oldParent The parent of the node
2347 * @param {Node} newParent The new parent the node is moving to
2348 * @param {Number} index The index it is being moved to
2350 "beforemove" : true,
2352 * @event beforeinsert
2353 * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2354 * @param {Tree} tree The owner tree
2355 * @param {Node} parent The parent node
2356 * @param {Node} node The child node to be inserted
2357 * @param {Node} refNode The child node the node is being inserted before
2359 "beforeinsert" : true
2362 Roo.data.Tree.superclass.constructor.call(this);
2365 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2368 proxyNodeEvent : function(){
2369 return this.fireEvent.apply(this, arguments);
2373 * Returns the root node for this tree.
2376 getRootNode : function(){
2381 * Sets the root node for this tree.
2382 * @param {Node} node
2385 setRootNode : function(node){
2387 node.ownerTree = this;
2389 this.registerNode(node);
2394 * Gets a node in this tree by its id.
2395 * @param {String} id
2398 getNodeById : function(id){
2399 return this.nodeHash[id];
2402 registerNode : function(node){
2403 this.nodeHash[node.id] = node;
2406 unregisterNode : function(node){
2407 delete this.nodeHash[node.id];
2410 toString : function(){
2411 return "[Tree"+(this.id?" "+this.id:"")+"]";
2416 * @class Roo.data.Node
2417 * @extends Roo.util.Observable
2418 * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2419 * @cfg {String} id The id for this node. If one is not specified, one is generated.
2421 * @param {Object} attributes The attributes/config for the node
2423 Roo.data.Node = function(attributes){
2425 * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2428 this.attributes = attributes || {};
2429 this.leaf = this.attributes.leaf;
2431 * The node id. @type String
2433 this.id = this.attributes.id;
2435 this.id = Roo.id(null, "ynode-");
2436 this.attributes.id = this.id;
2441 * All child nodes of this node. @type Array
2443 this.childNodes = [];
2444 if(!this.childNodes.indexOf){ // indexOf is a must
2445 this.childNodes.indexOf = function(o){
2446 for(var i = 0, len = this.length; i < len; i++){
2455 * The parent node for this node. @type Node
2457 this.parentNode = null;
2459 * The first direct child node of this node, or null if this node has no child nodes. @type Node
2461 this.firstChild = null;
2463 * The last direct child node of this node, or null if this node has no child nodes. @type Node
2465 this.lastChild = null;
2467 * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2469 this.previousSibling = null;
2471 * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2473 this.nextSibling = null;
2478 * Fires when a new child node is appended
2479 * @param {Tree} tree The owner tree
2480 * @param {Node} this This node
2481 * @param {Node} node The newly appended node
2482 * @param {Number} index The index of the newly appended node
2487 * Fires when a child node is removed
2488 * @param {Tree} tree The owner tree
2489 * @param {Node} this This node
2490 * @param {Node} node The removed node
2495 * Fires when this node is moved to a new location in the tree
2496 * @param {Tree} tree The owner tree
2497 * @param {Node} this This node
2498 * @param {Node} oldParent The old parent of this node
2499 * @param {Node} newParent The new parent of this node
2500 * @param {Number} index The index it was moved to
2505 * Fires when a new child node is inserted.
2506 * @param {Tree} tree The owner tree
2507 * @param {Node} this This node
2508 * @param {Node} node The child node inserted
2509 * @param {Node} refNode The child node the node was inserted before
2513 * @event beforeappend
2514 * Fires before a new child is appended, return false to cancel the append.
2515 * @param {Tree} tree The owner tree
2516 * @param {Node} this This node
2517 * @param {Node} node The child node to be appended
2519 "beforeappend" : true,
2521 * @event beforeremove
2522 * Fires before a child is removed, return false to cancel the remove.
2523 * @param {Tree} tree The owner tree
2524 * @param {Node} this This node
2525 * @param {Node} node The child node to be removed
2527 "beforeremove" : true,
2530 * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2531 * @param {Tree} tree The owner tree
2532 * @param {Node} this This node
2533 * @param {Node} oldParent The parent of this node
2534 * @param {Node} newParent The new parent this node is moving to
2535 * @param {Number} index The index it is being moved to
2537 "beforemove" : true,
2539 * @event beforeinsert
2540 * Fires before a new child is inserted, return false to cancel the insert.
2541 * @param {Tree} tree The owner tree
2542 * @param {Node} this This node
2543 * @param {Node} node The child node to be inserted
2544 * @param {Node} refNode The child node the node is being inserted before
2546 "beforeinsert" : true
2548 this.listeners = this.attributes.listeners;
2549 Roo.data.Node.superclass.constructor.call(this);
2552 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2553 fireEvent : function(evtName){
2554 // first do standard event for this node
2555 if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2558 // then bubble it up to the tree if the event wasn't cancelled
2559 var ot = this.getOwnerTree();
2561 if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2569 * Returns true if this node is a leaf
2572 isLeaf : function(){
2573 return this.leaf === true;
2577 setFirstChild : function(node){
2578 this.firstChild = node;
2582 setLastChild : function(node){
2583 this.lastChild = node;
2588 * Returns true if this node is the last child of its parent
2591 isLast : function(){
2592 return (!this.parentNode ? true : this.parentNode.lastChild == this);
2596 * Returns true if this node is the first child of its parent
2599 isFirst : function(){
2600 return (!this.parentNode ? true : this.parentNode.firstChild == this);
2603 hasChildNodes : function(){
2604 return !this.isLeaf() && this.childNodes.length > 0;
2608 * Insert node(s) as the last child node of this node.
2609 * @param {Node/Array} node The node or Array of nodes to append
2610 * @return {Node} The appended node if single append, or null if an array was passed
2612 appendChild : function(node){
2614 if(node instanceof Array){
2616 }else if(arguments.length > 1){
2620 // if passed an array or multiple args do them one by one
2622 for(var i = 0, len = multi.length; i < len; i++) {
2623 this.appendChild(multi[i]);
2626 if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2629 var index = this.childNodes.length;
2630 var oldParent = node.parentNode;
2631 // it's a move, make sure we move it cleanly
2633 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2636 oldParent.removeChild(node);
2639 index = this.childNodes.length;
2641 this.setFirstChild(node);
2643 this.childNodes.push(node);
2644 node.parentNode = this;
2645 var ps = this.childNodes[index-1];
2647 node.previousSibling = ps;
2648 ps.nextSibling = node;
2650 node.previousSibling = null;
2652 node.nextSibling = null;
2653 this.setLastChild(node);
2654 node.setOwnerTree(this.getOwnerTree());
2655 this.fireEvent("append", this.ownerTree, this, node, index);
2656 if(this.ownerTree) {
2657 this.ownerTree.fireEvent("appendnode", this, node, index);
2660 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2667 * Removes a child node from this node.
2668 * @param {Node} node The node to remove
2669 * @return {Node} The removed node
2671 removeChild : function(node){
2672 var index = this.childNodes.indexOf(node);
2676 if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2680 // remove it from childNodes collection
2681 this.childNodes.splice(index, 1);
2684 if(node.previousSibling){
2685 node.previousSibling.nextSibling = node.nextSibling;
2687 if(node.nextSibling){
2688 node.nextSibling.previousSibling = node.previousSibling;
2691 // update child refs
2692 if(this.firstChild == node){
2693 this.setFirstChild(node.nextSibling);
2695 if(this.lastChild == node){
2696 this.setLastChild(node.previousSibling);
2699 node.setOwnerTree(null);
2700 // clear any references from the node
2701 node.parentNode = null;
2702 node.previousSibling = null;
2703 node.nextSibling = null;
2704 this.fireEvent("remove", this.ownerTree, this, node);
2709 * Inserts the first node before the second node in this nodes childNodes collection.
2710 * @param {Node} node The node to insert
2711 * @param {Node} refNode The node to insert before (if null the node is appended)
2712 * @return {Node} The inserted node
2714 insertBefore : function(node, refNode){
2715 if(!refNode){ // like standard Dom, refNode can be null for append
2716 return this.appendChild(node);
2719 if(node == refNode){
2723 if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2726 var index = this.childNodes.indexOf(refNode);
2727 var oldParent = node.parentNode;
2728 var refIndex = index;
2730 // when moving internally, indexes will change after remove
2731 if(oldParent == this && this.childNodes.indexOf(node) < index){
2735 // it's a move, make sure we move it cleanly
2737 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2740 oldParent.removeChild(node);
2743 this.setFirstChild(node);
2745 this.childNodes.splice(refIndex, 0, node);
2746 node.parentNode = this;
2747 var ps = this.childNodes[refIndex-1];
2749 node.previousSibling = ps;
2750 ps.nextSibling = node;
2752 node.previousSibling = null;
2754 node.nextSibling = refNode;
2755 refNode.previousSibling = node;
2756 node.setOwnerTree(this.getOwnerTree());
2757 this.fireEvent("insert", this.ownerTree, this, node, refNode);
2759 node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2765 * Returns the child node at the specified index.
2766 * @param {Number} index
2769 item : function(index){
2770 return this.childNodes[index];
2774 * Replaces one child node in this node with another.
2775 * @param {Node} newChild The replacement node
2776 * @param {Node} oldChild The node to replace
2777 * @return {Node} The replaced node
2779 replaceChild : function(newChild, oldChild){
2780 this.insertBefore(newChild, oldChild);
2781 this.removeChild(oldChild);
2786 * Returns the index of a child node
2787 * @param {Node} node
2788 * @return {Number} The index of the node or -1 if it was not found
2790 indexOf : function(child){
2791 return this.childNodes.indexOf(child);
2795 * Returns the tree this node is in.
2798 getOwnerTree : function(){
2799 // if it doesn't have one, look for one
2800 if(!this.ownerTree){
2804 this.ownerTree = p.ownerTree;
2810 return this.ownerTree;
2814 * Returns depth of this node (the root node has a depth of 0)
2817 getDepth : function(){
2820 while(p.parentNode){
2828 setOwnerTree : function(tree){
2829 // if it's move, we need to update everyone
2830 if(tree != this.ownerTree){
2832 this.ownerTree.unregisterNode(this);
2834 this.ownerTree = tree;
2835 var cs = this.childNodes;
2836 for(var i = 0, len = cs.length; i < len; i++) {
2837 cs[i].setOwnerTree(tree);
2840 tree.registerNode(this);
2846 * Returns the path for this node. The path can be used to expand or select this node programmatically.
2847 * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2848 * @return {String} The path
2850 getPath : function(attr){
2851 attr = attr || "id";
2852 var p = this.parentNode;
2853 var b = [this.attributes[attr]];
2855 b.unshift(p.attributes[attr]);
2858 var sep = this.getOwnerTree().pathSeparator;
2859 return sep + b.join(sep);
2863 * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2864 * function call will be the scope provided or the current node. The arguments to the function
2865 * will be the args provided or the current node. If the function returns false at any point,
2866 * the bubble is stopped.
2867 * @param {Function} fn The function to call
2868 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2869 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2871 bubble : function(fn, scope, args){
2874 if(fn.call(scope || p, args || p) === false){
2882 * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2883 * function call will be the scope provided or the current node. The arguments to the function
2884 * will be the args provided or the current node. If the function returns false at any point,
2885 * the cascade is stopped on that branch.
2886 * @param {Function} fn The function to call
2887 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2888 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2890 cascade : function(fn, scope, args){
2891 if(fn.call(scope || this, args || this) !== false){
2892 var cs = this.childNodes;
2893 for(var i = 0, len = cs.length; i < len; i++) {
2894 cs[i].cascade(fn, scope, args);
2900 * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2901 * function call will be the scope provided or the current node. The arguments to the function
2902 * will be the args provided or the current node. If the function returns false at any point,
2903 * the iteration stops.
2904 * @param {Function} fn The function to call
2905 * @param {Object} scope (optional) The scope of the function (defaults to current node)
2906 * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2908 eachChild : function(fn, scope, args){
2909 var cs = this.childNodes;
2910 for(var i = 0, len = cs.length; i < len; i++) {
2911 if(fn.call(scope || this, args || cs[i]) === false){
2918 * Finds the first child that has the attribute with the specified value.
2919 * @param {String} attribute The attribute name
2920 * @param {Mixed} value The value to search for
2921 * @return {Node} The found child or null if none was found
2923 findChild : function(attribute, value){
2924 var cs = this.childNodes;
2925 for(var i = 0, len = cs.length; i < len; i++) {
2926 if(cs[i].attributes[attribute] == value){
2934 * Finds the first child by a custom function. The child matches if the function passed
2936 * @param {Function} fn
2937 * @param {Object} scope (optional)
2938 * @return {Node} The found child or null if none was found
2940 findChildBy : function(fn, scope){
2941 var cs = this.childNodes;
2942 for(var i = 0, len = cs.length; i < len; i++) {
2943 if(fn.call(scope||cs[i], cs[i]) === true){
2951 * Sorts this nodes children using the supplied sort function
2952 * @param {Function} fn
2953 * @param {Object} scope (optional)
2955 sort : function(fn, scope){
2956 var cs = this.childNodes;
2957 var len = cs.length;
2959 var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2961 for(var i = 0; i < len; i++){
2963 n.previousSibling = cs[i-1];
2964 n.nextSibling = cs[i+1];
2966 this.setFirstChild(n);
2969 this.setLastChild(n);
2976 * Returns true if this node is an ancestor (at any point) of the passed node.
2977 * @param {Node} node
2980 contains : function(node){
2981 return node.isAncestor(this);
2985 * Returns true if the passed node is an ancestor (at any point) of this node.
2986 * @param {Node} node
2989 isAncestor : function(node){
2990 var p = this.parentNode;
3000 toString : function(){
3001 return "[Node"+(this.id?" "+this.id:"")+"]";
3005 * Ext JS Library 1.1.1
3006 * Copyright(c) 2006-2007, Ext JS, LLC.
3008 * Originally Released Under LGPL - original licence link has changed is not relivant.
3011 * <script type="text/javascript">
3017 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
3018 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
3019 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3021 * Create a new Shadow
3022 * @param {Object} config The config object
3024 Roo.Shadow = function(config){
3025 Roo.apply(this, config);
3026 if(typeof this.mode != "string"){
3027 this.mode = this.defaultMode;
3029 var o = this.offset, a = {h: 0};
3030 var rad = Math.floor(this.offset/2);
3031 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3037 a.l -= this.offset + rad;
3038 a.t -= this.offset + rad;
3049 a.l -= (this.offset - rad);
3050 a.t -= this.offset + rad;
3052 a.w -= (this.offset - rad)*2;
3063 a.l -= (this.offset - rad);
3064 a.t -= (this.offset - rad);
3066 a.w -= (this.offset + rad + 1);
3067 a.h -= (this.offset + rad);
3076 Roo.Shadow.prototype = {
3078 * @cfg {String} mode
3079 * The shadow display mode. Supports the following options:<br />
3080 * sides: Shadow displays on both sides and bottom only<br />
3081 * frame: Shadow displays equally on all four sides<br />
3082 * drop: Traditional bottom-right drop shadow (default)
3086 * @cfg {String} offset
3087 * The number of pixels to offset the shadow from the element (defaults to 4)
3092 defaultMode: "drop",
3095 * Displays the shadow under the target element
3096 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3098 show : function(target){
3099 target = Roo.get(target);
3101 this.el = Roo.Shadow.Pool.pull();
3102 if(this.el.dom.nextSibling != target.dom){
3103 this.el.insertBefore(target);
3106 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3108 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3111 target.getLeft(true),
3112 target.getTop(true),
3116 this.el.dom.style.display = "block";
3120 * Returns true if the shadow is visible, else false
3122 isVisible : function(){
3123 return this.el ? true : false;
3127 * Direct alignment when values are already available. Show must be called at least once before
3128 * calling this method to ensure it is initialized.
3129 * @param {Number} left The target element left position
3130 * @param {Number} top The target element top position
3131 * @param {Number} width The target element width
3132 * @param {Number} height The target element height
3134 realign : function(l, t, w, h){
3138 var a = this.adjusts, d = this.el.dom, s = d.style;
3140 s.left = (l+a.l)+"px";
3141 s.top = (t+a.t)+"px";
3142 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3144 if(s.width != sws || s.height != shs){
3148 var cn = d.childNodes;
3149 var sww = Math.max(0, (sw-12))+"px";
3150 cn[0].childNodes[1].style.width = sww;
3151 cn[1].childNodes[1].style.width = sww;
3152 cn[2].childNodes[1].style.width = sww;
3153 cn[1].style.height = Math.max(0, (sh-12))+"px";
3163 this.el.dom.style.display = "none";
3164 Roo.Shadow.Pool.push(this.el);
3170 * Adjust the z-index of this shadow
3171 * @param {Number} zindex The new z-index
3173 setZIndex : function(z){
3176 this.el.setStyle("z-index", z);
3181 // Private utility class that manages the internal Shadow cache
3182 Roo.Shadow.Pool = function(){
3184 var markup = Roo.isIE ?
3185 '<div class="x-ie-shadow"></div>' :
3186 '<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>';
3191 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3192 sh.autoBoxAdjust = false;
3197 push : function(sh){
3203 * Ext JS Library 1.1.1
3204 * Copyright(c) 2006-2007, Ext JS, LLC.
3206 * Originally Released Under LGPL - original licence link has changed is not relivant.
3209 * <script type="text/javascript">
3214 * @class Roo.SplitBar
3215 * @extends Roo.util.Observable
3216 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3220 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3221 Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3222 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3223 split.minSize = 100;
3224 split.maxSize = 600;
3225 split.animate = true;
3226 split.on('moved', splitterMoved);
3229 * Create a new SplitBar
3230 * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
3231 * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
3232 * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3233 * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or
3234 Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3235 position of the SplitBar).
3237 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3240 this.el = Roo.get(dragElement, true);
3241 this.el.dom.unselectable = "on";
3243 this.resizingEl = Roo.get(resizingElement, true);
3247 * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3248 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3251 this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3254 * The minimum size of the resizing element. (Defaults to 0)
3260 * The maximum size of the resizing element. (Defaults to 2000)
3263 this.maxSize = 2000;
3266 * Whether to animate the transition to the new size
3269 this.animate = false;
3272 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3275 this.useShim = false;
3282 this.proxy = Roo.SplitBar.createProxy(this.orientation);
3284 this.proxy = Roo.get(existingProxy).dom;
3287 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3290 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3293 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3296 this.dragSpecs = {};
3299 * @private The adapter to use to positon and resize elements
3301 this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3302 this.adapter.init(this);
3304 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3306 this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3307 this.el.addClass("x-splitbar-h");
3310 this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3311 this.el.addClass("x-splitbar-v");
3317 * Fires when the splitter is moved (alias for {@link #event-moved})
3318 * @param {Roo.SplitBar} this
3319 * @param {Number} newSize the new width or height
3324 * Fires when the splitter is moved
3325 * @param {Roo.SplitBar} this
3326 * @param {Number} newSize the new width or height
3330 * @event beforeresize
3331 * Fires before the splitter is dragged
3332 * @param {Roo.SplitBar} this
3334 "beforeresize" : true,
3336 "beforeapply" : true
3339 Roo.util.Observable.call(this);
3342 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3343 onStartProxyDrag : function(x, y){
3344 this.fireEvent("beforeresize", this);
3346 var o = Roo.DomHelper.insertFirst(document.body, {cls: "x-drag-overlay", html: " "}, true);
3348 o.enableDisplayMode("block");
3349 // all splitbars share the same overlay
3350 Roo.SplitBar.prototype.overlay = o;
3352 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3353 this.overlay.show();
3354 Roo.get(this.proxy).setDisplayed("block");
3355 var size = this.adapter.getElementSize(this);
3356 this.activeMinSize = this.getMinimumSize();;
3357 this.activeMaxSize = this.getMaximumSize();;
3358 var c1 = size - this.activeMinSize;
3359 var c2 = Math.max(this.activeMaxSize - size, 0);
3360 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3361 this.dd.resetConstraints();
3362 this.dd.setXConstraint(
3363 this.placement == Roo.SplitBar.LEFT ? c1 : c2,
3364 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3366 this.dd.setYConstraint(0, 0);
3368 this.dd.resetConstraints();
3369 this.dd.setXConstraint(0, 0);
3370 this.dd.setYConstraint(
3371 this.placement == Roo.SplitBar.TOP ? c1 : c2,
3372 this.placement == Roo.SplitBar.TOP ? c2 : c1
3375 this.dragSpecs.startSize = size;
3376 this.dragSpecs.startPoint = [x, y];
3377 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3381 * @private Called after the drag operation by the DDProxy
3383 onEndProxyDrag : function(e){
3384 Roo.get(this.proxy).setDisplayed(false);
3385 var endPoint = Roo.lib.Event.getXY(e);
3387 this.overlay.hide();
3390 if(this.orientation == Roo.SplitBar.HORIZONTAL){
3391 newSize = this.dragSpecs.startSize +
3392 (this.placement == Roo.SplitBar.LEFT ?
3393 endPoint[0] - this.dragSpecs.startPoint[0] :
3394 this.dragSpecs.startPoint[0] - endPoint[0]
3397 newSize = this.dragSpecs.startSize +
3398 (this.placement == Roo.SplitBar.TOP ?
3399 endPoint[1] - this.dragSpecs.startPoint[1] :
3400 this.dragSpecs.startPoint[1] - endPoint[1]
3403 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3404 if(newSize != this.dragSpecs.startSize){
3405 if(this.fireEvent('beforeapply', this, newSize) !== false){
3406 this.adapter.setElementSize(this, newSize);
3407 this.fireEvent("moved", this, newSize);
3408 this.fireEvent("resize", this, newSize);
3414 * Get the adapter this SplitBar uses
3415 * @return The adapter object
3417 getAdapter : function(){
3418 return this.adapter;
3422 * Set the adapter this SplitBar uses
3423 * @param {Object} adapter A SplitBar adapter object
3425 setAdapter : function(adapter){
3426 this.adapter = adapter;
3427 this.adapter.init(this);
3431 * Gets the minimum size for the resizing element
3432 * @return {Number} The minimum size
3434 getMinimumSize : function(){
3435 return this.minSize;
3439 * Sets the minimum size for the resizing element
3440 * @param {Number} minSize The minimum size
3442 setMinimumSize : function(minSize){
3443 this.minSize = minSize;
3447 * Gets the maximum size for the resizing element
3448 * @return {Number} The maximum size
3450 getMaximumSize : function(){
3451 return this.maxSize;
3455 * Sets the maximum size for the resizing element
3456 * @param {Number} maxSize The maximum size
3458 setMaximumSize : function(maxSize){
3459 this.maxSize = maxSize;
3463 * Sets the initialize size for the resizing element
3464 * @param {Number} size The initial size
3466 setCurrentSize : function(size){
3467 var oldAnimate = this.animate;
3468 this.animate = false;
3469 this.adapter.setElementSize(this, size);
3470 this.animate = oldAnimate;
3474 * Destroy this splitbar.
3475 * @param {Boolean} removeEl True to remove the element
3477 destroy : function(removeEl){
3482 this.proxy.parentNode.removeChild(this.proxy);
3490 * @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.
3492 Roo.SplitBar.createProxy = function(dir){
3493 var proxy = new Roo.Element(document.createElement("div"));
3494 proxy.unselectable();
3495 var cls = 'x-splitbar-proxy';
3496 proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3497 document.body.appendChild(proxy.dom);
3502 * @class Roo.SplitBar.BasicLayoutAdapter
3503 * Default Adapter. It assumes the splitter and resizing element are not positioned
3504 * elements and only gets/sets the width of the element. Generally used for table based layouts.
3506 Roo.SplitBar.BasicLayoutAdapter = function(){
3509 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3510 // do nothing for now
3515 * Called before drag operations to get the current size of the resizing element.
3516 * @param {Roo.SplitBar} s The SplitBar using this adapter
3518 getElementSize : function(s){
3519 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3520 return s.resizingEl.getWidth();
3522 return s.resizingEl.getHeight();
3527 * Called after drag operations to set the size of the resizing element.
3528 * @param {Roo.SplitBar} s The SplitBar using this adapter
3529 * @param {Number} newSize The new size to set
3530 * @param {Function} onComplete A function to be invoked when resizing is complete
3532 setElementSize : function(s, newSize, onComplete){
3533 if(s.orientation == Roo.SplitBar.HORIZONTAL){
3535 s.resizingEl.setWidth(newSize);
3537 onComplete(s, newSize);
3540 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3545 s.resizingEl.setHeight(newSize);
3547 onComplete(s, newSize);
3550 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3557 *@class Roo.SplitBar.AbsoluteLayoutAdapter
3558 * @extends Roo.SplitBar.BasicLayoutAdapter
3559 * Adapter that moves the splitter element to align with the resized sizing element.
3560 * Used with an absolute positioned SplitBar.
3561 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3562 * document.body, make sure you assign an id to the body element.
3564 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3565 this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3566 this.container = Roo.get(container);
3569 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3574 getElementSize : function(s){
3575 return this.basic.getElementSize(s);
3578 setElementSize : function(s, newSize, onComplete){
3579 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3582 moveSplitter : function(s){
3583 var yes = Roo.SplitBar;
3584 switch(s.placement){
3586 s.el.setX(s.resizingEl.getRight());
3589 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3592 s.el.setY(s.resizingEl.getBottom());
3595 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3602 * Orientation constant - Create a vertical SplitBar
3606 Roo.SplitBar.VERTICAL = 1;
3609 * Orientation constant - Create a horizontal SplitBar
3613 Roo.SplitBar.HORIZONTAL = 2;
3616 * Placement constant - The resizing element is to the left of the splitter element
3620 Roo.SplitBar.LEFT = 1;
3623 * Placement constant - The resizing element is to the right of the splitter element
3627 Roo.SplitBar.RIGHT = 2;
3630 * Placement constant - The resizing element is positioned above the splitter element
3634 Roo.SplitBar.TOP = 3;
3637 * Placement constant - The resizing element is positioned under splitter element
3641 Roo.SplitBar.BOTTOM = 4;
3644 * Ext JS Library 1.1.1
3645 * Copyright(c) 2006-2007, Ext JS, LLC.
3647 * Originally Released Under LGPL - original licence link has changed is not relivant.
3650 * <script type="text/javascript">
3655 * @extends Roo.util.Observable
3656 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
3657 * This class also supports single and multi selection modes. <br>
3658 * Create a data model bound view:
3660 var store = new Roo.data.Store(...);
3662 var view = new Roo.View({
3664 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
3667 selectedClass: "ydataview-selected",
3671 // listen for node click?
3672 view.on("click", function(vw, index, node, e){
3673 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3677 dataModel.load("foobar.xml");
3679 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3681 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3682 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3684 * Note: old style constructor is still suported (container, template, config)
3688 * @param {Object} config The config object
3691 Roo.View = function(config, depreciated_tpl, depreciated_config){
3693 this.parent = false;
3695 if (typeof(depreciated_tpl) == 'undefined') {
3696 // new way.. - universal constructor.
3697 Roo.apply(this, config);
3698 this.el = Roo.get(this.el);
3701 this.el = Roo.get(config);
3702 this.tpl = depreciated_tpl;
3703 Roo.apply(this, depreciated_config);
3705 this.wrapEl = this.el.wrap().wrap();
3706 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3709 if(typeof(this.tpl) == "string"){
3710 this.tpl = new Roo.Template(this.tpl);
3712 // support xtype ctors..
3713 this.tpl = new Roo.factory(this.tpl, Roo);
3722 * @event beforeclick
3723 * Fires before a click is processed. Returns false to cancel the default action.
3724 * @param {Roo.View} this
3725 * @param {Number} index The index of the target node
3726 * @param {HTMLElement} node The target node
3727 * @param {Roo.EventObject} e The raw event object
3729 "beforeclick" : true,
3732 * Fires when a template node is clicked.
3733 * @param {Roo.View} this
3734 * @param {Number} index The index of the target node
3735 * @param {HTMLElement} node The target node
3736 * @param {Roo.EventObject} e The raw event object
3741 * Fires when a template node is double clicked.
3742 * @param {Roo.View} this
3743 * @param {Number} index The index of the target node
3744 * @param {HTMLElement} node The target node
3745 * @param {Roo.EventObject} e The raw event object
3749 * @event contextmenu
3750 * Fires when a template node is right clicked.
3751 * @param {Roo.View} this
3752 * @param {Number} index The index of the target node
3753 * @param {HTMLElement} node The target node
3754 * @param {Roo.EventObject} e The raw event object
3756 "contextmenu" : true,
3758 * @event selectionchange
3759 * Fires when the selected nodes change.
3760 * @param {Roo.View} this
3761 * @param {Array} selections Array of the selected nodes
3763 "selectionchange" : true,
3766 * @event beforeselect
3767 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3768 * @param {Roo.View} this
3769 * @param {HTMLElement} node The node to be selected
3770 * @param {Array} selections Array of currently selected nodes
3772 "beforeselect" : true,
3774 * @event preparedata
3775 * Fires on every row to render, to allow you to change the data.
3776 * @param {Roo.View} this
3777 * @param {Object} data to be rendered (change this)
3779 "preparedata" : true
3787 "click": this.onClick,
3788 "dblclick": this.onDblClick,
3789 "contextmenu": this.onContextMenu,
3793 this.selections = [];
3795 this.cmp = new Roo.CompositeElementLite([]);
3797 this.store = Roo.factory(this.store, Roo.data);
3798 this.setStore(this.store, true);
3801 if ( this.footer && this.footer.xtype) {
3803 var fctr = this.wrapEl.appendChild(document.createElement("div"));
3805 this.footer.dataSource = this.store;
3806 this.footer.container = fctr;
3807 this.footer = Roo.factory(this.footer, Roo);
3808 fctr.insertFirst(this.el);
3810 // this is a bit insane - as the paging toolbar seems to detach the el..
3811 // dom.parentNode.parentNode.parentNode
3812 // they get detached?
3816 Roo.View.superclass.constructor.call(this);
3821 Roo.extend(Roo.View, Roo.util.Observable, {
3824 * @cfg {Roo.data.Store} store Data store to load data from.
3829 * @cfg {String|Roo.Element} el The container element.
3834 * @cfg {String|Roo.Template} tpl The template used by this View
3838 * @cfg {String} dataName the named area of the template to use as the data area
3839 * Works with domtemplates roo-name="name"
3843 * @cfg {String} selectedClass The css class to add to selected nodes
3845 selectedClass : "x-view-selected",
3847 * @cfg {String} emptyText The empty text to show when nothing is loaded.
3852 * @cfg {String} text to display on mask (default Loading)
3856 * @cfg {Boolean} multiSelect Allow multiple selection
3858 multiSelect : false,
3860 * @cfg {Boolean} singleSelect Allow single selection
3862 singleSelect: false,
3865 * @cfg {Boolean} toggleSelect - selecting
3867 toggleSelect : false,
3870 * @cfg {Boolean} tickable - selecting
3875 * Returns the element this view is bound to.
3876 * @return {Roo.Element}
3885 * Refreshes the view. - called by datachanged on the store. - do not call directly.
3887 refresh : function(){
3888 //Roo.log('refresh');
3891 // if we are using something like 'domtemplate', then
3892 // the what gets used is:
3893 // t.applySubtemplate(NAME, data, wrapping data..)
3894 // the outer template then get' applied with
3895 // the store 'extra data'
3896 // and the body get's added to the
3897 // roo-name="data" node?
3898 // <span class='roo-tpl-{name}'></span> ?????
3902 this.clearSelections();
3905 var records = this.store.getRange();
3906 if(records.length < 1) {
3908 // is this valid?? = should it render a template??
3910 this.el.update(this.emptyText);
3914 if (this.dataName) {
3915 this.el.update(t.apply(this.store.meta)); //????
3916 el = this.el.child('.roo-tpl-' + this.dataName);
3919 for(var i = 0, len = records.length; i < len; i++){
3920 var data = this.prepareData(records[i].data, i, records[i]);
3921 this.fireEvent("preparedata", this, data, i, records[i]);
3923 var d = Roo.apply({}, data);
3926 Roo.apply(d, {'roo-id' : Roo.id()});
3930 Roo.each(this.parent.item, function(item){
3931 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3934 Roo.apply(d, {'roo-data-checked' : 'checked'});
3938 html[html.length] = Roo.util.Format.trim(
3940 t.applySubtemplate(this.dataName, d, this.store.meta) :
3947 el.update(html.join(""));
3948 this.nodes = el.dom.childNodes;
3949 this.updateIndexes(0);
3954 * Function to override to reformat the data that is sent to
3955 * the template for each node.
3956 * DEPRICATED - use the preparedata event handler.
3957 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3958 * a JSON object for an UpdateManager bound view).
3960 prepareData : function(data, index, record)
3962 this.fireEvent("preparedata", this, data, index, record);
3966 onUpdate : function(ds, record){
3967 // Roo.log('on update');
3968 this.clearSelections();
3969 var index = this.store.indexOf(record);
3970 var n = this.nodes[index];
3971 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3972 n.parentNode.removeChild(n);
3973 this.updateIndexes(index, index);
3979 onAdd : function(ds, records, index)
3981 //Roo.log(['on Add', ds, records, index] );
3982 this.clearSelections();
3983 if(this.nodes.length == 0){
3987 var n = this.nodes[index];
3988 for(var i = 0, len = records.length; i < len; i++){
3989 var d = this.prepareData(records[i].data, i, records[i]);
3991 this.tpl.insertBefore(n, d);
3994 this.tpl.append(this.el, d);
3997 this.updateIndexes(index);
4000 onRemove : function(ds, record, index){
4001 // Roo.log('onRemove');
4002 this.clearSelections();
4003 var el = this.dataName ?
4004 this.el.child('.roo-tpl-' + this.dataName) :
4007 el.dom.removeChild(this.nodes[index]);
4008 this.updateIndexes(index);
4012 * Refresh an individual node.
4013 * @param {Number} index
4015 refreshNode : function(index){
4016 this.onUpdate(this.store, this.store.getAt(index));
4019 updateIndexes : function(startIndex, endIndex){
4020 var ns = this.nodes;
4021 startIndex = startIndex || 0;
4022 endIndex = endIndex || ns.length - 1;
4023 for(var i = startIndex; i <= endIndex; i++){
4024 ns[i].nodeIndex = i;
4029 * Changes the data store this view uses and refresh the view.
4030 * @param {Store} store
4032 setStore : function(store, initial){
4033 if(!initial && this.store){
4034 this.store.un("datachanged", this.refresh);
4035 this.store.un("add", this.onAdd);
4036 this.store.un("remove", this.onRemove);
4037 this.store.un("update", this.onUpdate);
4038 this.store.un("clear", this.refresh);
4039 this.store.un("beforeload", this.onBeforeLoad);
4040 this.store.un("load", this.onLoad);
4041 this.store.un("loadexception", this.onLoad);
4045 store.on("datachanged", this.refresh, this);
4046 store.on("add", this.onAdd, this);
4047 store.on("remove", this.onRemove, this);
4048 store.on("update", this.onUpdate, this);
4049 store.on("clear", this.refresh, this);
4050 store.on("beforeload", this.onBeforeLoad, this);
4051 store.on("load", this.onLoad, this);
4052 store.on("loadexception", this.onLoad, this);
4060 * onbeforeLoad - masks the loading area.
4063 onBeforeLoad : function(store,opts)
4065 //Roo.log('onBeforeLoad');
4069 this.el.mask(this.mask ? this.mask : "Loading" );
4071 onLoad : function ()
4078 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4079 * @param {HTMLElement} node
4080 * @return {HTMLElement} The template node
4082 findItemFromChild : function(node){
4083 var el = this.dataName ?
4084 this.el.child('.roo-tpl-' + this.dataName,true) :
4087 if(!node || node.parentNode == el){
4090 var p = node.parentNode;
4091 while(p && p != el){
4092 if(p.parentNode == el){
4101 onClick : function(e){
4102 var item = this.findItemFromChild(e.getTarget());
4104 var index = this.indexOf(item);
4105 if(this.onItemClick(item, index, e) !== false){
4106 this.fireEvent("click", this, index, item, e);
4109 this.clearSelections();
4114 onContextMenu : function(e){
4115 var item = this.findItemFromChild(e.getTarget());
4117 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4122 onDblClick : function(e){
4123 var item = this.findItemFromChild(e.getTarget());
4125 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4129 onItemClick : function(item, index, e)
4131 if(this.fireEvent("beforeclick", this, index, item, e) === false){
4134 if (this.toggleSelect) {
4135 var m = this.isSelected(item) ? 'unselect' : 'select';
4138 _t[m](item, true, false);
4141 if(this.multiSelect || this.singleSelect){
4142 if(this.multiSelect && e.shiftKey && this.lastSelection){
4143 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4145 this.select(item, this.multiSelect && e.ctrlKey);
4146 this.lastSelection = item;
4158 * Get the number of selected nodes.
4161 getSelectionCount : function(){
4162 return this.selections.length;
4166 * Get the currently selected nodes.
4167 * @return {Array} An array of HTMLElements
4169 getSelectedNodes : function(){
4170 return this.selections;
4174 * Get the indexes of the selected nodes.
4177 getSelectedIndexes : function(){
4178 var indexes = [], s = this.selections;
4179 for(var i = 0, len = s.length; i < len; i++){
4180 indexes.push(s[i].nodeIndex);
4186 * Clear all selections
4187 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4189 clearSelections : function(suppressEvent){
4190 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4191 this.cmp.elements = this.selections;
4192 this.cmp.removeClass(this.selectedClass);
4193 this.selections = [];
4195 this.fireEvent("selectionchange", this, this.selections);
4201 * Returns true if the passed node is selected
4202 * @param {HTMLElement/Number} node The node or node index
4205 isSelected : function(node){
4206 var s = this.selections;
4210 node = this.getNode(node);
4211 return s.indexOf(node) !== -1;
4216 * @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
4217 * @param {Boolean} keepExisting (optional) true to keep existing selections
4218 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4220 select : function(nodeInfo, keepExisting, suppressEvent){
4221 if(nodeInfo instanceof Array){
4223 this.clearSelections(true);
4225 for(var i = 0, len = nodeInfo.length; i < len; i++){
4226 this.select(nodeInfo[i], true, true);
4230 var node = this.getNode(nodeInfo);
4231 if(!node || this.isSelected(node)){
4232 return; // already selected.
4235 this.clearSelections(true);
4238 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4239 Roo.fly(node).addClass(this.selectedClass);
4240 this.selections.push(node);
4242 this.fireEvent("selectionchange", this, this.selections);
4250 * @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
4251 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4252 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4254 unselect : function(nodeInfo, keepExisting, suppressEvent)
4256 if(nodeInfo instanceof Array){
4257 Roo.each(this.selections, function(s) {
4258 this.unselect(s, nodeInfo);
4262 var node = this.getNode(nodeInfo);
4263 if(!node || !this.isSelected(node)){
4264 //Roo.log("not selected");
4265 return; // not selected.
4269 Roo.each(this.selections, function(s) {
4271 Roo.fly(node).removeClass(this.selectedClass);
4278 this.selections= ns;
4279 this.fireEvent("selectionchange", this, this.selections);
4283 * Gets a template node.
4284 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4285 * @return {HTMLElement} The node or null if it wasn't found
4287 getNode : function(nodeInfo){
4288 if(typeof nodeInfo == "string"){
4289 return document.getElementById(nodeInfo);
4290 }else if(typeof nodeInfo == "number"){
4291 return this.nodes[nodeInfo];
4297 * Gets a range template nodes.
4298 * @param {Number} startIndex
4299 * @param {Number} endIndex
4300 * @return {Array} An array of nodes
4302 getNodes : function(start, end){
4303 var ns = this.nodes;
4305 end = typeof end == "undefined" ? ns.length - 1 : end;
4308 for(var i = start; i <= end; i++){
4312 for(var i = start; i >= end; i--){
4320 * Finds the index of the passed node
4321 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4322 * @return {Number} The index of the node or -1
4324 indexOf : function(node){
4325 node = this.getNode(node);
4326 if(typeof node.nodeIndex == "number"){
4327 return node.nodeIndex;
4329 var ns = this.nodes;
4330 for(var i = 0, len = ns.length; i < len; i++){
4340 * Ext JS Library 1.1.1
4341 * Copyright(c) 2006-2007, Ext JS, LLC.
4343 * Originally Released Under LGPL - original licence link has changed is not relivant.
4346 * <script type="text/javascript">
4350 * @class Roo.JsonView
4352 * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4354 var view = new Roo.JsonView({
4355 container: "my-element",
4356 tpl: '<div id="{id}">{foo} - {bar}</div>', // auto create template
4361 // listen for node click?
4362 view.on("click", function(vw, index, node, e){
4363 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4366 // direct load of JSON data
4367 view.load("foobar.php");
4369 // Example from my blog list
4370 var tpl = new Roo.Template(
4371 '<div class="entry">' +
4372 '<a class="entry-title" href="{link}">{title}</a>' +
4373 "<h4>{date} by {author} | {comments} Comments</h4>{description}" +
4374 "</div><hr />"
4377 var moreView = new Roo.JsonView({
4378 container : "entry-list",
4382 moreView.on("beforerender", this.sortEntries, this);
4384 url: "/blog/get-posts.php",
4385 params: "allposts=true",
4386 text: "Loading Blog Entries..."
4390 * Note: old code is supported with arguments : (container, template, config)
4394 * Create a new JsonView
4396 * @param {Object} config The config object
4399 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4402 Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4404 var um = this.el.getUpdateManager();
4405 um.setRenderer(this);
4406 um.on("update", this.onLoad, this);
4407 um.on("failure", this.onLoadException, this);
4410 * @event beforerender
4411 * Fires before rendering of the downloaded JSON data.
4412 * @param {Roo.JsonView} this
4413 * @param {Object} data The JSON data loaded
4417 * Fires when data is loaded.
4418 * @param {Roo.JsonView} this
4419 * @param {Object} data The JSON data loaded
4420 * @param {Object} response The raw Connect response object
4423 * @event loadexception
4424 * Fires when loading fails.
4425 * @param {Roo.JsonView} this
4426 * @param {Object} response The raw Connect response object
4429 'beforerender' : true,
4431 'loadexception' : true
4434 Roo.extend(Roo.JsonView, Roo.View, {
4436 * @type {String} The root property in the loaded JSON object that contains the data
4441 * Refreshes the view.
4443 refresh : function(){
4444 this.clearSelections();
4447 var o = this.jsonData;
4448 if(o && o.length > 0){
4449 for(var i = 0, len = o.length; i < len; i++){
4450 var data = this.prepareData(o[i], i, o);
4451 html[html.length] = this.tpl.apply(data);
4454 html.push(this.emptyText);
4456 this.el.update(html.join(""));
4457 this.nodes = this.el.dom.childNodes;
4458 this.updateIndexes(0);
4462 * 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.
4463 * @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:
4466 url: "your-url.php",
4467 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4468 callback: yourFunction,
4469 scope: yourObject, //(optional scope)
4477 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4478 * 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.
4479 * @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}
4480 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4481 * @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.
4484 var um = this.el.getUpdateManager();
4485 um.update.apply(um, arguments);
4488 // note - render is a standard framework call...
4489 // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4490 render : function(el, response){
4492 this.clearSelections();
4496 if (response != '') {
4497 o = Roo.util.JSON.decode(response.responseText);
4500 o = o[this.jsonRoot];
4506 * The current JSON data or null
4509 this.beforeRender();
4514 * Get the number of records in the current JSON dataset
4517 getCount : function(){
4518 return this.jsonData ? this.jsonData.length : 0;
4522 * Returns the JSON object for the specified node(s)
4523 * @param {HTMLElement/Array} node The node or an array of nodes
4524 * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4525 * you get the JSON object for the node
4527 getNodeData : function(node){
4528 if(node instanceof Array){
4530 for(var i = 0, len = node.length; i < len; i++){
4531 data.push(this.getNodeData(node[i]));
4535 return this.jsonData[this.indexOf(node)] || null;
4538 beforeRender : function(){
4539 this.snapshot = this.jsonData;
4541 this.sort.apply(this, this.sortInfo);
4543 this.fireEvent("beforerender", this, this.jsonData);
4546 onLoad : function(el, o){
4547 this.fireEvent("load", this, this.jsonData, o);
4550 onLoadException : function(el, o){
4551 this.fireEvent("loadexception", this, o);
4555 * Filter the data by a specific property.
4556 * @param {String} property A property on your JSON objects
4557 * @param {String/RegExp} value Either string that the property values
4558 * should start with, or a RegExp to test against the property
4560 filter : function(property, value){
4563 var ss = this.snapshot;
4564 if(typeof value == "string"){
4565 var vlen = value.length;
4570 value = value.toLowerCase();
4571 for(var i = 0, len = ss.length; i < len; i++){
4573 if(o[property].substr(0, vlen).toLowerCase() == value){
4577 } else if(value.exec){ // regex?
4578 for(var i = 0, len = ss.length; i < len; i++){
4580 if(value.test(o[property])){
4587 this.jsonData = data;
4593 * Filter by a function. The passed function will be called with each
4594 * object in the current dataset. If the function returns true the value is kept,
4595 * otherwise it is filtered.
4596 * @param {Function} fn
4597 * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4599 filterBy : function(fn, scope){
4602 var ss = this.snapshot;
4603 for(var i = 0, len = ss.length; i < len; i++){
4605 if(fn.call(scope || this, o)){
4609 this.jsonData = data;
4615 * Clears the current filter.
4617 clearFilter : function(){
4618 if(this.snapshot && this.jsonData != this.snapshot){
4619 this.jsonData = this.snapshot;
4626 * Sorts the data for this view and refreshes it.
4627 * @param {String} property A property on your JSON objects to sort on
4628 * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4629 * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4631 sort : function(property, dir, sortType){
4632 this.sortInfo = Array.prototype.slice.call(arguments, 0);
4635 var dsc = dir && dir.toLowerCase() == "desc";
4636 var f = function(o1, o2){
4637 var v1 = sortType ? sortType(o1[p]) : o1[p];
4638 var v2 = sortType ? sortType(o2[p]) : o2[p];
4641 return dsc ? +1 : -1;
4643 return dsc ? -1 : +1;
4648 this.jsonData.sort(f);
4650 if(this.jsonData != this.snapshot){
4651 this.snapshot.sort(f);
4657 * Ext JS Library 1.1.1
4658 * Copyright(c) 2006-2007, Ext JS, LLC.
4660 * Originally Released Under LGPL - original licence link has changed is not relivant.
4663 * <script type="text/javascript">
4668 * @class Roo.ColorPalette
4669 * @extends Roo.Component
4670 * Simple color palette class for choosing colors. The palette can be rendered to any container.<br />
4671 * Here's an example of typical usage:
4673 var cp = new Roo.ColorPalette({value:'993300'}); // initial selected color
4674 cp.render('my-div');
4676 cp.on('select', function(palette, selColor){
4677 // do something with selColor
4681 * Create a new ColorPalette
4682 * @param {Object} config The config object
4684 Roo.ColorPalette = function(config){
4685 Roo.ColorPalette.superclass.constructor.call(this, config);
4689 * Fires when a color is selected
4690 * @param {ColorPalette} this
4691 * @param {String} color The 6-digit color hex code (without the # symbol)
4697 this.on("select", this.handler, this.scope, true);
4700 Roo.extend(Roo.ColorPalette, Roo.Component, {
4702 * @cfg {String} itemCls
4703 * The CSS class to apply to the containing element (defaults to "x-color-palette")
4705 itemCls : "x-color-palette",
4707 * @cfg {String} value
4708 * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol). Note that
4709 * the hex codes are case-sensitive.
4714 ctype: "Roo.ColorPalette",
4717 * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4719 allowReselect : false,
4722 * <p>An array of 6-digit color hex code strings (without the # symbol). This array can contain any number
4723 * of colors, and each hex code should be unique. The width of the palette is controlled via CSS by adjusting
4724 * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4725 * of colors with the width setting until the box is symmetrical.</p>
4726 * <p>You can override individual colors if needed:</p>
4728 var cp = new Roo.ColorPalette();
4729 cp.colors[0] = "FF0000"; // change the first box to red
4732 Or you can provide a custom array of your own for complete control:
4734 var cp = new Roo.ColorPalette();
4735 cp.colors = ["000000", "993300", "333300"];
4740 "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4741 "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4742 "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4743 "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4744 "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4748 onRender : function(container, position){
4749 var t = new Roo.MasterTemplate(
4750 '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on"> </span></em></a></tpl>'
4752 var c = this.colors;
4753 for(var i = 0, len = c.length; i < len; i++){
4756 var el = document.createElement("div");
4757 el.className = this.itemCls;
4759 container.dom.insertBefore(el, position);
4760 this.el = Roo.get(el);
4761 this.el.on(this.clickEvent, this.handleClick, this, {delegate: "a"});
4762 if(this.clickEvent != 'click'){
4763 this.el.on('click', Roo.emptyFn, this, {delegate: "a", preventDefault:true});
4768 afterRender : function(){
4769 Roo.ColorPalette.superclass.afterRender.call(this);
4778 handleClick : function(e, t){
4781 var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4782 this.select(c.toUpperCase());
4787 * Selects the specified color in the palette (fires the select event)
4788 * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4790 select : function(color){
4791 color = color.replace("#", "");
4792 if(color != this.value || this.allowReselect){
4795 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4797 el.child("a.color-"+color).addClass("x-color-palette-sel");
4799 this.fireEvent("select", this, color);
4804 * Ext JS Library 1.1.1
4805 * Copyright(c) 2006-2007, Ext JS, LLC.
4807 * Originally Released Under LGPL - original licence link has changed is not relivant.
4810 * <script type="text/javascript">
4814 * @class Roo.DatePicker
4815 * @extends Roo.Component
4816 * Simple date picker class.
4818 * Create a new DatePicker
4819 * @param {Object} config The config object
4821 Roo.DatePicker = function(config){
4822 Roo.DatePicker.superclass.constructor.call(this, config);
4824 this.value = config && config.value ?
4825 config.value.clearTime() : new Date().clearTime();
4830 * Fires when a date is selected
4831 * @param {DatePicker} this
4832 * @param {Date} date The selected date
4836 * @event monthchange
4837 * Fires when the displayed month changes
4838 * @param {DatePicker} this
4839 * @param {Date} date The selected month
4845 this.on("select", this.handler, this.scope || this);
4847 // build the disabledDatesRE
4848 if(!this.disabledDatesRE && this.disabledDates){
4849 var dd = this.disabledDates;
4851 for(var i = 0; i < dd.length; i++){
4853 if(i != dd.length-1) {
4857 this.disabledDatesRE = new RegExp(re + ")");
4861 Roo.extend(Roo.DatePicker, Roo.Component, {
4863 * @cfg {String} todayText
4864 * The text to display on the button that selects the current date (defaults to "Today")
4866 todayText : "Today",
4868 * @cfg {String} okText
4869 * The text to display on the ok button
4871 okText : " OK ", //   to give the user extra clicking room
4873 * @cfg {String} cancelText
4874 * The text to display on the cancel button
4876 cancelText : "Cancel",
4878 * @cfg {String} todayTip
4879 * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4881 todayTip : "{0} (Spacebar)",
4883 * @cfg {Date} minDate
4884 * Minimum allowable date (JavaScript date object, defaults to null)
4888 * @cfg {Date} maxDate
4889 * Maximum allowable date (JavaScript date object, defaults to null)
4893 * @cfg {String} minText
4894 * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4896 minText : "This date is before the minimum date",
4898 * @cfg {String} maxText
4899 * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4901 maxText : "This date is after the maximum date",
4903 * @cfg {String} format
4904 * The default date format string which can be overriden for localization support. The format must be
4905 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4909 * @cfg {Array} disabledDays
4910 * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4912 disabledDays : null,
4914 * @cfg {String} disabledDaysText
4915 * The tooltip to display when the date falls on a disabled day (defaults to "")
4917 disabledDaysText : "",
4919 * @cfg {RegExp} disabledDatesRE
4920 * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4922 disabledDatesRE : null,
4924 * @cfg {String} disabledDatesText
4925 * The tooltip text to display when the date falls on a disabled date (defaults to "")
4927 disabledDatesText : "",
4929 * @cfg {Boolean} constrainToViewport
4930 * True to constrain the date picker to the viewport (defaults to true)
4932 constrainToViewport : true,
4934 * @cfg {Array} monthNames
4935 * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4937 monthNames : Date.monthNames,
4939 * @cfg {Array} dayNames
4940 * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4942 dayNames : Date.dayNames,
4944 * @cfg {String} nextText
4945 * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4947 nextText: 'Next Month (Control+Right)',
4949 * @cfg {String} prevText
4950 * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4952 prevText: 'Previous Month (Control+Left)',
4954 * @cfg {String} monthYearText
4955 * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4957 monthYearText: 'Choose a month (Control+Up/Down to move years)',
4959 * @cfg {Number} startDay
4960 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4964 * @cfg {Bool} showClear
4965 * Show a clear button (usefull for date form elements that can be blank.)
4971 * Sets the value of the date field
4972 * @param {Date} value The date to set
4974 setValue : function(value){
4975 var old = this.value;
4977 if (typeof(value) == 'string') {
4979 value = Date.parseDate(value, this.format);
4985 this.value = value.clearTime(true);
4987 this.update(this.value);
4992 * Gets the current selected value of the date field
4993 * @return {Date} The selected date
4995 getValue : function(){
5002 this.update(this.activeDate);
5007 onRender : function(container, position){
5010 '<table cellspacing="0">',
5011 '<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>',
5012 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5013 var dn = this.dayNames;
5014 for(var i = 0; i < 7; i++){
5015 var d = this.startDay+i;
5019 m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5021 m[m.length] = "</tr></thead><tbody><tr>";
5022 for(var i = 0; i < 42; i++) {
5023 if(i % 7 == 0 && i != 0){
5024 m[m.length] = "</tr><tr>";
5026 m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5028 m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5029 '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5031 var el = document.createElement("div");
5032 el.className = "x-date-picker";
5033 el.innerHTML = m.join("");
5035 container.dom.insertBefore(el, position);
5037 this.el = Roo.get(el);
5038 this.eventEl = Roo.get(el.firstChild);
5040 new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5041 handler: this.showPrevMonth,
5043 preventDefault:true,
5047 new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5048 handler: this.showNextMonth,
5050 preventDefault:true,
5054 this.eventEl.on("mousewheel", this.handleMouseWheel, this);
5056 this.monthPicker = this.el.down('div.x-date-mp');
5057 this.monthPicker.enableDisplayMode('block');
5059 var kn = new Roo.KeyNav(this.eventEl, {
5060 "left" : function(e){
5062 this.showPrevMonth() :
5063 this.update(this.activeDate.add("d", -1));
5066 "right" : function(e){
5068 this.showNextMonth() :
5069 this.update(this.activeDate.add("d", 1));
5074 this.showNextYear() :
5075 this.update(this.activeDate.add("d", -7));
5078 "down" : function(e){
5080 this.showPrevYear() :
5081 this.update(this.activeDate.add("d", 7));
5084 "pageUp" : function(e){
5085 this.showNextMonth();
5088 "pageDown" : function(e){
5089 this.showPrevMonth();
5092 "enter" : function(e){
5093 e.stopPropagation();
5100 this.eventEl.on("click", this.handleDateClick, this, {delegate: "a.x-date-date"});
5102 this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday, this);
5104 this.el.unselectable();
5106 this.cells = this.el.select("table.x-date-inner tbody td");
5107 this.textNodes = this.el.query("table.x-date-inner tbody span");
5109 this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5111 tooltip: this.monthYearText
5114 this.mbtn.on('click', this.showMonthPicker, this);
5115 this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5118 var today = (new Date()).dateFormat(this.format);
5120 var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5121 if (this.showClear) {
5122 baseTb.add( new Roo.Toolbar.Fill());
5125 text: String.format(this.todayText, today),
5126 tooltip: String.format(this.todayTip, today),
5127 handler: this.selectToday,
5131 //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5134 if (this.showClear) {
5136 baseTb.add( new Roo.Toolbar.Fill());
5139 cls: 'x-btn-icon x-btn-clear',
5140 handler: function() {
5142 this.fireEvent("select", this, '');
5152 this.update(this.value);
5155 createMonthPicker : function(){
5156 if(!this.monthPicker.dom.firstChild){
5157 var buf = ['<table border="0" cellspacing="0">'];
5158 for(var i = 0; i < 6; i++){
5160 '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5161 '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5163 '<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>' :
5164 '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5168 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5170 '</button><button type="button" class="x-date-mp-cancel">',
5172 '</button></td></tr>',
5175 this.monthPicker.update(buf.join(''));
5176 this.monthPicker.on('click', this.onMonthClick, this);
5177 this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5179 this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5180 this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5182 this.mpMonths.each(function(m, a, i){
5185 m.dom.xmonth = 5 + Math.round(i * .5);
5187 m.dom.xmonth = Math.round((i-1) * .5);
5193 showMonthPicker : function(){
5194 this.createMonthPicker();
5195 var size = this.el.getSize();
5196 this.monthPicker.setSize(size);
5197 this.monthPicker.child('table').setSize(size);
5199 this.mpSelMonth = (this.activeDate || this.value).getMonth();
5200 this.updateMPMonth(this.mpSelMonth);
5201 this.mpSelYear = (this.activeDate || this.value).getFullYear();
5202 this.updateMPYear(this.mpSelYear);
5204 this.monthPicker.slideIn('t', {duration:.2});
5207 updateMPYear : function(y){
5209 var ys = this.mpYears.elements;
5210 for(var i = 1; i <= 10; i++){
5211 var td = ys[i-1], y2;
5213 y2 = y + Math.round(i * .5);
5214 td.firstChild.innerHTML = y2;
5217 y2 = y - (5-Math.round(i * .5));
5218 td.firstChild.innerHTML = y2;
5221 this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5225 updateMPMonth : function(sm){
5226 this.mpMonths.each(function(m, a, i){
5227 m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5231 selectMPMonth: function(m){
5235 onMonthClick : function(e, t){
5237 var el = new Roo.Element(t), pn;
5238 if(el.is('button.x-date-mp-cancel')){
5239 this.hideMonthPicker();
5241 else if(el.is('button.x-date-mp-ok')){
5242 this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5243 this.hideMonthPicker();
5245 else if(pn = el.up('td.x-date-mp-month', 2)){
5246 this.mpMonths.removeClass('x-date-mp-sel');
5247 pn.addClass('x-date-mp-sel');
5248 this.mpSelMonth = pn.dom.xmonth;
5250 else if(pn = el.up('td.x-date-mp-year', 2)){
5251 this.mpYears.removeClass('x-date-mp-sel');
5252 pn.addClass('x-date-mp-sel');
5253 this.mpSelYear = pn.dom.xyear;
5255 else if(el.is('a.x-date-mp-prev')){
5256 this.updateMPYear(this.mpyear-10);
5258 else if(el.is('a.x-date-mp-next')){
5259 this.updateMPYear(this.mpyear+10);
5263 onMonthDblClick : function(e, t){
5265 var el = new Roo.Element(t), pn;
5266 if(pn = el.up('td.x-date-mp-month', 2)){
5267 this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5268 this.hideMonthPicker();
5270 else if(pn = el.up('td.x-date-mp-year', 2)){
5271 this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5272 this.hideMonthPicker();
5276 hideMonthPicker : function(disableAnim){
5277 if(this.monthPicker){
5278 if(disableAnim === true){
5279 this.monthPicker.hide();
5281 this.monthPicker.slideOut('t', {duration:.2});
5287 showPrevMonth : function(e){
5288 this.update(this.activeDate.add("mo", -1));
5292 showNextMonth : function(e){
5293 this.update(this.activeDate.add("mo", 1));
5297 showPrevYear : function(){
5298 this.update(this.activeDate.add("y", -1));
5302 showNextYear : function(){
5303 this.update(this.activeDate.add("y", 1));
5307 handleMouseWheel : function(e){
5308 var delta = e.getWheelDelta();
5310 this.showPrevMonth();
5312 } else if(delta < 0){
5313 this.showNextMonth();
5319 handleDateClick : function(e, t){
5321 if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5322 this.setValue(new Date(t.dateValue));
5323 this.fireEvent("select", this, this.value);
5328 selectToday : function(){
5329 this.setValue(new Date().clearTime());
5330 this.fireEvent("select", this, this.value);
5334 update : function(date)
5336 var vd = this.activeDate;
5337 this.activeDate = date;
5339 var t = date.getTime();
5340 if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5341 this.cells.removeClass("x-date-selected");
5342 this.cells.each(function(c){
5343 if(c.dom.firstChild.dateValue == t){
5344 c.addClass("x-date-selected");
5345 setTimeout(function(){
5346 try{c.dom.firstChild.focus();}catch(e){}
5355 var days = date.getDaysInMonth();
5356 var firstOfMonth = date.getFirstDateOfMonth();
5357 var startingPos = firstOfMonth.getDay()-this.startDay;
5359 if(startingPos <= this.startDay){
5363 var pm = date.add("mo", -1);
5364 var prevStart = pm.getDaysInMonth()-startingPos;
5366 var cells = this.cells.elements;
5367 var textEls = this.textNodes;
5368 days += startingPos;
5370 // convert everything to numbers so it's fast
5372 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5373 var today = new Date().clearTime().getTime();
5374 var sel = date.clearTime().getTime();
5375 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5376 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5377 var ddMatch = this.disabledDatesRE;
5378 var ddText = this.disabledDatesText;
5379 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5380 var ddaysText = this.disabledDaysText;
5381 var format = this.format;
5383 var setCellClass = function(cal, cell){
5385 var t = d.getTime();
5386 cell.firstChild.dateValue = t;
5388 cell.className += " x-date-today";
5389 cell.title = cal.todayText;
5392 cell.className += " x-date-selected";
5393 setTimeout(function(){
5394 try{cell.firstChild.focus();}catch(e){}
5399 cell.className = " x-date-disabled";
5400 cell.title = cal.minText;
5404 cell.className = " x-date-disabled";
5405 cell.title = cal.maxText;
5409 if(ddays.indexOf(d.getDay()) != -1){
5410 cell.title = ddaysText;
5411 cell.className = " x-date-disabled";
5414 if(ddMatch && format){
5415 var fvalue = d.dateFormat(format);
5416 if(ddMatch.test(fvalue)){
5417 cell.title = ddText.replace("%0", fvalue);
5418 cell.className = " x-date-disabled";
5424 for(; i < startingPos; i++) {
5425 textEls[i].innerHTML = (++prevStart);
5426 d.setDate(d.getDate()+1);
5427 cells[i].className = "x-date-prevday";
5428 setCellClass(this, cells[i]);
5430 for(; i < days; i++){
5431 intDay = i - startingPos + 1;
5432 textEls[i].innerHTML = (intDay);
5433 d.setDate(d.getDate()+1);
5434 cells[i].className = "x-date-active";
5435 setCellClass(this, cells[i]);
5438 for(; i < 42; i++) {
5439 textEls[i].innerHTML = (++extraDays);
5440 d.setDate(d.getDate()+1);
5441 cells[i].className = "x-date-nextday";
5442 setCellClass(this, cells[i]);
5445 this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5446 this.fireEvent('monthchange', this, date);
5448 if(!this.internalRender){
5449 var main = this.el.dom.firstChild;
5450 var w = main.offsetWidth;
5451 this.el.setWidth(w + this.el.getBorderWidth("lr"));
5452 Roo.fly(main).setWidth(w);
5453 this.internalRender = true;
5454 // opera does not respect the auto grow header center column
5455 // then, after it gets a width opera refuses to recalculate
5456 // without a second pass
5457 if(Roo.isOpera && !this.secondPass){
5458 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5459 this.secondPass = true;
5460 this.update.defer(10, this, [date]);
5468 * Ext JS Library 1.1.1
5469 * Copyright(c) 2006-2007, Ext JS, LLC.
5471 * Originally Released Under LGPL - original licence link has changed is not relivant.
5474 * <script type="text/javascript">
5477 * @class Roo.TabPanel
5478 * @extends Roo.util.Observable
5479 * A lightweight tab container.
5483 // basic tabs 1, built from existing content
5484 var tabs = new Roo.TabPanel("tabs1");
5485 tabs.addTab("script", "View Script");
5486 tabs.addTab("markup", "View Markup");
5487 tabs.activate("script");
5489 // more advanced tabs, built from javascript
5490 var jtabs = new Roo.TabPanel("jtabs");
5491 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5493 // set up the UpdateManager
5494 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5495 var updater = tab2.getUpdateManager();
5496 updater.setDefaultUrl("ajax1.htm");
5497 tab2.on('activate', updater.refresh, updater, true);
5499 // Use setUrl for Ajax loading
5500 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5501 tab3.setUrl("ajax2.htm", null, true);
5504 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5507 jtabs.activate("jtabs-1");
5510 * Create a new TabPanel.
5511 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5512 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5514 Roo.TabPanel = function(container, config){
5516 * The container element for this TabPanel.
5519 this.el = Roo.get(container, true);
5521 if(typeof config == "boolean"){
5522 this.tabPosition = config ? "bottom" : "top";
5524 Roo.apply(this, config);
5527 if(this.tabPosition == "bottom"){
5528 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5529 this.el.addClass("x-tabs-bottom");
5531 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5532 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5533 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5535 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5537 if(this.tabPosition != "bottom"){
5538 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5541 this.bodyEl = Roo.get(this.createBody(this.el.dom));
5542 this.el.addClass("x-tabs-top");
5546 this.bodyEl.setStyle("position", "relative");
5549 this.activateDelegate = this.activate.createDelegate(this);
5554 * Fires when the active tab changes
5555 * @param {Roo.TabPanel} this
5556 * @param {Roo.TabPanelItem} activePanel The new active tab
5560 * @event beforetabchange
5561 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5562 * @param {Roo.TabPanel} this
5563 * @param {Object} e Set cancel to true on this object to cancel the tab change
5564 * @param {Roo.TabPanelItem} tab The tab being changed to
5566 "beforetabchange" : true
5569 Roo.EventManager.onWindowResize(this.onResize, this);
5570 this.cpad = this.el.getPadding("lr");
5571 this.hiddenCount = 0;
5574 // toolbar on the tabbar support...
5576 var tcfg = this.toolbar;
5577 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
5578 this.toolbar = new Roo.Toolbar(tcfg);
5580 var tbl = tcfg.container.child('table', true);
5581 tbl.setAttribute('width', '100%');
5588 Roo.TabPanel.superclass.constructor.call(this);
5591 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5593 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5595 tabPosition : "top",
5597 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5599 currentTabWidth : 0,
5601 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5605 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5609 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5611 preferredTabWidth : 175,
5613 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5617 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5619 monitorResize : true,
5621 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
5626 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5627 * @param {String} id The id of the div to use <b>or create</b>
5628 * @param {String} text The text for the tab
5629 * @param {String} content (optional) Content to put in the TabPanelItem body
5630 * @param {Boolean} closable (optional) True to create a close icon on the tab
5631 * @return {Roo.TabPanelItem} The created TabPanelItem
5633 addTab : function(id, text, content, closable){
5634 var item = new Roo.TabPanelItem(this, id, text, closable);
5635 this.addTabItem(item);
5637 item.setContent(content);
5643 * Returns the {@link Roo.TabPanelItem} with the specified id/index
5644 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5645 * @return {Roo.TabPanelItem}
5647 getTab : function(id){
5648 return this.items[id];
5652 * Hides the {@link Roo.TabPanelItem} with the specified id/index
5653 * @param {String/Number} id The id or index of the TabPanelItem to hide.
5655 hideTab : function(id){
5656 var t = this.items[id];
5660 this.autoSizeTabs();
5665 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5666 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5668 unhideTab : function(id){
5669 var t = this.items[id];
5673 this.autoSizeTabs();
5678 * Adds an existing {@link Roo.TabPanelItem}.
5679 * @param {Roo.TabPanelItem} item The TabPanelItem to add
5681 addTabItem : function(item){
5682 this.items[item.id] = item;
5683 this.items.push(item);
5684 if(this.resizeTabs){
5685 item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5686 this.autoSizeTabs();
5693 * Removes a {@link Roo.TabPanelItem}.
5694 * @param {String/Number} id The id or index of the TabPanelItem to remove.
5696 removeTab : function(id){
5697 var items = this.items;
5698 var tab = items[id];
5699 if(!tab) { return; }
5700 var index = items.indexOf(tab);
5701 if(this.active == tab && items.length > 1){
5702 var newTab = this.getNextAvailable(index);
5707 this.stripEl.dom.removeChild(tab.pnode.dom);
5708 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5709 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5711 items.splice(index, 1);
5712 delete this.items[tab.id];
5713 tab.fireEvent("close", tab);
5714 tab.purgeListeners();
5715 this.autoSizeTabs();
5718 getNextAvailable : function(start){
5719 var items = this.items;
5721 // look for a next tab that will slide over to
5722 // replace the one being removed
5723 while(index < items.length){
5724 var item = items[++index];
5725 if(item && !item.isHidden()){
5729 // if one isn't found select the previous tab (on the left)
5732 var item = items[--index];
5733 if(item && !item.isHidden()){
5741 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5742 * @param {String/Number} id The id or index of the TabPanelItem to disable.
5744 disableTab : function(id){
5745 var tab = this.items[id];
5746 if(tab && this.active != tab){
5752 * Enables a {@link Roo.TabPanelItem} that is disabled.
5753 * @param {String/Number} id The id or index of the TabPanelItem to enable.
5755 enableTab : function(id){
5756 var tab = this.items[id];
5761 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5762 * @param {String/Number} id The id or index of the TabPanelItem to activate.
5763 * @return {Roo.TabPanelItem} The TabPanelItem.
5765 activate : function(id){
5766 var tab = this.items[id];
5770 if(tab == this.active || tab.disabled){
5774 this.fireEvent("beforetabchange", this, e, tab);
5775 if(e.cancel !== true && !tab.disabled){
5779 this.active = this.items[id];
5781 this.fireEvent("tabchange", this, this.active);
5787 * Gets the active {@link Roo.TabPanelItem}.
5788 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5790 getActiveTab : function(){
5795 * Updates the tab body element to fit the height of the container element
5796 * for overflow scrolling
5797 * @param {Number} targetHeight (optional) Override the starting height from the elements height
5799 syncHeight : function(targetHeight){
5800 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5801 var bm = this.bodyEl.getMargins();
5802 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5803 this.bodyEl.setHeight(newHeight);
5807 onResize : function(){
5808 if(this.monitorResize){
5809 this.autoSizeTabs();
5814 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5816 beginUpdate : function(){
5817 this.updating = true;
5821 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5823 endUpdate : function(){
5824 this.updating = false;
5825 this.autoSizeTabs();
5829 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5831 autoSizeTabs : function(){
5832 var count = this.items.length;
5833 var vcount = count - this.hiddenCount;
5834 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5837 var w = Math.max(this.el.getWidth() - this.cpad, 10);
5838 var availWidth = Math.floor(w / vcount);
5839 var b = this.stripBody;
5840 if(b.getWidth() > w){
5841 var tabs = this.items;
5842 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5843 if(availWidth < this.minTabWidth){
5844 /*if(!this.sleft){ // incomplete scrolling code
5845 this.createScrollButtons();
5848 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5851 if(this.currentTabWidth < this.preferredTabWidth){
5852 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5858 * Returns the number of tabs in this TabPanel.
5861 getCount : function(){
5862 return this.items.length;
5866 * Resizes all the tabs to the passed width
5867 * @param {Number} The new width
5869 setTabWidth : function(width){
5870 this.currentTabWidth = width;
5871 for(var i = 0, len = this.items.length; i < len; i++) {
5872 if(!this.items[i].isHidden()) {
5873 this.items[i].setWidth(width);
5879 * Destroys this TabPanel
5880 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5882 destroy : function(removeEl){
5883 Roo.EventManager.removeResizeListener(this.onResize, this);
5884 for(var i = 0, len = this.items.length; i < len; i++){
5885 this.items[i].purgeListeners();
5887 if(removeEl === true){
5895 * @class Roo.TabPanelItem
5896 * @extends Roo.util.Observable
5897 * Represents an individual item (tab plus body) in a TabPanel.
5898 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5899 * @param {String} id The id of this TabPanelItem
5900 * @param {String} text The text for the tab of this TabPanelItem
5901 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5903 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5905 * The {@link Roo.TabPanel} this TabPanelItem belongs to
5906 * @type Roo.TabPanel
5908 this.tabPanel = tabPanel;
5910 * The id for this TabPanelItem
5915 this.disabled = false;
5919 this.loaded = false;
5920 this.closable = closable;
5923 * The body element for this TabPanelItem.
5926 this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5927 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5928 this.bodyEl.setStyle("display", "block");
5929 this.bodyEl.setStyle("zoom", "1");
5932 var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5934 this.el = Roo.get(els.el, true);
5935 this.inner = Roo.get(els.inner, true);
5936 this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5937 this.pnode = Roo.get(els.el.parentNode, true);
5938 this.el.on("mousedown", this.onTabMouseDown, this);
5939 this.el.on("click", this.onTabClick, this);
5942 var c = Roo.get(els.close, true);
5943 c.dom.title = this.closeText;
5944 c.addClassOnOver("close-over");
5945 c.on("click", this.closeClick, this);
5951 * Fires when this tab becomes the active tab.
5952 * @param {Roo.TabPanel} tabPanel The parent TabPanel
5953 * @param {Roo.TabPanelItem} this
5957 * @event beforeclose
5958 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5959 * @param {Roo.TabPanelItem} this
5960 * @param {Object} e Set cancel to true on this object to cancel the close.
5962 "beforeclose": true,
5965 * Fires when this tab is closed.
5966 * @param {Roo.TabPanelItem} this
5971 * Fires when this tab is no longer the active tab.
5972 * @param {Roo.TabPanel} tabPanel The parent TabPanel
5973 * @param {Roo.TabPanelItem} this
5977 this.hidden = false;
5979 Roo.TabPanelItem.superclass.constructor.call(this);
5982 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5983 purgeListeners : function(){
5984 Roo.util.Observable.prototype.purgeListeners.call(this);
5985 this.el.removeAllListeners();
5988 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5991 this.pnode.addClass("on");
5994 this.tabPanel.stripWrap.repaint();
5996 this.fireEvent("activate", this.tabPanel, this);
6000 * Returns true if this tab is the active tab.
6003 isActive : function(){
6004 return this.tabPanel.getActiveTab() == this;
6008 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6011 this.pnode.removeClass("on");
6013 this.fireEvent("deactivate", this.tabPanel, this);
6016 hideAction : function(){
6018 this.bodyEl.setStyle("position", "absolute");
6019 this.bodyEl.setLeft("-20000px");
6020 this.bodyEl.setTop("-20000px");
6023 showAction : function(){
6024 this.bodyEl.setStyle("position", "relative");
6025 this.bodyEl.setTop("");
6026 this.bodyEl.setLeft("");
6031 * Set the tooltip for the tab.
6032 * @param {String} tooltip The tab's tooltip
6034 setTooltip : function(text){
6035 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6036 this.textEl.dom.qtip = text;
6037 this.textEl.dom.removeAttribute('title');
6039 this.textEl.dom.title = text;
6043 onTabClick : function(e){
6045 this.tabPanel.activate(this.id);
6048 onTabMouseDown : function(e){
6050 this.tabPanel.activate(this.id);
6053 getWidth : function(){
6054 return this.inner.getWidth();
6057 setWidth : function(width){
6058 var iwidth = width - this.pnode.getPadding("lr");
6059 this.inner.setWidth(iwidth);
6060 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6061 this.pnode.setWidth(width);
6065 * Show or hide the tab
6066 * @param {Boolean} hidden True to hide or false to show.
6068 setHidden : function(hidden){
6069 this.hidden = hidden;
6070 this.pnode.setStyle("display", hidden ? "none" : "");
6074 * Returns true if this tab is "hidden"
6077 isHidden : function(){
6082 * Returns the text for this tab
6085 getText : function(){
6089 autoSize : function(){
6090 //this.el.beginMeasure();
6091 this.textEl.setWidth(1);
6093 * #2804 [new] Tabs in Roojs
6094 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6096 this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6097 //this.el.endMeasure();
6101 * Sets the text for the tab (Note: this also sets the tooltip text)
6102 * @param {String} text The tab's text and tooltip
6104 setText : function(text){
6106 this.textEl.update(text);
6107 this.setTooltip(text);
6108 if(!this.tabPanel.resizeTabs){
6113 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6115 activate : function(){
6116 this.tabPanel.activate(this.id);
6120 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6122 disable : function(){
6123 if(this.tabPanel.active != this){
6124 this.disabled = true;
6125 this.pnode.addClass("disabled");
6130 * Enables this TabPanelItem if it was previously disabled.
6132 enable : function(){
6133 this.disabled = false;
6134 this.pnode.removeClass("disabled");
6138 * Sets the content for this TabPanelItem.
6139 * @param {String} content The content
6140 * @param {Boolean} loadScripts true to look for and load scripts
6142 setContent : function(content, loadScripts){
6143 this.bodyEl.update(content, loadScripts);
6147 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6148 * @return {Roo.UpdateManager} The UpdateManager
6150 getUpdateManager : function(){
6151 return this.bodyEl.getUpdateManager();
6155 * Set a URL to be used to load the content for this TabPanelItem.
6156 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6157 * @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)
6158 * @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)
6159 * @return {Roo.UpdateManager} The UpdateManager
6161 setUrl : function(url, params, loadOnce){
6162 if(this.refreshDelegate){
6163 this.un('activate', this.refreshDelegate);
6165 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6166 this.on("activate", this.refreshDelegate);
6167 return this.bodyEl.getUpdateManager();
6171 _handleRefresh : function(url, params, loadOnce){
6172 if(!loadOnce || !this.loaded){
6173 var updater = this.bodyEl.getUpdateManager();
6174 updater.update(url, params, this._setLoaded.createDelegate(this));
6179 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
6180 * Will fail silently if the setUrl method has not been called.
6181 * This does not activate the panel, just updates its content.
6183 refresh : function(){
6184 if(this.refreshDelegate){
6185 this.loaded = false;
6186 this.refreshDelegate();
6191 _setLoaded : function(){
6196 closeClick : function(e){
6199 this.fireEvent("beforeclose", this, o);
6200 if(o.cancel !== true){
6201 this.tabPanel.removeTab(this.id);
6205 * The text displayed in the tooltip for the close icon.
6208 closeText : "Close this tab"
6212 Roo.TabPanel.prototype.createStrip = function(container){
6213 var strip = document.createElement("div");
6214 strip.className = "x-tabs-wrap";
6215 container.appendChild(strip);
6219 Roo.TabPanel.prototype.createStripList = function(strip){
6220 // div wrapper for retard IE
6221 // returns the "tr" element.
6222 strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6223 '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6224 '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6225 return strip.firstChild.firstChild.firstChild.firstChild;
6228 Roo.TabPanel.prototype.createBody = function(container){
6229 var body = document.createElement("div");
6230 Roo.id(body, "tab-body");
6231 Roo.fly(body).addClass("x-tabs-body");
6232 container.appendChild(body);
6236 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6237 var body = Roo.getDom(id);
6239 body = document.createElement("div");
6242 Roo.fly(body).addClass("x-tabs-item-body");
6243 bodyEl.insertBefore(body, bodyEl.firstChild);
6247 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6248 var td = document.createElement("td");
6249 stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6250 //stripEl.appendChild(td);
6252 td.className = "x-tabs-closable";
6254 this.closeTpl = new Roo.Template(
6255 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6256 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6257 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
6260 var el = this.closeTpl.overwrite(td, {"text": text});
6261 var close = el.getElementsByTagName("div")[0];
6262 var inner = el.getElementsByTagName("em")[0];
6263 return {"el": el, "close": close, "inner": inner};
6266 this.tabTpl = new Roo.Template(
6267 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6268 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6271 var el = this.tabTpl.overwrite(td, {"text": text});
6272 var inner = el.getElementsByTagName("em")[0];
6273 return {"el": el, "inner": inner};
6277 * Ext JS Library 1.1.1
6278 * Copyright(c) 2006-2007, Ext JS, LLC.
6280 * Originally Released Under LGPL - original licence link has changed is not relivant.
6283 * <script type="text/javascript">
6288 * @extends Roo.util.Observable
6289 * Simple Button class
6290 * @cfg {String} text The button text
6291 * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6292 * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6293 * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6294 * @cfg {Object} scope The scope of the handler
6295 * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6296 * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6297 * @cfg {Boolean} hidden True to start hidden (defaults to false)
6298 * @cfg {Boolean} disabled True to start disabled (defaults to false)
6299 * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6300 * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6301 applies if enableToggle = true)
6302 * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6303 * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6304 an {@link Roo.util.ClickRepeater} config object (defaults to false).
6306 * Create a new button
6307 * @param {Object} config The config object
6309 Roo.Button = function(renderTo, config)
6313 renderTo = config.renderTo || false;
6316 Roo.apply(this, config);
6320 * Fires when this button is clicked
6321 * @param {Button} this
6322 * @param {EventObject} e The click event
6327 * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6328 * @param {Button} this
6329 * @param {Boolean} pressed
6334 * Fires when the mouse hovers over the button
6335 * @param {Button} this
6336 * @param {Event} e The event object
6341 * Fires when the mouse exits the button
6342 * @param {Button} this
6343 * @param {Event} e The event object
6348 * Fires when the button is rendered
6349 * @param {Button} this
6354 this.menu = Roo.menu.MenuMgr.get(this.menu);
6356 // register listeners first!! - so render can be captured..
6357 Roo.util.Observable.call(this);
6359 this.render(renderTo);
6365 Roo.extend(Roo.Button, Roo.util.Observable, {
6371 * Read-only. True if this button is hidden
6376 * Read-only. True if this button is disabled
6381 * Read-only. True if this button is pressed (only if enableToggle = true)
6387 * @cfg {Number} tabIndex
6388 * The DOM tabIndex for this button (defaults to undefined)
6390 tabIndex : undefined,
6393 * @cfg {Boolean} enableToggle
6394 * True to enable pressed/not pressed toggling (defaults to false)
6396 enableToggle: false,
6398 * @cfg {Roo.menu.Menu} menu
6399 * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6403 * @cfg {String} menuAlign
6404 * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6406 menuAlign : "tl-bl?",
6409 * @cfg {String} iconCls
6410 * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6412 iconCls : undefined,
6414 * @cfg {String} type
6415 * The button's type, corresponding to the DOM input element type attribute. Either "submit," "reset" or "button" (default).
6420 menuClassTarget: 'tr',
6423 * @cfg {String} clickEvent
6424 * The type of event to map to the button's event handler (defaults to 'click')
6426 clickEvent : 'click',
6429 * @cfg {Boolean} handleMouseEvents
6430 * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6432 handleMouseEvents : true,
6435 * @cfg {String} tooltipType
6436 * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6438 tooltipType : 'qtip',
6442 * A CSS class to apply to the button's main element.
6446 * @cfg {Roo.Template} template (Optional)
6447 * An {@link Roo.Template} with which to create the Button's main element. This Template must
6448 * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6449 * require code modifications if required elements (e.g. a button) aren't present.
6453 render : function(renderTo){
6455 if(this.hideParent){
6456 this.parentEl = Roo.get(renderTo);
6460 if(!Roo.Button.buttonTemplate){
6461 // hideous table template
6462 Roo.Button.buttonTemplate = new Roo.Template(
6463 '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6464 '<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>',
6465 "</tr></tbody></table>");
6467 this.template = Roo.Button.buttonTemplate;
6469 btn = this.template.append(renderTo, [this.text || ' ', this.type], true);
6470 var btnEl = btn.child("button:first");
6471 btnEl.on('focus', this.onFocus, this);
6472 btnEl.on('blur', this.onBlur, this);
6474 btn.addClass(this.cls);
6477 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6480 btnEl.addClass(this.iconCls);
6482 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6485 if(this.tabIndex !== undefined){
6486 btnEl.dom.tabIndex = this.tabIndex;
6489 if(typeof this.tooltip == 'object'){
6490 Roo.QuickTips.tips(Roo.apply({
6494 btnEl.dom[this.tooltipType] = this.tooltip;
6498 btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6502 this.el.dom.id = this.el.id = this.id;
6505 this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6506 this.menu.on("show", this.onMenuShow, this);
6507 this.menu.on("hide", this.onMenuHide, this);
6509 btn.addClass("x-btn");
6510 if(Roo.isIE && !Roo.isIE7){
6511 this.autoWidth.defer(1, this);
6515 if(this.handleMouseEvents){
6516 btn.on("mouseover", this.onMouseOver, this);
6517 btn.on("mouseout", this.onMouseOut, this);
6518 btn.on("mousedown", this.onMouseDown, this);
6520 btn.on(this.clickEvent, this.onClick, this);
6521 //btn.on("mouseup", this.onMouseUp, this);
6528 Roo.ButtonToggleMgr.register(this);
6530 this.el.addClass("x-btn-pressed");
6533 var repeater = new Roo.util.ClickRepeater(btn,
6534 typeof this.repeat == "object" ? this.repeat : {}
6536 repeater.on("click", this.onClick, this);
6539 this.fireEvent('render', this);
6543 * Returns the button's underlying element
6544 * @return {Roo.Element} The element
6551 * Destroys this Button and removes any listeners.
6553 destroy : function(){
6554 Roo.ButtonToggleMgr.unregister(this);
6555 this.el.removeAllListeners();
6556 this.purgeListeners();
6561 autoWidth : function(){
6563 this.el.setWidth("auto");
6564 if(Roo.isIE7 && Roo.isStrict){
6565 var ib = this.el.child('button');
6566 if(ib && ib.getWidth() > 20){
6568 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6573 this.el.beginMeasure();
6575 if(this.el.getWidth() < this.minWidth){
6576 this.el.setWidth(this.minWidth);
6579 this.el.endMeasure();
6586 * Assigns this button's click handler
6587 * @param {Function} handler The function to call when the button is clicked
6588 * @param {Object} scope (optional) Scope for the function passed in
6590 setHandler : function(handler, scope){
6591 this.handler = handler;
6596 * Sets this button's text
6597 * @param {String} text The button text
6599 setText : function(text){
6602 this.el.child("td.x-btn-center button.x-btn-text").update(text);
6608 * Gets the text for this button
6609 * @return {String} The button text
6611 getText : function(){
6619 this.hidden = false;
6621 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6631 this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6636 * Convenience function for boolean show/hide
6637 * @param {Boolean} visible True to show, false to hide
6639 setVisible: function(visible){
6647 * Similar to toggle, but does not trigger event.
6648 * @param {Boolean} state [required] Force a particular state
6650 setPressed : function(state)
6652 if(state != this.pressed){
6654 this.el.addClass("x-btn-pressed");
6655 this.pressed = true;
6657 this.el.removeClass("x-btn-pressed");
6658 this.pressed = false;
6664 * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6665 * @param {Boolean} state (optional) Force a particular state
6667 toggle : function(state){
6668 state = state === undefined ? !this.pressed : state;
6669 if(state != this.pressed){
6671 this.el.addClass("x-btn-pressed");
6672 this.pressed = true;
6673 this.fireEvent("toggle", this, true);
6675 this.el.removeClass("x-btn-pressed");
6676 this.pressed = false;
6677 this.fireEvent("toggle", this, false);
6679 if(this.toggleHandler){
6680 this.toggleHandler.call(this.scope || this, this, state);
6691 this.el.child('button:first').focus();
6695 * Disable this button
6697 disable : function(){
6699 this.el.addClass("x-btn-disabled");
6701 this.disabled = true;
6705 * Enable this button
6707 enable : function(){
6709 this.el.removeClass("x-btn-disabled");
6711 this.disabled = false;
6715 * Convenience function for boolean enable/disable
6716 * @param {Boolean} enabled True to enable, false to disable
6718 setDisabled : function(v){
6719 this[v !== true ? "enable" : "disable"]();
6723 onClick : function(e)
6732 if(this.enableToggle){
6735 if(this.menu && !this.menu.isVisible()){
6736 this.menu.show(this.el, this.menuAlign);
6738 this.fireEvent("click", this, e);
6740 this.el.removeClass("x-btn-over");
6741 this.handler.call(this.scope || this, this, e);
6746 onMouseOver : function(e){
6748 this.el.addClass("x-btn-over");
6749 this.fireEvent('mouseover', this, e);
6753 onMouseOut : function(e){
6754 if(!e.within(this.el, true)){
6755 this.el.removeClass("x-btn-over");
6756 this.fireEvent('mouseout', this, e);
6760 onFocus : function(e){
6762 this.el.addClass("x-btn-focus");
6766 onBlur : function(e){
6767 this.el.removeClass("x-btn-focus");
6770 onMouseDown : function(e){
6771 if(!this.disabled && e.button == 0){
6772 this.el.addClass("x-btn-click");
6773 Roo.get(document).on('mouseup', this.onMouseUp, this);
6777 onMouseUp : function(e){
6779 this.el.removeClass("x-btn-click");
6780 Roo.get(document).un('mouseup', this.onMouseUp, this);
6784 onMenuShow : function(e){
6785 this.el.addClass("x-btn-menu-active");
6788 onMenuHide : function(e){
6789 this.el.removeClass("x-btn-menu-active");
6793 // Private utility class used by Button
6794 Roo.ButtonToggleMgr = function(){
6797 function toggleGroup(btn, state){
6799 var g = groups[btn.toggleGroup];
6800 for(var i = 0, l = g.length; i < l; i++){
6809 register : function(btn){
6810 if(!btn.toggleGroup){
6813 var g = groups[btn.toggleGroup];
6815 g = groups[btn.toggleGroup] = [];
6818 btn.on("toggle", toggleGroup);
6821 unregister : function(btn){
6822 if(!btn.toggleGroup){
6825 var g = groups[btn.toggleGroup];
6828 btn.un("toggle", toggleGroup);
6834 * Ext JS Library 1.1.1
6835 * Copyright(c) 2006-2007, Ext JS, LLC.
6837 * Originally Released Under LGPL - original licence link has changed is not relivant.
6840 * <script type="text/javascript">
6844 * @class Roo.SplitButton
6845 * @extends Roo.Button
6846 * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6847 * click event of the button. Typically this would be used to display a dropdown menu that provides additional
6848 * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6849 * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6850 * @cfg {String} arrowTooltip The title attribute of the arrow
6852 * Create a new menu button
6853 * @param {String/HTMLElement/Element} renderTo The element to append the button to
6854 * @param {Object} config The config object
6856 Roo.SplitButton = function(renderTo, config){
6857 Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6860 * Fires when this button's arrow is clicked
6861 * @param {SplitButton} this
6862 * @param {EventObject} e The click event
6864 this.addEvents({"arrowclick":true});
6867 Roo.extend(Roo.SplitButton, Roo.Button, {
6868 render : function(renderTo){
6869 // this is one sweet looking template!
6870 var tpl = new Roo.Template(
6871 '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6872 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6873 '<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>',
6874 "</tbody></table></td><td>",
6875 '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6876 '<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>',
6877 "</tbody></table></td></tr></table>"
6879 var btn = tpl.append(renderTo, [this.text, this.type], true);
6880 var btnEl = btn.child("button");
6882 btn.addClass(this.cls);
6885 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6888 btnEl.addClass(this.iconCls);
6890 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6894 if(this.handleMouseEvents){
6895 btn.on("mouseover", this.onMouseOver, this);
6896 btn.on("mouseout", this.onMouseOut, this);
6897 btn.on("mousedown", this.onMouseDown, this);
6898 btn.on("mouseup", this.onMouseUp, this);
6900 btn.on(this.clickEvent, this.onClick, this);
6902 if(typeof this.tooltip == 'object'){
6903 Roo.QuickTips.tips(Roo.apply({
6907 btnEl.dom[this.tooltipType] = this.tooltip;
6910 if(this.arrowTooltip){
6911 btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6920 this.el.addClass("x-btn-pressed");
6922 if(Roo.isIE && !Roo.isIE7){
6923 this.autoWidth.defer(1, this);
6928 this.menu.on("show", this.onMenuShow, this);
6929 this.menu.on("hide", this.onMenuHide, this);
6931 this.fireEvent('render', this);
6935 autoWidth : function(){
6937 var tbl = this.el.child("table:first");
6938 var tbl2 = this.el.child("table:last");
6939 this.el.setWidth("auto");
6940 tbl.setWidth("auto");
6941 if(Roo.isIE7 && Roo.isStrict){
6942 var ib = this.el.child('button:first');
6943 if(ib && ib.getWidth() > 20){
6945 ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6950 this.el.beginMeasure();
6952 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6953 tbl.setWidth(this.minWidth-tbl2.getWidth());
6956 this.el.endMeasure();
6959 this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6963 * Sets this button's click handler
6964 * @param {Function} handler The function to call when the button is clicked
6965 * @param {Object} scope (optional) Scope for the function passed above
6967 setHandler : function(handler, scope){
6968 this.handler = handler;
6973 * Sets this button's arrow click handler
6974 * @param {Function} handler The function to call when the arrow is clicked
6975 * @param {Object} scope (optional) Scope for the function passed above
6977 setArrowHandler : function(handler, scope){
6978 this.arrowHandler = handler;
6987 this.el.child("button:first").focus();
6992 onClick : function(e){
6995 if(e.getTarget(".x-btn-menu-arrow-wrap")){
6996 if(this.menu && !this.menu.isVisible()){
6997 this.menu.show(this.el, this.menuAlign);
6999 this.fireEvent("arrowclick", this, e);
7000 if(this.arrowHandler){
7001 this.arrowHandler.call(this.scope || this, this, e);
7004 this.fireEvent("click", this, e);
7006 this.handler.call(this.scope || this, this, e);
7012 onMouseDown : function(e){
7014 Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7018 onMouseUp : function(e){
7019 Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7025 Roo.MenuButton = Roo.SplitButton;/*
7027 * Ext JS Library 1.1.1
7028 * Copyright(c) 2006-2007, Ext JS, LLC.
7030 * Originally Released Under LGPL - original licence link has changed is not relivant.
7033 * <script type="text/javascript">
7037 * @class Roo.Toolbar
7038 * @children Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
7039 * Basic Toolbar class.
7041 * Creates a new Toolbar
7042 * @param {Object} container The config object
7044 Roo.Toolbar = function(container, buttons, config)
7046 /// old consturctor format still supported..
7047 if(container instanceof Array){ // omit the container for later rendering
7048 buttons = container;
7052 if (typeof(container) == 'object' && container.xtype) {
7054 container = config.container;
7055 buttons = config.buttons || []; // not really - use items!!
7058 if (config && config.items) {
7059 xitems = config.items;
7060 delete config.items;
7062 Roo.apply(this, config);
7063 this.buttons = buttons;
7066 this.render(container);
7068 this.xitems = xitems;
7069 Roo.each(xitems, function(b) {
7075 Roo.Toolbar.prototype = {
7077 * @cfg {Array} items
7078 * array of button configs or elements to add (will be converted to a MixedCollection)
7082 * @cfg {String/HTMLElement/Element} container
7083 * The id or element that will contain the toolbar
7086 render : function(ct){
7087 this.el = Roo.get(ct);
7089 this.el.addClass(this.cls);
7091 // using a table allows for vertical alignment
7092 // 100% width is needed by Safari...
7093 this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7094 this.tr = this.el.child("tr", true);
7096 this.items = new Roo.util.MixedCollection(false, function(o){
7097 return o.id || ("item" + (++autoId));
7100 this.add.apply(this, this.buttons);
7101 delete this.buttons;
7106 * Adds element(s) to the toolbar -- this function takes a variable number of
7107 * arguments of mixed type and adds them to the toolbar.
7108 * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7110 * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7111 * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7112 * <li>Field: Any form field (equivalent to {@link #addField})</li>
7113 * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7114 * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7115 * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7116 * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7117 * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7118 * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7120 * @param {Mixed} arg2
7121 * @param {Mixed} etc.
7124 var a = arguments, l = a.length;
7125 for(var i = 0; i < l; i++){
7130 _add : function(el) {
7133 el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7136 if (el.applyTo){ // some kind of form field
7137 return this.addField(el);
7139 if (el.render){ // some kind of Toolbar.Item
7140 return this.addItem(el);
7142 if (typeof el == "string"){ // string
7143 if(el == "separator" || el == "-"){
7144 return this.addSeparator();
7147 return this.addSpacer();
7150 return this.addFill();
7152 return this.addText(el);
7155 if(el.tagName){ // element
7156 return this.addElement(el);
7158 if(typeof el == "object"){ // must be button config?
7159 return this.addButton(el);
7167 * Add an Xtype element
7168 * @param {Object} xtype Xtype Object
7169 * @return {Object} created Object
7171 addxtype : function(e){
7176 * Returns the Element for this toolbar.
7177 * @return {Roo.Element}
7185 * @return {Roo.Toolbar.Item} The separator item
7187 addSeparator : function(){
7188 return this.addItem(new Roo.Toolbar.Separator());
7192 * Adds a spacer element
7193 * @return {Roo.Toolbar.Spacer} The spacer item
7195 addSpacer : function(){
7196 return this.addItem(new Roo.Toolbar.Spacer());
7200 * Adds a fill element that forces subsequent additions to the right side of the toolbar
7201 * @return {Roo.Toolbar.Fill} The fill item
7203 addFill : function(){
7204 return this.addItem(new Roo.Toolbar.Fill());
7208 * Adds any standard HTML element to the toolbar
7209 * @param {String/HTMLElement/Element} el The element or id of the element to add
7210 * @return {Roo.Toolbar.Item} The element's item
7212 addElement : function(el){
7213 return this.addItem(new Roo.Toolbar.Item(el));
7216 * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7217 * @type Roo.util.MixedCollection
7222 * Adds any Toolbar.Item or subclass
7223 * @param {Roo.Toolbar.Item} item
7224 * @return {Roo.Toolbar.Item} The item
7226 addItem : function(item){
7227 var td = this.nextBlock();
7229 this.items.add(item);
7234 * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7235 * @param {Object/Array} config A button config or array of configs
7236 * @return {Roo.Toolbar.Button/Array}
7238 addButton : function(config){
7239 if(config instanceof Array){
7241 for(var i = 0, len = config.length; i < len; i++) {
7242 buttons.push(this.addButton(config[i]));
7247 if(!(config instanceof Roo.Toolbar.Button)){
7249 new Roo.Toolbar.SplitButton(config) :
7250 new Roo.Toolbar.Button(config);
7252 var td = this.nextBlock();
7259 * Adds text to the toolbar
7260 * @param {String} text The text to add
7261 * @return {Roo.Toolbar.Item} The element's item
7263 addText : function(text){
7264 return this.addItem(new Roo.Toolbar.TextItem(text));
7268 * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7269 * @param {Number} index The index where the item is to be inserted
7270 * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7271 * @return {Roo.Toolbar.Button/Item}
7273 insertButton : function(index, item){
7274 if(item instanceof Array){
7276 for(var i = 0, len = item.length; i < len; i++) {
7277 buttons.push(this.insertButton(index + i, item[i]));
7281 if (!(item instanceof Roo.Toolbar.Button)){
7282 item = new Roo.Toolbar.Button(item);
7284 var td = document.createElement("td");
7285 this.tr.insertBefore(td, this.tr.childNodes[index]);
7287 this.items.insert(index, item);
7292 * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7293 * @param {Object} config
7294 * @return {Roo.Toolbar.Item} The element's item
7296 addDom : function(config, returnEl){
7297 var td = this.nextBlock();
7298 Roo.DomHelper.overwrite(td, config);
7299 var ti = new Roo.Toolbar.Item(td.firstChild);
7306 * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7307 * @type Roo.util.MixedCollection
7312 * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7313 * Note: the field should not have been rendered yet. For a field that has already been
7314 * rendered, use {@link #addElement}.
7315 * @param {Roo.form.Field} field
7316 * @return {Roo.ToolbarItem}
7320 addField : function(field) {
7323 this.fields = new Roo.util.MixedCollection(false, function(o){
7324 return o.id || ("item" + (++autoId));
7329 var td = this.nextBlock();
7331 var ti = new Roo.Toolbar.Item(td.firstChild);
7334 this.fields.add(field);
7345 this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7346 this.el.child('div').hide();
7354 this.el.child('div').show();
7358 nextBlock : function(){
7359 var td = document.createElement("td");
7360 this.tr.appendChild(td);
7365 destroy : function(){
7366 if(this.items){ // rendered?
7367 Roo.destroy.apply(Roo, this.items.items);
7369 if(this.fields){ // rendered?
7370 Roo.destroy.apply(Roo, this.fields.items);
7372 Roo.Element.uncache(this.el, this.tr);
7377 * @class Roo.Toolbar.Item
7378 * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7380 * Creates a new Item
7381 * @param {HTMLElement} el
7383 Roo.Toolbar.Item = function(el){
7385 if (typeof (el.xtype) != 'undefined') {
7390 this.el = Roo.getDom(el);
7391 this.id = Roo.id(this.el);
7392 this.hidden = false;
7397 * Fires when the button is rendered
7398 * @param {Button} this
7402 Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7404 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7405 //Roo.Toolbar.Item.prototype = {
7408 * Get this item's HTML Element
7409 * @return {HTMLElement}
7416 render : function(td){
7419 td.appendChild(this.el);
7421 this.fireEvent('render', this);
7425 * Removes and destroys this item.
7427 destroy : function(){
7428 this.td.parentNode.removeChild(this.td);
7435 this.hidden = false;
7436 this.td.style.display = "";
7444 this.td.style.display = "none";
7448 * Convenience function for boolean show/hide.
7449 * @param {Boolean} visible true to show/false to hide
7451 setVisible: function(visible){
7460 * Try to focus this item.
7463 Roo.fly(this.el).focus();
7467 * Disables this item.
7469 disable : function(){
7470 Roo.fly(this.td).addClass("x-item-disabled");
7471 this.disabled = true;
7472 this.el.disabled = true;
7476 * Enables this item.
7478 enable : function(){
7479 Roo.fly(this.td).removeClass("x-item-disabled");
7480 this.disabled = false;
7481 this.el.disabled = false;
7487 * @class Roo.Toolbar.Separator
7488 * @extends Roo.Toolbar.Item
7489 * A simple toolbar separator class
7491 * Creates a new Separator
7493 Roo.Toolbar.Separator = function(cfg){
7495 var s = document.createElement("span");
7496 s.className = "ytb-sep";
7501 Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7503 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7505 disable:Roo.emptyFn,
7510 * @class Roo.Toolbar.Spacer
7511 * @extends Roo.Toolbar.Item
7512 * A simple element that adds extra horizontal space to a toolbar.
7514 * Creates a new Spacer
7516 Roo.Toolbar.Spacer = function(cfg){
7517 var s = document.createElement("div");
7518 s.className = "ytb-spacer";
7522 Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7524 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7526 disable:Roo.emptyFn,
7531 * @class Roo.Toolbar.Fill
7532 * @extends Roo.Toolbar.Spacer
7533 * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7535 * Creates a new Spacer
7537 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7539 render : function(td){
7540 td.style.width = '100%';
7541 Roo.Toolbar.Fill.superclass.render.call(this, td);
7546 * @class Roo.Toolbar.TextItem
7547 * @extends Roo.Toolbar.Item
7548 * A simple class that renders text directly into a toolbar.
7550 * Creates a new TextItem
7551 * @cfg {string} text
7553 Roo.Toolbar.TextItem = function(cfg){
7554 var text = cfg || "";
7555 if (typeof(cfg) == 'object') {
7556 text = cfg.text || "";
7560 var s = document.createElement("span");
7561 s.className = "ytb-text";
7567 Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg || s);
7569 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7573 disable:Roo.emptyFn,
7579 this.hidden = false;
7580 this.el.style.display = "";
7588 this.el.style.display = "none";
7594 * @class Roo.Toolbar.Button
7595 * @extends Roo.Button
7596 * A button that renders into a toolbar.
7598 * Creates a new Button
7599 * @param {Object} config A standard {@link Roo.Button} config object
7601 Roo.Toolbar.Button = function(config){
7602 Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7604 Roo.extend(Roo.Toolbar.Button, Roo.Button,
7608 render : function(td){
7610 Roo.Toolbar.Button.superclass.render.call(this, td);
7614 * Removes and destroys this button
7616 destroy : function(){
7617 Roo.Toolbar.Button.superclass.destroy.call(this);
7618 this.td.parentNode.removeChild(this.td);
7625 this.hidden = false;
7626 this.td.style.display = "";
7634 this.td.style.display = "none";
7638 * Disables this item
7640 disable : function(){
7641 Roo.fly(this.td).addClass("x-item-disabled");
7642 this.disabled = true;
7648 enable : function(){
7649 Roo.fly(this.td).removeClass("x-item-disabled");
7650 this.disabled = false;
7654 Roo.ToolbarButton = Roo.Toolbar.Button;
7657 * @class Roo.Toolbar.SplitButton
7658 * @extends Roo.SplitButton
7659 * A menu button that renders into a toolbar.
7661 * Creates a new SplitButton
7662 * @param {Object} config A standard {@link Roo.SplitButton} config object
7664 Roo.Toolbar.SplitButton = function(config){
7665 Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7667 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7668 render : function(td){
7670 Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7674 * Removes and destroys this button
7676 destroy : function(){
7677 Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7678 this.td.parentNode.removeChild(this.td);
7685 this.hidden = false;
7686 this.td.style.display = "";
7694 this.td.style.display = "none";
7699 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7701 * Ext JS Library 1.1.1
7702 * Copyright(c) 2006-2007, Ext JS, LLC.
7704 * Originally Released Under LGPL - original licence link has changed is not relivant.
7707 * <script type="text/javascript">
7711 * @class Roo.PagingToolbar
7712 * @extends Roo.Toolbar
7713 * @children Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
7714 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7716 * Create a new PagingToolbar
7717 * @param {Object} config The config object
7719 Roo.PagingToolbar = function(el, ds, config)
7721 // old args format still supported... - xtype is prefered..
7722 if (typeof(el) == 'object' && el.xtype) {
7723 // created from xtype...
7726 el = config.container;
7730 items = config.items;
7734 Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7737 this.renderButtons(this.el);
7740 // supprot items array.
7742 Roo.each(items, function(e) {
7743 this.add(Roo.factory(e));
7748 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7751 * @cfg {String/HTMLElement/Element} container
7752 * container The id or element that will contain the toolbar
7755 * @cfg {Boolean} displayInfo
7756 * True to display the displayMsg (defaults to false)
7761 * @cfg {Number} pageSize
7762 * The number of records to display per page (defaults to 20)
7766 * @cfg {String} displayMsg
7767 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7769 displayMsg : 'Displaying {0} - {1} of {2}',
7771 * @cfg {String} emptyMsg
7772 * The message to display when no records are found (defaults to "No data to display")
7774 emptyMsg : 'No data to display',
7776 * Customizable piece of the default paging text (defaults to "Page")
7779 beforePageText : "Page",
7781 * Customizable piece of the default paging text (defaults to "of %0")
7784 afterPageText : "of {0}",
7786 * Customizable piece of the default paging text (defaults to "First Page")
7789 firstText : "First Page",
7791 * Customizable piece of the default paging text (defaults to "Previous Page")
7794 prevText : "Previous Page",
7796 * Customizable piece of the default paging text (defaults to "Next Page")
7799 nextText : "Next Page",
7801 * Customizable piece of the default paging text (defaults to "Last Page")
7804 lastText : "Last Page",
7806 * Customizable piece of the default paging text (defaults to "Refresh")
7809 refreshText : "Refresh",
7812 renderButtons : function(el){
7813 Roo.PagingToolbar.superclass.render.call(this, el);
7814 this.first = this.addButton({
7815 tooltip: this.firstText,
7816 cls: "x-btn-icon x-grid-page-first",
7818 handler: this.onClick.createDelegate(this, ["first"])
7820 this.prev = this.addButton({
7821 tooltip: this.prevText,
7822 cls: "x-btn-icon x-grid-page-prev",
7824 handler: this.onClick.createDelegate(this, ["prev"])
7826 //this.addSeparator();
7827 this.add(this.beforePageText);
7828 this.field = Roo.get(this.addDom({
7833 cls: "x-grid-page-number"
7835 this.field.on("keydown", this.onPagingKeydown, this);
7836 this.field.on("focus", function(){this.dom.select();});
7837 this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7838 this.field.setHeight(18);
7839 //this.addSeparator();
7840 this.next = this.addButton({
7841 tooltip: this.nextText,
7842 cls: "x-btn-icon x-grid-page-next",
7844 handler: this.onClick.createDelegate(this, ["next"])
7846 this.last = this.addButton({
7847 tooltip: this.lastText,
7848 cls: "x-btn-icon x-grid-page-last",
7850 handler: this.onClick.createDelegate(this, ["last"])
7852 //this.addSeparator();
7853 this.loading = this.addButton({
7854 tooltip: this.refreshText,
7855 cls: "x-btn-icon x-grid-loading",
7856 handler: this.onClick.createDelegate(this, ["refresh"])
7859 if(this.displayInfo){
7860 this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7865 updateInfo : function(){
7867 var count = this.ds.getCount();
7868 var msg = count == 0 ?
7872 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
7874 this.displayEl.update(msg);
7879 onLoad : function(ds, r, o){
7880 this.cursor = o.params ? o.params.start : 0;
7881 var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7883 this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7884 this.field.dom.value = ap;
7885 this.first.setDisabled(ap == 1);
7886 this.prev.setDisabled(ap == 1);
7887 this.next.setDisabled(ap == ps);
7888 this.last.setDisabled(ap == ps);
7889 this.loading.enable();
7894 getPageData : function(){
7895 var total = this.ds.getTotalCount();
7898 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7899 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7904 onLoadError : function(){
7905 this.loading.enable();
7909 onPagingKeydown : function(e){
7911 var d = this.getPageData();
7913 var v = this.field.dom.value, pageNum;
7914 if(!v || isNaN(pageNum = parseInt(v, 10))){
7915 this.field.dom.value = d.activePage;
7918 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7919 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7922 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))
7924 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7925 this.field.dom.value = pageNum;
7926 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7929 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7931 var v = this.field.dom.value, pageNum;
7932 var increment = (e.shiftKey) ? 10 : 1;
7933 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7936 if(!v || isNaN(pageNum = parseInt(v, 10))) {
7937 this.field.dom.value = d.activePage;
7940 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7942 this.field.dom.value = parseInt(v, 10) + increment;
7943 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7944 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7951 beforeLoad : function(){
7953 this.loading.disable();
7957 * event that occurs when you click on the navigation buttons - can be used to trigger load of a grid.
7958 * @param {String} which (first|prev|next|last|refresh) which button to press.
7962 onClick : function(which){
7966 ds.load({params:{start: 0, limit: this.pageSize}});
7969 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7972 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7975 var total = ds.getTotalCount();
7976 var extra = total % this.pageSize;
7977 var lastStart = extra ? (total - extra) : total-this.pageSize;
7978 ds.load({params:{start: lastStart, limit: this.pageSize}});
7981 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7987 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7988 * @param {Roo.data.Store} store The data store to unbind
7990 unbind : function(ds){
7991 ds.un("beforeload", this.beforeLoad, this);
7992 ds.un("load", this.onLoad, this);
7993 ds.un("loadexception", this.onLoadError, this);
7994 ds.un("remove", this.updateInfo, this);
7995 ds.un("add", this.updateInfo, this);
7996 this.ds = undefined;
8000 * Binds the paging toolbar to the specified {@link Roo.data.Store}
8001 * @param {Roo.data.Store} store The data store to bind
8003 bind : function(ds){
8004 ds.on("beforeload", this.beforeLoad, this);
8005 ds.on("load", this.onLoad, this);
8006 ds.on("loadexception", this.onLoadError, this);
8007 ds.on("remove", this.updateInfo, this);
8008 ds.on("add", this.updateInfo, this);
8013 * Ext JS Library 1.1.1
8014 * Copyright(c) 2006-2007, Ext JS, LLC.
8016 * Originally Released Under LGPL - original licence link has changed is not relivant.
8019 * <script type="text/javascript">
8023 * @class Roo.Resizable
8024 * @extends Roo.util.Observable
8025 * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8026 * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8027 * 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
8028 * the element will be wrapped for you automatically.</p>
8029 * <p>Here is the list of valid resize handles:</p>
8032 ------ -------------------
8041 'hd' horizontal drag
8044 * <p>Here's an example showing the creation of a typical Resizable:</p>
8046 var resizer = new Roo.Resizable("element-id", {
8054 resizer.on("resize", myHandler);
8056 * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8057 * resizer.east.setDisplayed(false);</p>
8058 * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8059 * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8060 * resize operation's new size (defaults to [0, 0])
8061 * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8062 * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8063 * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8064 * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8065 * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8066 * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8067 * @cfg {Number} width The width of the element in pixels (defaults to null)
8068 * @cfg {Number} height The height of the element in pixels (defaults to null)
8069 * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8070 * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8071 * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8072 * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8073 * @cfg {Boolean} multiDirectional <b>Deprecated</b>. The old style of adding multi-direction resize handles, deprecated
8074 * in favor of the handles config option (defaults to false)
8075 * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8076 * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8077 * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8078 * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8079 * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8080 * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8081 * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8082 * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8083 * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8084 * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8085 * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8087 * Create a new resizable component
8088 * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8089 * @param {Object} config configuration options
8091 Roo.Resizable = function(el, config)
8093 this.el = Roo.get(el);
8095 if(config && config.wrap){
8096 config.resizeChild = this.el;
8097 this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8098 this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8099 this.el.setStyle("overflow", "hidden");
8100 this.el.setPositioning(config.resizeChild.getPositioning());
8101 config.resizeChild.clearPositioning();
8102 if(!config.width || !config.height){
8103 var csize = config.resizeChild.getSize();
8104 this.el.setSize(csize.width, csize.height);
8106 if(config.pinned && !config.adjustments){
8107 config.adjustments = "auto";
8111 this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8112 this.proxy.unselectable();
8113 this.proxy.enableDisplayMode('block');
8115 Roo.apply(this, config);
8118 this.disableTrackOver = true;
8119 this.el.addClass("x-resizable-pinned");
8121 // if the element isn't positioned, make it relative
8122 var position = this.el.getStyle("position");
8123 if(position != "absolute" && position != "fixed"){
8124 this.el.setStyle("position", "relative");
8126 if(!this.handles){ // no handles passed, must be legacy style
8127 this.handles = 's,e,se';
8128 if(this.multiDirectional){
8129 this.handles += ',n,w';
8132 if(this.handles == "all"){
8133 this.handles = "n s e w ne nw se sw";
8135 var hs = this.handles.split(/\s*?[,;]\s*?| /);
8136 var ps = Roo.Resizable.positions;
8137 for(var i = 0, len = hs.length; i < len; i++){
8138 if(hs[i] && ps[hs[i]]){
8139 var pos = ps[hs[i]];
8140 this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8144 this.corner = this.southeast;
8146 // updateBox = the box can move..
8147 if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8148 this.updateBox = true;
8151 this.activeHandle = null;
8153 if(this.resizeChild){
8154 if(typeof this.resizeChild == "boolean"){
8155 this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8157 this.resizeChild = Roo.get(this.resizeChild, true);
8161 if(this.adjustments == "auto"){
8162 var rc = this.resizeChild;
8163 var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8164 if(rc && (hw || hn)){
8165 rc.position("relative");
8166 rc.setLeft(hw ? hw.el.getWidth() : 0);
8167 rc.setTop(hn ? hn.el.getHeight() : 0);
8169 this.adjustments = [
8170 (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8171 (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8176 this.dd = this.dynamic ?
8177 this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8178 this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8184 * @event beforeresize
8185 * Fired before resize is allowed. Set enabled to false to cancel resize.
8186 * @param {Roo.Resizable} this
8187 * @param {Roo.EventObject} e The mousedown event
8189 "beforeresize" : true,
8193 * @param {Roo.Resizable} this
8194 * @param {Number} x The new x position
8195 * @param {Number} y The new y position
8196 * @param {Number} w The new w width
8197 * @param {Number} h The new h hight
8198 * @param {Roo.EventObject} e The mouseup event
8203 * Fired after a resize.
8204 * @param {Roo.Resizable} this
8205 * @param {Number} width The new width
8206 * @param {Number} height The new height
8207 * @param {Roo.EventObject} e The mouseup event
8212 if(this.width !== null && this.height !== null){
8213 this.resizeTo(this.width, this.height);
8215 this.updateChildSize();
8218 this.el.dom.style.zoom = 1;
8220 Roo.Resizable.superclass.constructor.call(this);
8223 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8224 resizeChild : false,
8225 adjustments : [0, 0],
8235 multiDirectional : false,
8236 disableTrackOver : false,
8237 easing : 'easeOutStrong',
8239 heightIncrement : 0,
8243 preserveRatio : false,
8250 * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8252 constrainTo: undefined,
8254 * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8256 resizeRegion: undefined,
8260 * Perform a manual resize
8261 * @param {Number} width
8262 * @param {Number} height
8264 resizeTo : function(width, height){
8265 this.el.setSize(width, height);
8266 this.updateChildSize();
8267 this.fireEvent("resize", this, width, height, null);
8271 startSizing : function(e, handle){
8272 this.fireEvent("beforeresize", this, e);
8273 if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8276 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: " "});
8277 this.overlay.unselectable();
8278 this.overlay.enableDisplayMode("block");
8279 this.overlay.on("mousemove", this.onMouseMove, this);
8280 this.overlay.on("mouseup", this.onMouseUp, this);
8282 this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8284 this.resizing = true;
8285 this.startBox = this.el.getBox();
8286 this.startPoint = e.getXY();
8287 this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8288 (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8290 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8291 this.overlay.show();
8293 if(this.constrainTo) {
8294 var ct = Roo.get(this.constrainTo);
8295 this.resizeRegion = ct.getRegion().adjust(
8296 ct.getFrameWidth('t'),
8297 ct.getFrameWidth('l'),
8298 -ct.getFrameWidth('b'),
8299 -ct.getFrameWidth('r')
8303 this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8305 this.proxy.setBox(this.startBox);
8307 this.proxy.setStyle('visibility', 'visible');
8313 onMouseDown : function(handle, e){
8316 this.activeHandle = handle;
8317 this.startSizing(e, handle);
8322 onMouseUp : function(e){
8323 var size = this.resizeElement();
8324 this.resizing = false;
8326 this.overlay.hide();
8328 this.fireEvent("resize", this, size.width, size.height, e);
8332 updateChildSize : function(){
8334 if(this.resizeChild){
8336 var child = this.resizeChild;
8337 var adj = this.adjustments;
8338 if(el.dom.offsetWidth){
8339 var b = el.getSize(true);
8340 child.setSize(b.width+adj[0], b.height+adj[1]);
8342 // Second call here for IE
8343 // The first call enables instant resizing and
8344 // the second call corrects scroll bars if they
8347 setTimeout(function(){
8348 if(el.dom.offsetWidth){
8349 var b = el.getSize(true);
8350 child.setSize(b.width+adj[0], b.height+adj[1]);
8358 snap : function(value, inc, min){
8359 if(!inc || !value) {
8362 var newValue = value;
8363 var m = value % inc;
8366 newValue = value + (inc-m);
8368 newValue = value - m;
8371 return Math.max(min, newValue);
8375 resizeElement : function(){
8376 var box = this.proxy.getBox();
8378 this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8380 this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8382 this.updateChildSize();
8390 constrain : function(v, diff, m, mx){
8393 }else if(v - diff > mx){
8400 onMouseMove : function(e){
8403 try{// try catch so if something goes wrong the user doesn't get hung
8405 if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8409 //var curXY = this.startPoint;
8410 var curSize = this.curSize || this.startBox;
8411 var x = this.startBox.x, y = this.startBox.y;
8413 var w = curSize.width, h = curSize.height;
8415 var mw = this.minWidth, mh = this.minHeight;
8416 var mxw = this.maxWidth, mxh = this.maxHeight;
8417 var wi = this.widthIncrement;
8418 var hi = this.heightIncrement;
8420 var eventXY = e.getXY();
8421 var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8422 var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8424 var pos = this.activeHandle.position;
8429 w = Math.min(Math.max(mw, w), mxw);
8434 h = Math.min(Math.max(mh, h), mxh);
8439 w = Math.min(Math.max(mw, w), mxw);
8440 h = Math.min(Math.max(mh, h), mxh);
8443 diffY = this.constrain(h, diffY, mh, mxh);
8450 var adiffX = Math.abs(diffX);
8451 var sub = (adiffX % wi); // how much
8452 if (sub > (wi/2)) { // far enough to snap
8453 diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8455 // remove difference..
8456 diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8460 x = Math.max(this.minX, x);
8463 diffX = this.constrain(w, diffX, mw, mxw);
8469 w = Math.min(Math.max(mw, w), mxw);
8470 diffY = this.constrain(h, diffY, mh, mxh);
8475 diffX = this.constrain(w, diffX, mw, mxw);
8476 diffY = this.constrain(h, diffY, mh, mxh);
8483 diffX = this.constrain(w, diffX, mw, mxw);
8485 h = Math.min(Math.max(mh, h), mxh);
8491 var sw = this.snap(w, wi, mw);
8492 var sh = this.snap(h, hi, mh);
8493 if(sw != w || sh != h){
8516 if(this.preserveRatio){
8521 h = Math.min(Math.max(mh, h), mxh);
8526 w = Math.min(Math.max(mw, w), mxw);
8531 w = Math.min(Math.max(mw, w), mxw);
8537 w = Math.min(Math.max(mw, w), mxw);
8543 h = Math.min(Math.max(mh, h), mxh);
8551 h = Math.min(Math.max(mh, h), mxh);
8561 h = Math.min(Math.max(mh, h), mxh);
8569 if (pos == 'hdrag') {
8572 this.proxy.setBounds(x, y, w, h);
8574 this.resizeElement();
8578 this.fireEvent("resizing", this, x, y, w, h, e);
8582 handleOver : function(){
8584 this.el.addClass("x-resizable-over");
8589 handleOut : function(){
8591 this.el.removeClass("x-resizable-over");
8596 * Returns the element this component is bound to.
8597 * @return {Roo.Element}
8604 * Returns the resizeChild element (or null).
8605 * @return {Roo.Element}
8607 getResizeChild : function(){
8608 return this.resizeChild;
8610 groupHandler : function()
8615 * Destroys this resizable. If the element was wrapped and
8616 * removeEl is not true then the element remains.
8617 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8619 destroy : function(removeEl){
8620 this.proxy.remove();
8622 this.overlay.removeAllListeners();
8623 this.overlay.remove();
8625 var ps = Roo.Resizable.positions;
8627 if(typeof ps[k] != "function" && this[ps[k]]){
8628 var h = this[ps[k]];
8629 h.el.removeAllListeners();
8641 // hash to map config positions to true positions
8642 Roo.Resizable.positions = {
8643 n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast",
8648 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8650 // only initialize the template if resizable is used
8651 var tpl = Roo.DomHelper.createTemplate(
8652 {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8655 Roo.Resizable.Handle.prototype.tpl = tpl;
8657 this.position = pos;
8659 // show north drag fro topdra
8660 var handlepos = pos == 'hdrag' ? 'north' : pos;
8662 this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8663 if (pos == 'hdrag') {
8664 this.el.setStyle('cursor', 'pointer');
8666 this.el.unselectable();
8668 this.el.setOpacity(0);
8670 this.el.on("mousedown", this.onMouseDown, this);
8671 if(!disableTrackOver){
8672 this.el.on("mouseover", this.onMouseOver, this);
8673 this.el.on("mouseout", this.onMouseOut, this);
8678 Roo.Resizable.Handle.prototype = {
8679 afterResize : function(rz){
8684 onMouseDown : function(e){
8685 this.rz.onMouseDown(this, e);
8688 onMouseOver : function(e){
8689 this.rz.handleOver(this, e);
8692 onMouseOut : function(e){
8693 this.rz.handleOut(this, e);
8697 * Ext JS Library 1.1.1
8698 * Copyright(c) 2006-2007, Ext JS, LLC.
8700 * Originally Released Under LGPL - original licence link has changed is not relivant.
8703 * <script type="text/javascript">
8708 * @extends Roo.Component
8709 * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8711 * Create a new Editor
8712 * @param {Roo.form.Field} field The Field object (or descendant)
8713 * @param {Object} config The config object
8715 Roo.Editor = function(field, config){
8716 Roo.Editor.superclass.constructor.call(this, config);
8720 * @event beforestartedit
8721 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
8722 * false from the handler of this event.
8723 * @param {Editor} this
8724 * @param {Roo.Element} boundEl The underlying element bound to this editor
8725 * @param {Mixed} value The field value being set
8727 "beforestartedit" : true,
8730 * Fires when this editor is displayed
8731 * @param {Roo.Element} boundEl The underlying element bound to this editor
8732 * @param {Mixed} value The starting field value
8736 * @event beforecomplete
8737 * Fires after a change has been made to the field, but before the change is reflected in the underlying
8738 * field. Saving the change to the field can be canceled by returning false from the handler of this event.
8739 * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8740 * event will not fire since no edit actually occurred.
8741 * @param {Editor} this
8742 * @param {Mixed} value The current field value
8743 * @param {Mixed} startValue The original field value
8745 "beforecomplete" : true,
8748 * Fires after editing is complete and any changed value has been written to the underlying field.
8749 * @param {Editor} this
8750 * @param {Mixed} value The current field value
8751 * @param {Mixed} startValue The original field value
8756 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8757 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8758 * @param {Roo.form.Field} this
8759 * @param {Roo.EventObject} e The event object
8765 Roo.extend(Roo.Editor, Roo.Component, {
8767 * @cfg {Boolean/String} autosize
8768 * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8769 * or "height" to adopt the height only (defaults to false)
8772 * @cfg {Boolean} revertInvalid
8773 * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8774 * validation fails (defaults to true)
8777 * @cfg {Boolean} ignoreNoChange
8778 * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8779 * the value has not changed (defaults to false). Applies only to string values - edits for other data types
8780 * will never be ignored.
8783 * @cfg {Boolean} hideEl
8784 * False to keep the bound element visible while the editor is displayed (defaults to true)
8787 * @cfg {Mixed} value
8788 * The data value of the underlying field (defaults to "")
8792 * @cfg {String} alignment
8793 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8797 * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8798 * for bottom-right shadow (defaults to "frame")
8802 * @cfg {Boolean} constrain True to constrain the editor to the viewport
8806 * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8808 completeOnEnter : false,
8810 * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8812 cancelOnEsc : false,
8814 * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8819 onRender : function(ct, position){
8820 this.el = new Roo.Layer({
8821 shadow: this.shadow,
8827 constrain: this.constrain
8829 this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8830 if(this.field.msgTarget != 'title'){
8831 this.field.msgTarget = 'qtip';
8833 this.field.render(this.el);
8835 this.field.el.dom.setAttribute('autocomplete', 'off');
8837 this.field.on("specialkey", this.onSpecialKey, this);
8838 if(this.swallowKeys){
8839 this.field.el.swallowEvent(['keydown','keypress']);
8842 this.field.on("blur", this.onBlur, this);
8843 if(this.field.grow){
8844 this.field.on("autosize", this.el.sync, this.el, {delay:1});
8848 onSpecialKey : function(field, e)
8850 //Roo.log('editor onSpecialKey');
8851 if(this.completeOnEnter && e.getKey() == e.ENTER){
8853 this.completeEdit();
8856 // do not fire special key otherwise it might hide close the editor...
8857 if(e.getKey() == e.ENTER){
8860 if(this.cancelOnEsc && e.getKey() == e.ESC){
8864 this.fireEvent('specialkey', field, e);
8869 * Starts the editing process and shows the editor.
8870 * @param {String/HTMLElement/Element} el The element to edit
8871 * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8872 * to the innerHTML of el.
8874 startEdit : function(el, value){
8876 this.completeEdit();
8878 this.boundEl = Roo.get(el);
8879 var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8881 this.render(this.parentEl || document.body);
8883 if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8886 this.startValue = v;
8887 this.field.setValue(v);
8889 var sz = this.boundEl.getSize();
8890 switch(this.autoSize){
8892 this.setSize(sz.width, "");
8895 this.setSize("", sz.height);
8898 this.setSize(sz.width, sz.height);
8901 this.el.alignTo(this.boundEl, this.alignment);
8902 this.editing = true;
8904 Roo.QuickTips.disable();
8910 * Sets the height and width of this editor.
8911 * @param {Number} width The new width
8912 * @param {Number} height The new height
8914 setSize : function(w, h){
8915 this.field.setSize(w, h);
8922 * Realigns the editor to the bound field based on the current alignment config value.
8924 realign : function(){
8925 this.el.alignTo(this.boundEl, this.alignment);
8929 * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8930 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8932 completeEdit : function(remainVisible){
8936 var v = this.getValue();
8937 if(this.revertInvalid !== false && !this.field.isValid()){
8938 v = this.startValue;
8939 this.cancelEdit(true);
8941 if(String(v) === String(this.startValue) && this.ignoreNoChange){
8942 this.editing = false;
8946 if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8947 this.editing = false;
8948 if(this.updateEl && this.boundEl){
8949 this.boundEl.update(v);
8951 if(remainVisible !== true){
8954 this.fireEvent("complete", this, v, this.startValue);
8959 onShow : function(){
8961 if(this.hideEl !== false){
8962 this.boundEl.hide();
8965 if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8966 this.fixIEFocus = true;
8967 this.deferredFocus.defer(50, this);
8971 this.fireEvent("startedit", this.boundEl, this.startValue);
8974 deferredFocus : function(){
8981 * Cancels the editing process and hides the editor without persisting any changes. The field value will be
8982 * reverted to the original starting value.
8983 * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8984 * cancel (defaults to false)
8986 cancelEdit : function(remainVisible){
8988 this.setValue(this.startValue);
8989 if(remainVisible !== true){
8996 onBlur : function(){
8997 if(this.allowBlur !== true && this.editing){
8998 this.completeEdit();
9003 onHide : function(){
9005 this.completeEdit();
9009 if(this.field.collapse){
9010 this.field.collapse();
9013 if(this.hideEl !== false){
9014 this.boundEl.show();
9017 Roo.QuickTips.enable();
9022 * Sets the data value of the editor
9023 * @param {Mixed} value Any valid value supported by the underlying field
9025 setValue : function(v){
9026 this.field.setValue(v);
9030 * Gets the data value of the editor
9031 * @return {Mixed} The data value
9033 getValue : function(){
9034 return this.field.getValue();
9038 * Ext JS Library 1.1.1
9039 * Copyright(c) 2006-2007, Ext JS, LLC.
9041 * Originally Released Under LGPL - original licence link has changed is not relivant.
9044 * <script type="text/javascript">
9048 * @class Roo.BasicDialog
9049 * @extends Roo.util.Observable
9050 * @parent none builder
9051 * Lightweight Dialog Class. The code below shows the creation of a typical dialog using existing HTML markup:
9053 var dlg = new Roo.BasicDialog("my-dlg", {
9062 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9063 dlg.addButton('OK', dlg.hide, dlg); // Could call a save function instead of hiding
9064 dlg.addButton('Cancel', dlg.hide, dlg);
9067 <b>A Dialog should always be a direct child of the body element.</b>
9068 * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9069 * @cfg {String} title Default text to display in the title bar (defaults to null)
9070 * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9071 * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS). Determined by browser if unspecified.
9072 * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9073 * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9074 * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9075 * (defaults to null with no animation)
9076 * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9077 * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9078 * property for valid values (defaults to 'all')
9079 * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9080 * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9081 * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9082 * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9083 * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9084 * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9085 * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9086 * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9087 * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9088 * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9089 * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9090 * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9091 * draggable = true (defaults to false)
9092 * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9093 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9094 * shadow (defaults to false)
9095 * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9096 * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9097 * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9098 * @cfg {Array} buttons Array of buttons
9099 * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9101 * Create a new BasicDialog.
9102 * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9103 * @param {Object} config Configuration options
9105 Roo.BasicDialog = function(el, config){
9106 this.el = Roo.get(el);
9107 var dh = Roo.DomHelper;
9108 if(!this.el && config && config.autoCreate){
9109 if(typeof config.autoCreate == "object"){
9110 if(!config.autoCreate.id){
9111 config.autoCreate.id = el;
9113 this.el = dh.append(document.body,
9114 config.autoCreate, true);
9116 this.el = dh.append(document.body,
9117 {tag: "div", id: el, style:'visibility:hidden;'}, true);
9121 el.setDisplayed(true);
9122 el.hide = this.hideAction;
9124 el.addClass("x-dlg");
9126 Roo.apply(this, config);
9128 this.proxy = el.createProxy("x-dlg-proxy");
9129 this.proxy.hide = this.hideAction;
9130 this.proxy.setOpacity(.5);
9134 el.setWidth(config.width);
9137 el.setHeight(config.height);
9139 this.size = el.getSize();
9140 if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9141 this.xy = [config.x,config.y];
9143 this.xy = el.getCenterXY(true);
9145 /** The header element @type Roo.Element */
9146 this.header = el.child("> .x-dlg-hd");
9147 /** The body element @type Roo.Element */
9148 this.body = el.child("> .x-dlg-bd");
9149 /** The footer element @type Roo.Element */
9150 this.footer = el.child("> .x-dlg-ft");
9153 this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: " "}, this.body ? this.body.dom : null);
9156 this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9159 this.header.unselectable();
9161 this.header.update(this.title);
9163 // this element allows the dialog to be focused for keyboard event
9164 this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9165 this.focusEl.swallowEvent("click", true);
9167 this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9169 // wrap the body and footer for special rendering
9170 this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9172 this.bwrap.dom.appendChild(this.footer.dom);
9175 this.bg = this.el.createChild({
9176 tag: "div", cls:"x-dlg-bg",
9177 html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center"> </div></div></div>'
9179 this.centerBg = this.bg.child("div.x-dlg-bg-center");
9182 if(this.autoScroll !== false && !this.autoTabs){
9183 this.body.setStyle("overflow", "auto");
9186 this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9188 if(this.closable !== false){
9189 this.el.addClass("x-dlg-closable");
9190 this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9191 this.close.on("click", this.closeClick, this);
9192 this.close.addClassOnOver("x-dlg-close-over");
9194 if(this.collapsible !== false){
9195 this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9196 this.collapseBtn.on("click", this.collapseClick, this);
9197 this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9198 this.header.on("dblclick", this.collapseClick, this);
9200 if(this.resizable !== false){
9201 this.el.addClass("x-dlg-resizable");
9202 this.resizer = new Roo.Resizable(el, {
9203 minWidth: this.minWidth || 80,
9204 minHeight:this.minHeight || 80,
9205 handles: this.resizeHandles || "all",
9208 this.resizer.on("beforeresize", this.beforeResize, this);
9209 this.resizer.on("resize", this.onResize, this);
9211 if(this.draggable !== false){
9212 el.addClass("x-dlg-draggable");
9213 if (!this.proxyDrag) {
9214 var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9217 var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9219 dd.setHandleElId(this.header.id);
9220 dd.endDrag = this.endMove.createDelegate(this);
9221 dd.startDrag = this.startMove.createDelegate(this);
9222 dd.onDrag = this.onDrag.createDelegate(this);
9227 this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9228 this.mask.enableDisplayMode("block");
9230 this.el.addClass("x-dlg-modal");
9233 this.shadow = new Roo.Shadow({
9234 mode : typeof this.shadow == "string" ? this.shadow : "sides",
9235 offset : this.shadowOffset
9238 this.shadowOffset = 0;
9240 if(Roo.useShims && this.shim !== false){
9241 this.shim = this.el.createShim();
9242 this.shim.hide = this.hideAction;
9251 var bts= this.buttons;
9253 Roo.each(bts, function(b) {
9262 * Fires when a key is pressed
9263 * @param {Roo.BasicDialog} this
9264 * @param {Roo.EventObject} e
9269 * Fires when this dialog is moved by the user.
9270 * @param {Roo.BasicDialog} this
9271 * @param {Number} x The new page X
9272 * @param {Number} y The new page Y
9277 * Fires when this dialog is resized by the user.
9278 * @param {Roo.BasicDialog} this
9279 * @param {Number} width The new width
9280 * @param {Number} height The new height
9285 * Fires before this dialog is hidden.
9286 * @param {Roo.BasicDialog} this
9288 "beforehide" : true,
9291 * Fires when this dialog is hidden.
9292 * @param {Roo.BasicDialog} this
9297 * Fires before this dialog is shown.
9298 * @param {Roo.BasicDialog} this
9300 "beforeshow" : true,
9303 * Fires when this dialog is shown.
9304 * @param {Roo.BasicDialog} this
9308 el.on("keydown", this.onKeyDown, this);
9309 el.on("mousedown", this.toFront, this);
9310 Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9312 Roo.DialogManager.register(this);
9313 Roo.BasicDialog.superclass.constructor.call(this);
9316 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9317 shadowOffset: Roo.isIE ? 6 : 5,
9321 defaultButton: null,
9322 buttonAlign: "right",
9327 * Sets the dialog title text
9328 * @param {String} text The title text to display
9329 * @return {Roo.BasicDialog} this
9331 setTitle : function(text){
9332 this.header.update(text);
9337 closeClick : function(){
9342 collapseClick : function(){
9343 this[this.collapsed ? "expand" : "collapse"]();
9347 * Collapses the dialog to its minimized state (only the title bar is visible).
9348 * Equivalent to the user clicking the collapse dialog button.
9350 collapse : function(){
9351 if(!this.collapsed){
9352 this.collapsed = true;
9353 this.el.addClass("x-dlg-collapsed");
9354 this.restoreHeight = this.el.getHeight();
9355 this.resizeTo(this.el.getWidth(), this.header.getHeight());
9360 * Expands a collapsed dialog back to its normal state. Equivalent to the user
9361 * clicking the expand dialog button.
9363 expand : function(){
9365 this.collapsed = false;
9366 this.el.removeClass("x-dlg-collapsed");
9367 this.resizeTo(this.el.getWidth(), this.restoreHeight);
9372 * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9373 * @return {Roo.TabPanel} The tabs component
9375 initTabs : function(){
9376 var tabs = this.getTabs();
9377 while(tabs.getTab(0)){
9380 this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9382 tabs.addTab(Roo.id(dom), dom.title);
9390 beforeResize : function(){
9391 this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9395 onResize : function(){
9397 this.syncBodyHeight();
9398 this.adjustAssets();
9400 this.fireEvent("resize", this, this.size.width, this.size.height);
9404 onKeyDown : function(e){
9405 if(this.isVisible()){
9406 this.fireEvent("keydown", this, e);
9411 * Resizes the dialog.
9412 * @param {Number} width
9413 * @param {Number} height
9414 * @return {Roo.BasicDialog} this
9416 resizeTo : function(width, height){
9417 this.el.setSize(width, height);
9418 this.size = {width: width, height: height};
9419 this.syncBodyHeight();
9420 if(this.fixedcenter){
9423 if(this.isVisible()){
9425 this.adjustAssets();
9427 this.fireEvent("resize", this, width, height);
9433 * Resizes the dialog to fit the specified content size.
9434 * @param {Number} width
9435 * @param {Number} height
9436 * @return {Roo.BasicDialog} this
9438 setContentSize : function(w, h){
9439 h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9440 w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9441 //if(!this.el.isBorderBox()){
9442 h += this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9443 w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9446 h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9447 w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9449 this.resizeTo(w, h);
9454 * Adds a key listener for when this dialog is displayed. This allows you to hook in a function that will be
9455 * executed in response to a particular key being pressed while the dialog is active.
9456 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9457 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9458 * @param {Function} fn The function to call
9459 * @param {Object} scope (optional) The scope of the function
9460 * @return {Roo.BasicDialog} this
9462 addKeyListener : function(key, fn, scope){
9463 var keyCode, shift, ctrl, alt;
9464 if(typeof key == "object" && !(key instanceof Array)){
9465 keyCode = key["key"];
9466 shift = key["shift"];
9472 var handler = function(dlg, e){
9473 if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) && (!alt || e.altKey)){
9475 if(keyCode instanceof Array){
9476 for(var i = 0, len = keyCode.length; i < len; i++){
9477 if(keyCode[i] == k){
9478 fn.call(scope || window, dlg, k, e);
9484 fn.call(scope || window, dlg, k, e);
9489 this.on("keydown", handler);
9494 * Returns the TabPanel component (creates it if it doesn't exist).
9495 * Note: If you wish to simply check for the existence of tabs without creating them,
9496 * check for a null 'tabs' property.
9497 * @return {Roo.TabPanel} The tabs component
9499 getTabs : function(){
9501 this.el.addClass("x-dlg-auto-tabs");
9502 this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9503 this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9509 * Adds a button to the footer section of the dialog.
9510 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9511 * object or a valid Roo.DomHelper element config
9512 * @param {Function} handler The function called when the button is clicked
9513 * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9514 * @return {Roo.Button} The new button
9516 addButton : function(config, handler, scope){
9517 var dh = Roo.DomHelper;
9519 this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9521 if(!this.btnContainer){
9522 var tb = this.footer.createChild({
9524 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9525 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9527 this.btnContainer = tb.firstChild.firstChild.firstChild;
9532 minWidth: this.minButtonWidth,
9535 if(typeof config == "string"){
9536 bconfig.text = config;
9539 bconfig.dhconfig = config;
9541 Roo.apply(bconfig, config);
9545 if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9546 bconfig.position = Math.max(0, bconfig.position);
9547 fc = this.btnContainer.childNodes[bconfig.position];
9550 var btn = new Roo.Button(
9552 this.btnContainer.insertBefore(document.createElement("td"),fc)
9553 : this.btnContainer.appendChild(document.createElement("td")),
9554 //Roo.get(this.btnContainer).createChild( { tag: 'td'}, fc ),
9557 this.syncBodyHeight();
9560 * Array of all the buttons that have been added to this dialog via addButton
9565 this.buttons.push(btn);
9570 * Sets the default button to be focused when the dialog is displayed.
9571 * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9572 * @return {Roo.BasicDialog} this
9574 setDefaultButton : function(btn){
9575 this.defaultButton = btn;
9580 getHeaderFooterHeight : function(safe){
9583 height += this.header.getHeight();
9586 var fm = this.footer.getMargins();
9587 height += (this.footer.getHeight()+fm.top+fm.bottom);
9589 height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9590 height += this.centerBg.getPadding("tb");
9595 syncBodyHeight : function()
9597 var bd = this.body, // the text
9598 cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9600 var height = this.size.height - this.getHeaderFooterHeight(false);
9601 bd.setHeight(height-bd.getMargins("tb"));
9602 var hh = this.header.getHeight();
9603 var h = this.size.height-hh;
9606 bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9607 bw.setHeight(h-cb.getPadding("tb"));
9609 bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9610 bd.setWidth(bw.getWidth(true));
9612 this.tabs.syncHeight();
9614 this.tabs.el.repaint();
9620 * Restores the previous state of the dialog if Roo.state is configured.
9621 * @return {Roo.BasicDialog} this
9623 restoreState : function(){
9624 var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9625 if(box && box.width){
9626 this.xy = [box.x, box.y];
9627 this.resizeTo(box.width, box.height);
9633 beforeShow : function(){
9635 if(this.fixedcenter){
9636 this.xy = this.el.getCenterXY(true);
9639 Roo.get(document.body).addClass("x-body-masked");
9640 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9647 animShow : function(){
9648 var b = Roo.get(this.animateTarget).getBox();
9649 this.proxy.setSize(b.width, b.height);
9650 this.proxy.setLocation(b.x, b.y);
9652 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9653 true, .35, this.showEl.createDelegate(this));
9658 * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9659 * @return {Roo.BasicDialog} this
9661 show : function(animateTarget){
9662 if (this.fireEvent("beforeshow", this) === false){
9665 if(this.syncHeightBeforeShow){
9666 this.syncBodyHeight();
9667 }else if(this.firstShow){
9668 this.firstShow = false;
9669 this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9671 this.animateTarget = animateTarget || this.animateTarget;
9672 if(!this.el.isVisible()){
9674 if(this.animateTarget && Roo.get(this.animateTarget)){
9684 showEl : function(){
9686 this.el.setXY(this.xy);
9688 this.adjustAssets(true);
9691 // IE peekaboo bug - fix found by Dave Fenwick
9695 this.fireEvent("show", this);
9699 * Focuses the dialog. If a defaultButton is set, it will receive focus, otherwise the
9700 * dialog itself will receive focus.
9703 if(this.defaultButton){
9704 this.defaultButton.focus();
9706 this.focusEl.focus();
9711 constrainXY : function(){
9712 if(this.constraintoviewport !== false){
9715 var s = this.container.getSize();
9716 this.viewSize = [s.width, s.height];
9718 this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9721 var s = Roo.get(this.container||document).getScroll();
9723 var x = this.xy[0], y = this.xy[1];
9724 var w = this.size.width, h = this.size.height;
9725 var vw = this.viewSize[0], vh = this.viewSize[1];
9726 // only move it if it needs it
9728 // first validate right/bottom
9729 if(x + w > vw+s.left){
9733 if(y + h > vh+s.top){
9737 // then make sure top/left isn't negative
9749 if(this.isVisible()){
9750 this.el.setLocation(x, y);
9751 this.adjustAssets();
9758 onDrag : function(){
9759 if(!this.proxyDrag){
9760 this.xy = this.el.getXY();
9761 this.adjustAssets();
9766 adjustAssets : function(doShow){
9767 var x = this.xy[0], y = this.xy[1];
9768 var w = this.size.width, h = this.size.height;
9769 if(doShow === true){
9771 this.shadow.show(this.el);
9777 if(this.shadow && this.shadow.isVisible()){
9778 this.shadow.show(this.el);
9780 if(this.shim && this.shim.isVisible()){
9781 this.shim.setBounds(x, y, w, h);
9786 adjustViewport : function(w, h){
9788 w = Roo.lib.Dom.getViewWidth();
9789 h = Roo.lib.Dom.getViewHeight();
9792 this.viewSize = [w, h];
9793 if(this.modal && this.mask.isVisible()){
9794 this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9795 this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9797 if(this.isVisible()){
9803 * Destroys this dialog and all its supporting elements (including any tabs, shim,
9804 * shadow, proxy, mask, etc.) Also removes all event listeners.
9805 * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9807 destroy : function(removeEl){
9808 if(this.isVisible()){
9809 this.animateTarget = null;
9812 Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9814 this.tabs.destroy(removeEl);
9827 for(var i = 0, len = this.buttons.length; i < len; i++){
9828 this.buttons[i].destroy();
9831 this.el.removeAllListeners();
9832 if(removeEl === true){
9836 Roo.DialogManager.unregister(this);
9840 startMove : function(){
9844 if(this.constraintoviewport !== false){
9845 this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9850 endMove : function(){
9851 if(!this.proxyDrag){
9852 Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9854 Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9858 this.adjustAssets();
9860 this.fireEvent("move", this, this.xy[0], this.xy[1]);
9864 * Brings this dialog to the front of any other visible dialogs
9865 * @return {Roo.BasicDialog} this
9867 toFront : function(){
9868 Roo.DialogManager.bringToFront(this);
9873 * Sends this dialog to the back (under) of any other visible dialogs
9874 * @return {Roo.BasicDialog} this
9876 toBack : function(){
9877 Roo.DialogManager.sendToBack(this);
9882 * Centers this dialog in the viewport
9883 * @return {Roo.BasicDialog} this
9885 center : function(){
9886 var xy = this.el.getCenterXY(true);
9887 this.moveTo(xy[0], xy[1]);
9892 * Moves the dialog's top-left corner to the specified point
9895 * @return {Roo.BasicDialog} this
9897 moveTo : function(x, y){
9899 if(this.isVisible()){
9900 this.el.setXY(this.xy);
9901 this.adjustAssets();
9907 * Aligns the dialog to the specified element
9908 * @param {String/HTMLElement/Roo.Element} element The element to align to.
9909 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9910 * @param {Array} offsets (optional) Offset the positioning by [x, y]
9911 * @return {Roo.BasicDialog} this
9913 alignTo : function(element, position, offsets){
9914 this.xy = this.el.getAlignToXY(element, position, offsets);
9915 if(this.isVisible()){
9916 this.el.setXY(this.xy);
9917 this.adjustAssets();
9923 * Anchors an element to another element and realigns it when the window is resized.
9924 * @param {String/HTMLElement/Roo.Element} element The element to align to.
9925 * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9926 * @param {Array} offsets (optional) Offset the positioning by [x, y]
9927 * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9928 * is a number, it is used as the buffer delay (defaults to 50ms).
9929 * @return {Roo.BasicDialog} this
9931 anchorTo : function(el, alignment, offsets, monitorScroll){
9932 var action = function(){
9933 this.alignTo(el, alignment, offsets);
9935 Roo.EventManager.onWindowResize(action, this);
9936 var tm = typeof monitorScroll;
9937 if(tm != 'undefined'){
9938 Roo.EventManager.on(window, 'scroll', action, this,
9939 {buffer: tm == 'number' ? monitorScroll : 50});
9946 * Returns true if the dialog is visible
9949 isVisible : function(){
9950 return this.el.isVisible();
9954 animHide : function(callback){
9955 var b = Roo.get(this.animateTarget).getBox();
9957 this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9959 this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9960 this.hideEl.createDelegate(this, [callback]));
9965 * @param {Function} callback (optional) Function to call when the dialog is hidden
9966 * @return {Roo.BasicDialog} this
9968 hide : function(callback){
9969 if (this.fireEvent("beforehide", this) === false){
9978 // sometimes animateTarget seems to get set.. causing problems...
9979 // this just double checks..
9980 if(this.animateTarget && Roo.get(this.animateTarget)) {
9981 this.animHide(callback);
9984 this.hideEl(callback);
9990 hideEl : function(callback){
9994 Roo.get(document.body).removeClass("x-body-masked");
9996 this.fireEvent("hide", this);
9997 if(typeof callback == "function"){
10003 hideAction : function(){
10004 this.setLeft("-10000px");
10005 this.setTop("-10000px");
10006 this.setStyle("visibility", "hidden");
10010 refreshSize : function(){
10011 this.size = this.el.getSize();
10012 this.xy = this.el.getXY();
10013 Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10017 // z-index is managed by the DialogManager and may be overwritten at any time
10018 setZIndex : function(index){
10020 this.mask.setStyle("z-index", index);
10023 this.shim.setStyle("z-index", ++index);
10026 this.shadow.setZIndex(++index);
10028 this.el.setStyle("z-index", ++index);
10030 this.proxy.setStyle("z-index", ++index);
10033 this.resizer.proxy.setStyle("z-index", ++index);
10036 this.lastZIndex = index;
10040 * Returns the element for this dialog
10041 * @return {Roo.Element} The underlying dialog Element
10043 getEl : function(){
10049 * @class Roo.DialogManager
10050 * Provides global access to BasicDialogs that have been created and
10051 * support for z-indexing (layering) multiple open dialogs.
10053 Roo.DialogManager = function(){
10055 var accessList = [];
10059 var sortDialogs = function(d1, d2){
10060 return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10064 var orderDialogs = function(){
10065 accessList.sort(sortDialogs);
10066 var seed = Roo.DialogManager.zseed;
10067 for(var i = 0, len = accessList.length; i < len; i++){
10068 var dlg = accessList[i];
10070 dlg.setZIndex(seed + (i*10));
10077 * The starting z-index for BasicDialogs (defaults to 9000)
10078 * @type Number The z-index value
10083 register : function(dlg){
10084 list[dlg.id] = dlg;
10085 accessList.push(dlg);
10089 unregister : function(dlg){
10090 delete list[dlg.id];
10093 if(!accessList.indexOf){
10094 for( i = 0, len = accessList.length; i < len; i++){
10095 if(accessList[i] == dlg){
10096 accessList.splice(i, 1);
10101 i = accessList.indexOf(dlg);
10103 accessList.splice(i, 1);
10109 * Gets a registered dialog by id
10110 * @param {String/Object} id The id of the dialog or a dialog
10111 * @return {Roo.BasicDialog} this
10113 get : function(id){
10114 return typeof id == "object" ? id : list[id];
10118 * Brings the specified dialog to the front
10119 * @param {String/Object} dlg The id of the dialog or a dialog
10120 * @return {Roo.BasicDialog} this
10122 bringToFront : function(dlg){
10123 dlg = this.get(dlg);
10126 dlg._lastAccess = new Date().getTime();
10133 * Sends the specified dialog to the back
10134 * @param {String/Object} dlg The id of the dialog or a dialog
10135 * @return {Roo.BasicDialog} this
10137 sendToBack : function(dlg){
10138 dlg = this.get(dlg);
10139 dlg._lastAccess = -(new Date().getTime());
10145 * Hides all dialogs
10147 hideAll : function(){
10148 for(var id in list){
10149 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10158 * @class Roo.LayoutDialog
10159 * @extends Roo.BasicDialog
10160 * @children Roo.ContentPanel
10161 * @parent builder none
10162 * Dialog which provides adjustments for working with a layout in a Dialog.
10163 * Add your necessary layout config options to the dialog's config.<br>
10164 * Example usage (including a nested layout):
10167 dialog = new Roo.LayoutDialog("download-dlg", {
10176 // layout config merges with the dialog config
10178 tabPosition: "top",
10179 alwaysShowTabs: true
10182 dialog.addKeyListener(27, dialog.hide, dialog);
10183 dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10184 dialog.addButton("Build It!", this.getDownload, this);
10186 // we can even add nested layouts
10187 var innerLayout = new Roo.BorderLayout("dl-inner", {
10197 innerLayout.beginUpdate();
10198 innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10199 innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10200 innerLayout.endUpdate(true);
10202 var layout = dialog.getLayout();
10203 layout.beginUpdate();
10204 layout.add("center", new Roo.ContentPanel("standard-panel",
10205 {title: "Download the Source", fitToFrame:true}));
10206 layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10207 {title: "Build your own roo.js"}));
10208 layout.getRegion("center").showPanel(sp);
10209 layout.endUpdate();
10213 * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10214 * @param {Object} config configuration options
10216 Roo.LayoutDialog = function(el, cfg){
10219 if (typeof(cfg) == 'undefined') {
10220 config = Roo.apply({}, el);
10221 // not sure why we use documentElement here.. - it should always be body.
10222 // IE7 borks horribly if we use documentElement.
10223 // webkit also does not like documentElement - it creates a body element...
10224 el = Roo.get( document.body || document.documentElement ).createChild();
10225 //config.autoCreate = true;
10229 config.autoTabs = false;
10230 Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10231 this.body.setStyle({overflow:"hidden", position:"relative"});
10232 this.layout = new Roo.BorderLayout(this.body.dom, config);
10233 this.layout.monitorWindowResize = false;
10234 this.el.addClass("x-dlg-auto-layout");
10235 // fix case when center region overwrites center function
10236 this.center = Roo.BasicDialog.prototype.center;
10237 this.on("show", this.layout.layout, this.layout, true);
10238 if (config.items) {
10239 var xitems = config.items;
10240 delete config.items;
10241 Roo.each(xitems, this.addxtype, this);
10246 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10250 * @cfg {Roo.LayoutRegion} east
10253 * @cfg {Roo.LayoutRegion} west
10256 * @cfg {Roo.LayoutRegion} south
10259 * @cfg {Roo.LayoutRegion} north
10262 * @cfg {Roo.LayoutRegion} center
10265 * @cfg {Roo.Button} buttons[] Bottom buttons..
10270 * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10273 endUpdate : function(){
10274 this.layout.endUpdate();
10278 * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10281 beginUpdate : function(){
10282 this.layout.beginUpdate();
10286 * Get the BorderLayout for this dialog
10287 * @return {Roo.BorderLayout}
10289 getLayout : function(){
10290 return this.layout;
10293 showEl : function(){
10294 Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10296 this.layout.layout();
10301 // Use the syncHeightBeforeShow config option to control this automatically
10302 syncBodyHeight : function(){
10303 Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10304 if(this.layout){this.layout.layout();}
10308 * Add an xtype element (actually adds to the layout.)
10309 * @return {Object} xdata xtype object data.
10312 addxtype : function(c) {
10313 return this.layout.addxtype(c);
10317 * Ext JS Library 1.1.1
10318 * Copyright(c) 2006-2007, Ext JS, LLC.
10320 * Originally Released Under LGPL - original licence link has changed is not relivant.
10323 * <script type="text/javascript">
10327 * @class Roo.MessageBox
10329 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
10333 Roo.Msg.alert('Status', 'Changes saved successfully.');
10335 // Prompt for user data:
10336 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10338 // process text value...
10342 // Show a dialog using config options:
10344 title:'Save Changes?',
10345 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10346 buttons: Roo.Msg.YESNOCANCEL,
10353 Roo.MessageBox = function(){
10354 var dlg, opt, mask, waitTimer;
10355 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10356 var buttons, activeTextEl, bwidth;
10359 var handleButton = function(button){
10361 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10365 var handleHide = function(){
10366 if(opt && opt.cls){
10367 dlg.el.removeClass(opt.cls);
10370 Roo.TaskMgr.stop(waitTimer);
10376 var updateButtons = function(b){
10379 buttons["ok"].hide();
10380 buttons["cancel"].hide();
10381 buttons["yes"].hide();
10382 buttons["no"].hide();
10383 dlg.footer.dom.style.display = 'none';
10386 dlg.footer.dom.style.display = '';
10387 for(var k in buttons){
10388 if(typeof buttons[k] != "function"){
10391 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10392 width += buttons[k].el.getWidth()+15;
10402 var handleEsc = function(d, k, e){
10403 if(opt && opt.closable !== false){
10413 * Returns a reference to the underlying {@link Roo.BasicDialog} element
10414 * @return {Roo.BasicDialog} The BasicDialog element
10416 getDialog : function(){
10418 dlg = new Roo.BasicDialog("x-msg-box", {
10423 constraintoviewport:false,
10425 collapsible : false,
10428 width:400, height:100,
10429 buttonAlign:"center",
10430 closeClick : function(){
10431 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10432 handleButton("no");
10434 handleButton("cancel");
10439 dlg.on("hide", handleHide);
10441 dlg.addKeyListener(27, handleEsc);
10443 var bt = this.buttonText;
10444 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10445 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10446 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10447 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10448 bodyEl = dlg.body.createChild({
10450 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>'
10452 msgEl = bodyEl.dom.firstChild;
10453 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10454 textboxEl.enableDisplayMode();
10455 textboxEl.addKeyListener([10,13], function(){
10456 if(dlg.isVisible() && opt && opt.buttons){
10457 if(opt.buttons.ok){
10458 handleButton("ok");
10459 }else if(opt.buttons.yes){
10460 handleButton("yes");
10464 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10465 textareaEl.enableDisplayMode();
10466 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10467 progressEl.enableDisplayMode();
10468 var pf = progressEl.dom.firstChild;
10470 pp = Roo.get(pf.firstChild);
10471 pp.setHeight(pf.offsetHeight);
10479 * Updates the message box body text
10480 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10481 * the XHTML-compliant non-breaking space character '&#160;')
10482 * @return {Roo.MessageBox} This message box
10484 updateText : function(text){
10485 if(!dlg.isVisible() && !opt.width){
10486 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10488 msgEl.innerHTML = text || ' ';
10490 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10491 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10493 Math.min(opt.width || cw , this.maxWidth),
10494 Math.max(opt.minWidth || this.minWidth, bwidth)
10497 activeTextEl.setWidth(w);
10499 if(dlg.isVisible()){
10500 dlg.fixedcenter = false;
10502 // to big, make it scroll. = But as usual stupid IE does not support
10505 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10506 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10507 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10509 bodyEl.dom.style.height = '';
10510 bodyEl.dom.style.overflowY = '';
10513 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10515 bodyEl.dom.style.overflowX = '';
10518 dlg.setContentSize(w, bodyEl.getHeight());
10519 if(dlg.isVisible()){
10520 dlg.fixedcenter = true;
10526 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
10527 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10528 * @param {Number} value Any number between 0 and 1 (e.g., .5)
10529 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10530 * @return {Roo.MessageBox} This message box
10532 updateProgress : function(value, text){
10534 this.updateText(text);
10536 if (pp) { // weird bug on my firefox - for some reason this is not defined
10537 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10543 * Returns true if the message box is currently displayed
10544 * @return {Boolean} True if the message box is visible, else false
10546 isVisible : function(){
10547 return dlg && dlg.isVisible();
10551 * Hides the message box if it is displayed
10554 if(this.isVisible()){
10560 * Displays a new message box, or reinitializes an existing message box, based on the config options
10561 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10562 * The following config object properties are supported:
10564 Property Type Description
10565 ---------- --------------- ------------------------------------------------------------------------------------
10566 animEl String/Element An id or Element from which the message box should animate as it opens and
10567 closes (defaults to undefined)
10568 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10569 cancel:'Bar'}), or false to not show any buttons (defaults to false)
10570 closable Boolean False to hide the top-right close button (defaults to true). Note that
10571 progress and wait dialogs will ignore this property and always hide the
10572 close button as they can only be closed programmatically.
10573 cls String A custom CSS class to apply to the message box element
10574 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
10575 displayed (defaults to 75)
10576 fn Function A callback function to execute after closing the dialog. The arguments to the
10577 function will be btn (the name of the button that was clicked, if applicable,
10578 e.g. "ok"), and text (the value of the active text field, if applicable).
10579 Progress and wait dialogs will ignore this option since they do not respond to
10580 user actions and can only be closed programmatically, so any required function
10581 should be called by the same code after it closes the dialog.
10582 icon String A CSS class that provides a background image to be used as an icon for
10583 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10584 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
10585 minWidth Number The minimum width in pixels of the message box (defaults to 100)
10586 modal Boolean False to allow user interaction with the page while the message box is
10587 displayed (defaults to true)
10588 msg String A string that will replace the existing message box body text (defaults
10589 to the XHTML-compliant non-breaking space character ' ')
10590 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
10591 progress Boolean True to display a progress bar (defaults to false)
10592 progressText String The text to display inside the progress bar if progress = true (defaults to '')
10593 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
10594 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
10595 title String The title text
10596 value String The string value to set into the active textbox element if displayed
10597 wait Boolean True to display a progress bar (defaults to false)
10598 width Number The width of the dialog in pixels
10605 msg: 'Please enter your address:',
10607 buttons: Roo.MessageBox.OKCANCEL,
10610 animEl: 'addAddressBtn'
10613 * @param {Object} config Configuration options
10614 * @return {Roo.MessageBox} This message box
10616 show : function(options)
10619 // this causes nightmares if you show one dialog after another
10620 // especially on callbacks..
10622 if(this.isVisible()){
10625 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10626 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
10627 Roo.log("New Dialog Message:" + options.msg )
10628 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10629 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10632 var d = this.getDialog();
10634 d.setTitle(opt.title || " ");
10635 d.close.setDisplayed(opt.closable !== false);
10636 activeTextEl = textboxEl;
10637 opt.prompt = opt.prompt || (opt.multiline ? true : false);
10642 textareaEl.setHeight(typeof opt.multiline == "number" ?
10643 opt.multiline : this.defaultTextHeight);
10644 activeTextEl = textareaEl;
10653 progressEl.setDisplayed(opt.progress === true);
10654 this.updateProgress(0);
10655 activeTextEl.dom.value = opt.value || "";
10657 dlg.setDefaultButton(activeTextEl);
10659 var bs = opt.buttons;
10662 db = buttons["ok"];
10663 }else if(bs && bs.yes){
10664 db = buttons["yes"];
10666 dlg.setDefaultButton(db);
10668 bwidth = updateButtons(opt.buttons);
10669 this.updateText(opt.msg);
10671 d.el.addClass(opt.cls);
10673 d.proxyDrag = opt.proxyDrag === true;
10674 d.modal = opt.modal !== false;
10675 d.mask = opt.modal !== false ? mask : false;
10676 if(!d.isVisible()){
10677 // force it to the end of the z-index stack so it gets a cursor in FF
10678 document.body.appendChild(dlg.el.dom);
10679 d.animateTarget = null;
10680 d.show(options.animEl);
10687 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
10688 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10689 * and closing the message box when the process is complete.
10690 * @param {String} title The title bar text
10691 * @param {String} msg The message box body text
10692 * @return {Roo.MessageBox} This message box
10694 progress : function(title, msg){
10701 minWidth: this.minProgressWidth,
10708 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10709 * If a callback function is passed it will be called after the user clicks the button, and the
10710 * id of the button that was clicked will be passed as the only parameter to the callback
10711 * (could also be the top-right close button).
10712 * @param {String} title The title bar text
10713 * @param {String} msg The message box body text
10714 * @param {Function} fn (optional) The callback function invoked after the message box is closed
10715 * @param {Object} scope (optional) The scope of the callback function
10716 * @return {Roo.MessageBox} This message box
10718 alert : function(title, msg, fn, scope){
10731 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
10732 * interaction while waiting for a long-running process to complete that does not have defined intervals.
10733 * You are responsible for closing the message box when the process is complete.
10734 * @param {String} msg The message box body text
10735 * @param {String} title (optional) The title bar text
10736 * @return {Roo.MessageBox} This message box
10738 wait : function(msg, title){
10749 waitTimer = Roo.TaskMgr.start({
10751 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10759 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10760 * If a callback function is passed it will be called after the user clicks either button, and the id of the
10761 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10762 * @param {String} title The title bar text
10763 * @param {String} msg The message box body text
10764 * @param {Function} fn (optional) The callback function invoked after the message box is closed
10765 * @param {Object} scope (optional) The scope of the callback function
10766 * @return {Roo.MessageBox} This message box
10768 confirm : function(title, msg, fn, scope){
10772 buttons: this.YESNO,
10781 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10782 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
10783 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10784 * (could also be the top-right close button) and the text that was entered will be passed as the two
10785 * parameters to the callback.
10786 * @param {String} title The title bar text
10787 * @param {String} msg The message box body text
10788 * @param {Function} fn (optional) The callback function invoked after the message box is closed
10789 * @param {Object} scope (optional) The scope of the callback function
10790 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10791 * property, or the height in pixels to create the textbox (defaults to false / single-line)
10792 * @return {Roo.MessageBox} This message box
10794 prompt : function(title, msg, fn, scope, multiline){
10798 buttons: this.OKCANCEL,
10803 multiline: multiline,
10810 * Button config that displays a single OK button
10815 * Button config that displays Yes and No buttons
10818 YESNO : {yes:true, no:true},
10820 * Button config that displays OK and Cancel buttons
10823 OKCANCEL : {ok:true, cancel:true},
10825 * Button config that displays Yes, No and Cancel buttons
10828 YESNOCANCEL : {yes:true, no:true, cancel:true},
10831 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10834 defaultTextHeight : 75,
10836 * The maximum width in pixels of the message box (defaults to 600)
10841 * The minimum width in pixels of the message box (defaults to 100)
10846 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
10847 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10850 minProgressWidth : 250,
10852 * An object containing the default button text strings that can be overriden for localized language support.
10853 * Supported properties are: ok, cancel, yes and no.
10854 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10867 * Shorthand for {@link Roo.MessageBox}
10869 Roo.Msg = Roo.MessageBox;/*
10871 * Ext JS Library 1.1.1
10872 * Copyright(c) 2006-2007, Ext JS, LLC.
10874 * Originally Released Under LGPL - original licence link has changed is not relivant.
10877 * <script type="text/javascript">
10880 * @class Roo.QuickTips
10881 * Provides attractive and customizable tooltips for any element.
10884 Roo.QuickTips = function(){
10885 var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10886 var ce, bd, xy, dd;
10887 var visible = false, disabled = true, inited = false;
10888 var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10890 var onOver = function(e){
10894 var t = e.getTarget();
10895 if(!t || t.nodeType !== 1 || t == document || t == document.body){
10898 if(ce && t == ce.el){
10899 clearTimeout(hideProc);
10902 if(t && tagEls[t.id]){
10903 tagEls[t.id].el = t;
10904 showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10907 var ttp, et = Roo.fly(t);
10908 var ns = cfg.namespace;
10909 if(tm.interceptTitles && t.title){
10912 t.removeAttribute("title");
10913 e.preventDefault();
10915 ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10918 showProc = show.defer(tm.showDelay, tm, [{
10920 text: ttp.replace(/\\n/g,'<br/>'),
10921 width: et.getAttributeNS(ns, cfg.width),
10922 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10923 title: et.getAttributeNS(ns, cfg.title),
10924 cls: et.getAttributeNS(ns, cfg.cls)
10929 var onOut = function(e){
10930 clearTimeout(showProc);
10931 var t = e.getTarget();
10932 if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10933 hideProc = setTimeout(hide, tm.hideDelay);
10937 var onMove = function(e){
10943 if(tm.trackMouse && ce){
10948 var onDown = function(e){
10949 clearTimeout(showProc);
10950 clearTimeout(hideProc);
10952 if(tm.hideOnClick){
10955 tm.enable.defer(100, tm);
10960 var getPad = function(){
10961 return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10964 var show = function(o){
10968 clearTimeout(dismissProc);
10970 if(removeCls){ // in case manually hidden
10971 el.removeClass(removeCls);
10975 el.addClass(ce.cls);
10976 removeCls = ce.cls;
10979 tipTitle.update(ce.title);
10982 tipTitle.update('');
10985 el.dom.style.width = tm.maxWidth+'px';
10986 //tipBody.dom.style.width = '';
10987 tipBodyText.update(o.text);
10988 var p = getPad(), w = ce.width;
10990 var td = tipBodyText.dom;
10991 var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10992 if(aw > tm.maxWidth){
10994 }else if(aw < tm.minWidth){
11000 //tipBody.setWidth(w);
11001 el.setWidth(parseInt(w, 10) + p);
11002 if(ce.autoHide === false){
11003 close.setDisplayed(true);
11008 close.setDisplayed(false);
11014 el.avoidY = xy[1]-18;
11019 el.setStyle("visibility", "visible");
11020 el.fadeIn({callback: afterShow});
11026 var afterShow = function(){
11030 if(tm.autoDismiss && ce.autoHide !== false){
11031 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11036 var hide = function(noanim){
11037 clearTimeout(dismissProc);
11038 clearTimeout(hideProc);
11040 if(el.isVisible()){
11042 if(noanim !== true && tm.animate){
11043 el.fadeOut({callback: afterHide});
11050 var afterHide = function(){
11053 el.removeClass(removeCls);
11060 * @cfg {Number} minWidth
11061 * The minimum width of the quick tip (defaults to 40)
11065 * @cfg {Number} maxWidth
11066 * The maximum width of the quick tip (defaults to 300)
11070 * @cfg {Boolean} interceptTitles
11071 * True to automatically use the element's DOM title value if available (defaults to false)
11073 interceptTitles : false,
11075 * @cfg {Boolean} trackMouse
11076 * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11078 trackMouse : false,
11080 * @cfg {Boolean} hideOnClick
11081 * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11083 hideOnClick : true,
11085 * @cfg {Number} showDelay
11086 * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11090 * @cfg {Number} hideDelay
11091 * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11095 * @cfg {Boolean} autoHide
11096 * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11097 * Used in conjunction with hideDelay.
11102 * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11103 * (defaults to true). Used in conjunction with autoDismissDelay.
11105 autoDismiss : true,
11108 * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11110 autoDismissDelay : 5000,
11112 * @cfg {Boolean} animate
11113 * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11118 * @cfg {String} title
11119 * Title text to display (defaults to ''). This can be any valid HTML markup.
11123 * @cfg {String} text
11124 * Body text to display (defaults to ''). This can be any valid HTML markup.
11128 * @cfg {String} cls
11129 * A CSS class to apply to the base quick tip element (defaults to '').
11133 * @cfg {Number} width
11134 * Width in pixels of the quick tip (defaults to auto). Width will be ignored if it exceeds the bounds of
11135 * minWidth or maxWidth.
11140 * Initialize and enable QuickTips for first use. This should be called once before the first attempt to access
11141 * or display QuickTips in a page.
11144 tm = Roo.QuickTips;
11145 cfg = tm.tagConfig;
11147 if(!Roo.isReady){ // allow calling of init() before onReady
11148 Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11151 el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11152 el.fxDefaults = {stopFx: true};
11153 // maximum custom styling
11154 //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>');
11155 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>');
11156 tipTitle = el.child('h3');
11157 tipTitle.enableDisplayMode("block");
11158 tipBody = el.child('div.x-tip-bd');
11159 tipBodyText = el.child('div.x-tip-bd-inner');
11160 //bdLeft = el.child('div.x-tip-bd-left');
11161 //bdRight = el.child('div.x-tip-bd-right');
11162 close = el.child('div.x-tip-close');
11163 close.enableDisplayMode("block");
11164 close.on("click", hide);
11165 var d = Roo.get(document);
11166 d.on("mousedown", onDown);
11167 d.on("mouseover", onOver);
11168 d.on("mouseout", onOut);
11169 d.on("mousemove", onMove);
11170 esc = d.addKeyListener(27, hide);
11173 dd = el.initDD("default", null, {
11174 onDrag : function(){
11178 dd.setHandleElId(tipTitle.id);
11187 * Configures a new quick tip instance and assigns it to a target element. The following config options
11190 Property Type Description
11191 ---------- --------------------- ------------------------------------------------------------------------
11192 target Element/String/Array An Element, id or array of ids that this quick tip should be tied to
11194 * @param {Object} config The config object
11196 register : function(config){
11197 var cs = config instanceof Array ? config : arguments;
11198 for(var i = 0, len = cs.length; i < len; i++) {
11200 var target = c.target;
11202 if(target instanceof Array){
11203 for(var j = 0, jlen = target.length; j < jlen; j++){
11204 tagEls[target[j]] = c;
11207 tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11214 * Removes this quick tip from its element and destroys it.
11215 * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11217 unregister : function(el){
11218 delete tagEls[Roo.id(el)];
11222 * Enable this quick tip.
11224 enable : function(){
11225 if(inited && disabled){
11227 if(locks.length < 1){
11234 * Disable this quick tip.
11236 disable : function(){
11238 clearTimeout(showProc);
11239 clearTimeout(hideProc);
11240 clearTimeout(dismissProc);
11248 * Returns true if the quick tip is enabled, else false.
11250 isEnabled : function(){
11256 namespace : "roo", // was ext?? this may break..
11257 alt_namespace : "ext",
11258 attribute : "qtip",
11268 // backwards compat
11269 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11271 * Ext JS Library 1.1.1
11272 * Copyright(c) 2006-2007, Ext JS, LLC.
11274 * Originally Released Under LGPL - original licence link has changed is not relivant.
11277 * <script type="text/javascript">
11282 * @class Roo.tree.TreePanel
11283 * @extends Roo.data.Tree
11284 * @cfg {Roo.tree.TreeNode} root The root node
11285 * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11286 * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11287 * @cfg {Boolean} enableDD true to enable drag and drop
11288 * @cfg {Boolean} enableDrag true to enable just drag
11289 * @cfg {Boolean} enableDrop true to enable just drop
11290 * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11291 * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11292 * @cfg {String} ddGroup The DD group this TreePanel belongs to
11293 * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11294 * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11295 * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11296 * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11297 * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11298 * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11299 * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11300 * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11301 * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
11302 * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
11303 * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11304 * @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>
11305 * @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>
11308 * @param {String/HTMLElement/Element} el The container element
11309 * @param {Object} config
11311 Roo.tree.TreePanel = function(el, config){
11313 var loader = false;
11315 root = config.root;
11316 delete config.root;
11318 if (config.loader) {
11319 loader = config.loader;
11320 delete config.loader;
11323 Roo.apply(this, config);
11324 Roo.tree.TreePanel.superclass.constructor.call(this);
11325 this.el = Roo.get(el);
11326 this.el.addClass('x-tree');
11327 //console.log(root);
11329 this.setRootNode( Roo.factory(root, Roo.tree));
11332 this.loader = Roo.factory(loader, Roo.tree);
11335 * Read-only. The id of the container element becomes this TreePanel's id.
11337 this.id = this.el.id;
11340 * @event beforeload
11341 * Fires before a node is loaded, return false to cancel
11342 * @param {Node} node The node being loaded
11344 "beforeload" : true,
11347 * Fires when a node is loaded
11348 * @param {Node} node The node that was loaded
11352 * @event textchange
11353 * Fires when the text for a node is changed
11354 * @param {Node} node The node
11355 * @param {String} text The new text
11356 * @param {String} oldText The old text
11358 "textchange" : true,
11360 * @event beforeexpand
11361 * Fires before a node is expanded, return false to cancel.
11362 * @param {Node} node The node
11363 * @param {Boolean} deep
11364 * @param {Boolean} anim
11366 "beforeexpand" : true,
11368 * @event beforecollapse
11369 * Fires before a node is collapsed, return false to cancel.
11370 * @param {Node} node The node
11371 * @param {Boolean} deep
11372 * @param {Boolean} anim
11374 "beforecollapse" : true,
11377 * Fires when a node is expanded
11378 * @param {Node} node The node
11382 * @event disabledchange
11383 * Fires when the disabled status of a node changes
11384 * @param {Node} node The node
11385 * @param {Boolean} disabled
11387 "disabledchange" : true,
11390 * Fires when a node is collapsed
11391 * @param {Node} node The node
11395 * @event beforeclick
11396 * Fires before click processing on a node. Return false to cancel the default action.
11397 * @param {Node} node The node
11398 * @param {Roo.EventObject} e The event object
11400 "beforeclick":true,
11402 * @event checkchange
11403 * Fires when a node with a checkbox's checked property changes
11404 * @param {Node} this This node
11405 * @param {Boolean} checked
11407 "checkchange":true,
11410 * Fires when a node is clicked
11411 * @param {Node} node The node
11412 * @param {Roo.EventObject} e The event object
11417 * Fires when a node is double clicked
11418 * @param {Node} node The node
11419 * @param {Roo.EventObject} e The event object
11423 * @event contextmenu
11424 * Fires when a node is right clicked
11425 * @param {Node} node The node
11426 * @param {Roo.EventObject} e The event object
11428 "contextmenu":true,
11430 * @event beforechildrenrendered
11431 * Fires right before the child nodes for a node are rendered
11432 * @param {Node} node The node
11434 "beforechildrenrendered":true,
11437 * Fires when a node starts being dragged
11438 * @param {Roo.tree.TreePanel} this
11439 * @param {Roo.tree.TreeNode} node
11440 * @param {event} e The raw browser event
11442 "startdrag" : true,
11445 * Fires when a drag operation is complete
11446 * @param {Roo.tree.TreePanel} this
11447 * @param {Roo.tree.TreeNode} node
11448 * @param {event} e The raw browser event
11453 * Fires when a dragged node is dropped on a valid DD target
11454 * @param {Roo.tree.TreePanel} this
11455 * @param {Roo.tree.TreeNode} node
11456 * @param {DD} dd The dd it was dropped on
11457 * @param {event} e The raw browser event
11461 * @event beforenodedrop
11462 * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11463 * passed to handlers has the following properties:<br />
11464 * <ul style="padding:5px;padding-left:16px;">
11465 * <li>tree - The TreePanel</li>
11466 * <li>target - The node being targeted for the drop</li>
11467 * <li>data - The drag data from the drag source</li>
11468 * <li>point - The point of the drop - append, above or below</li>
11469 * <li>source - The drag source</li>
11470 * <li>rawEvent - Raw mouse event</li>
11471 * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11472 * to be inserted by setting them on this object.</li>
11473 * <li>cancel - Set this to true to cancel the drop.</li>
11475 * @param {Object} dropEvent
11477 "beforenodedrop" : true,
11480 * Fires after a DD object is dropped on a node in this tree. The dropEvent
11481 * passed to handlers has the following properties:<br />
11482 * <ul style="padding:5px;padding-left:16px;">
11483 * <li>tree - The TreePanel</li>
11484 * <li>target - The node being targeted for the drop</li>
11485 * <li>data - The drag data from the drag source</li>
11486 * <li>point - The point of the drop - append, above or below</li>
11487 * <li>source - The drag source</li>
11488 * <li>rawEvent - Raw mouse event</li>
11489 * <li>dropNode - Dropped node(s).</li>
11491 * @param {Object} dropEvent
11495 * @event nodedragover
11496 * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11497 * passed to handlers has the following properties:<br />
11498 * <ul style="padding:5px;padding-left:16px;">
11499 * <li>tree - The TreePanel</li>
11500 * <li>target - The node being targeted for the drop</li>
11501 * <li>data - The drag data from the drag source</li>
11502 * <li>point - The point of the drop - append, above or below</li>
11503 * <li>source - The drag source</li>
11504 * <li>rawEvent - Raw mouse event</li>
11505 * <li>dropNode - Drop node(s) provided by the source.</li>
11506 * <li>cancel - Set this to true to signal drop not allowed.</li>
11508 * @param {Object} dragOverEvent
11510 "nodedragover" : true,
11512 * @event appendnode
11513 * Fires when append node to the tree
11514 * @param {Roo.tree.TreePanel} this
11515 * @param {Roo.tree.TreeNode} node
11516 * @param {Number} index The index of the newly appended node
11518 "appendnode" : true
11521 if(this.singleExpand){
11522 this.on("beforeexpand", this.restrictExpand, this);
11525 this.editor.tree = this;
11526 this.editor = Roo.factory(this.editor, Roo.tree);
11529 if (this.selModel) {
11530 this.selModel = Roo.factory(this.selModel, Roo.tree);
11534 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11535 rootVisible : true,
11536 animate: Roo.enableFx,
11539 hlDrop : Roo.enableFx,
11543 rendererTip: false,
11545 restrictExpand : function(node){
11546 var p = node.parentNode;
11548 if(p.expandedChild && p.expandedChild.parentNode == p){
11549 p.expandedChild.collapse();
11551 p.expandedChild = node;
11555 // private override
11556 setRootNode : function(node){
11557 Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11558 if(!this.rootVisible){
11559 node.ui = new Roo.tree.RootTreeNodeUI(node);
11565 * Returns the container element for this TreePanel
11567 getEl : function(){
11572 * Returns the default TreeLoader for this TreePanel
11574 getLoader : function(){
11575 return this.loader;
11581 expandAll : function(){
11582 this.root.expand(true);
11586 * Collapse all nodes
11588 collapseAll : function(){
11589 this.root.collapse(true);
11593 * Returns the selection model used by this TreePanel
11595 getSelectionModel : function(){
11596 if(!this.selModel){
11597 this.selModel = new Roo.tree.DefaultSelectionModel();
11599 return this.selModel;
11603 * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11604 * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11605 * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11608 getChecked : function(a, startNode){
11609 startNode = startNode || this.root;
11611 var f = function(){
11612 if(this.attributes.checked){
11613 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11616 startNode.cascade(f);
11621 * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11622 * @param {String} path
11623 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11624 * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11625 * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11627 expandPath : function(path, attr, callback){
11628 attr = attr || "id";
11629 var keys = path.split(this.pathSeparator);
11630 var curNode = this.root;
11631 if(curNode.attributes[attr] != keys[1]){ // invalid root
11633 callback(false, null);
11638 var f = function(){
11639 if(++index == keys.length){
11641 callback(true, curNode);
11645 var c = curNode.findChild(attr, keys[index]);
11648 callback(false, curNode);
11653 c.expand(false, false, f);
11655 curNode.expand(false, false, f);
11659 * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11660 * @param {String} path
11661 * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11662 * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11663 * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11665 selectPath : function(path, attr, callback){
11666 attr = attr || "id";
11667 var keys = path.split(this.pathSeparator);
11668 var v = keys.pop();
11669 if(keys.length > 0){
11670 var f = function(success, node){
11671 if(success && node){
11672 var n = node.findChild(attr, v);
11678 }else if(callback){
11679 callback(false, n);
11683 callback(false, n);
11687 this.expandPath(keys.join(this.pathSeparator), attr, f);
11689 this.root.select();
11691 callback(true, this.root);
11696 getTreeEl : function(){
11701 * Trigger rendering of this TreePanel
11703 render : function(){
11704 if (this.innerCt) {
11705 return this; // stop it rendering more than once!!
11708 this.innerCt = this.el.createChild({tag:"ul",
11709 cls:"x-tree-root-ct " +
11710 (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11712 if(this.containerScroll){
11713 Roo.dd.ScrollManager.register(this.el);
11715 if((this.enableDD || this.enableDrop) && !this.dropZone){
11717 * The dropZone used by this tree if drop is enabled
11718 * @type Roo.tree.TreeDropZone
11720 this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11721 ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11724 if((this.enableDD || this.enableDrag) && !this.dragZone){
11726 * The dragZone used by this tree if drag is enabled
11727 * @type Roo.tree.TreeDragZone
11729 this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11730 ddGroup: this.ddGroup || "TreeDD",
11731 scroll: this.ddScroll
11734 this.getSelectionModel().init(this);
11736 Roo.log("ROOT not set in tree");
11739 this.root.render();
11740 if(!this.rootVisible){
11741 this.root.renderChildren();
11747 * Ext JS Library 1.1.1
11748 * Copyright(c) 2006-2007, Ext JS, LLC.
11750 * Originally Released Under LGPL - original licence link has changed is not relivant.
11753 * <script type="text/javascript">
11758 * @class Roo.tree.DefaultSelectionModel
11759 * @extends Roo.util.Observable
11760 * The default single selection for a TreePanel.
11761 * @param {Object} cfg Configuration
11763 Roo.tree.DefaultSelectionModel = function(cfg){
11764 this.selNode = null;
11770 * @event selectionchange
11771 * Fires when the selected node changes
11772 * @param {DefaultSelectionModel} this
11773 * @param {TreeNode} node the new selection
11775 "selectionchange" : true,
11778 * @event beforeselect
11779 * Fires before the selected node changes, return false to cancel the change
11780 * @param {DefaultSelectionModel} this
11781 * @param {TreeNode} node the new selection
11782 * @param {TreeNode} node the old selection
11784 "beforeselect" : true
11787 Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11790 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11791 init : function(tree){
11793 tree.getTreeEl().on("keydown", this.onKeyDown, this);
11794 tree.on("click", this.onNodeClick, this);
11797 onNodeClick : function(node, e){
11798 if (e.ctrlKey && this.selNode == node) {
11799 this.unselect(node);
11807 * @param {TreeNode} node The node to select
11808 * @return {TreeNode} The selected node
11810 select : function(node){
11811 var last = this.selNode;
11812 if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11814 last.ui.onSelectedChange(false);
11816 this.selNode = node;
11817 node.ui.onSelectedChange(true);
11818 this.fireEvent("selectionchange", this, node, last);
11825 * @param {TreeNode} node The node to unselect
11827 unselect : function(node){
11828 if(this.selNode == node){
11829 this.clearSelections();
11834 * Clear all selections
11836 clearSelections : function(){
11837 var n = this.selNode;
11839 n.ui.onSelectedChange(false);
11840 this.selNode = null;
11841 this.fireEvent("selectionchange", this, null);
11847 * Get the selected node
11848 * @return {TreeNode} The selected node
11850 getSelectedNode : function(){
11851 return this.selNode;
11855 * Returns true if the node is selected
11856 * @param {TreeNode} node The node to check
11857 * @return {Boolean}
11859 isSelected : function(node){
11860 return this.selNode == node;
11864 * Selects the node above the selected node in the tree, intelligently walking the nodes
11865 * @return TreeNode The new selection
11867 selectPrevious : function(){
11868 var s = this.selNode || this.lastSelNode;
11872 var ps = s.previousSibling;
11874 if(!ps.isExpanded() || ps.childNodes.length < 1){
11875 return this.select(ps);
11877 var lc = ps.lastChild;
11878 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11881 return this.select(lc);
11883 } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11884 return this.select(s.parentNode);
11890 * Selects the node above the selected node in the tree, intelligently walking the nodes
11891 * @return TreeNode The new selection
11893 selectNext : function(){
11894 var s = this.selNode || this.lastSelNode;
11898 if(s.firstChild && s.isExpanded()){
11899 return this.select(s.firstChild);
11900 }else if(s.nextSibling){
11901 return this.select(s.nextSibling);
11902 }else if(s.parentNode){
11904 s.parentNode.bubble(function(){
11905 if(this.nextSibling){
11906 newS = this.getOwnerTree().selModel.select(this.nextSibling);
11915 onKeyDown : function(e){
11916 var s = this.selNode || this.lastSelNode;
11917 // undesirable, but required
11922 var k = e.getKey();
11930 this.selectPrevious();
11933 e.preventDefault();
11934 if(s.hasChildNodes()){
11935 if(!s.isExpanded()){
11937 }else if(s.firstChild){
11938 this.select(s.firstChild, e);
11943 e.preventDefault();
11944 if(s.hasChildNodes() && s.isExpanded()){
11946 }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11947 this.select(s.parentNode, e);
11955 * @class Roo.tree.MultiSelectionModel
11956 * @extends Roo.util.Observable
11957 * Multi selection for a TreePanel.
11958 * @param {Object} cfg Configuration
11960 Roo.tree.MultiSelectionModel = function(){
11961 this.selNodes = [];
11965 * @event selectionchange
11966 * Fires when the selected nodes change
11967 * @param {MultiSelectionModel} this
11968 * @param {Array} nodes Array of the selected nodes
11970 "selectionchange" : true
11972 Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11976 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11977 init : function(tree){
11979 tree.getTreeEl().on("keydown", this.onKeyDown, this);
11980 tree.on("click", this.onNodeClick, this);
11983 onNodeClick : function(node, e){
11984 this.select(node, e, e.ctrlKey);
11989 * @param {TreeNode} node The node to select
11990 * @param {EventObject} e (optional) An event associated with the selection
11991 * @param {Boolean} keepExisting True to retain existing selections
11992 * @return {TreeNode} The selected node
11994 select : function(node, e, keepExisting){
11995 if(keepExisting !== true){
11996 this.clearSelections(true);
11998 if(this.isSelected(node)){
11999 this.lastSelNode = node;
12002 this.selNodes.push(node);
12003 this.selMap[node.id] = node;
12004 this.lastSelNode = node;
12005 node.ui.onSelectedChange(true);
12006 this.fireEvent("selectionchange", this, this.selNodes);
12012 * @param {TreeNode} node The node to unselect
12014 unselect : function(node){
12015 if(this.selMap[node.id]){
12016 node.ui.onSelectedChange(false);
12017 var sn = this.selNodes;
12020 index = sn.indexOf(node);
12022 for(var i = 0, len = sn.length; i < len; i++){
12030 this.selNodes.splice(index, 1);
12032 delete this.selMap[node.id];
12033 this.fireEvent("selectionchange", this, this.selNodes);
12038 * Clear all selections
12040 clearSelections : function(suppressEvent){
12041 var sn = this.selNodes;
12043 for(var i = 0, len = sn.length; i < len; i++){
12044 sn[i].ui.onSelectedChange(false);
12046 this.selNodes = [];
12048 if(suppressEvent !== true){
12049 this.fireEvent("selectionchange", this, this.selNodes);
12055 * Returns true if the node is selected
12056 * @param {TreeNode} node The node to check
12057 * @return {Boolean}
12059 isSelected : function(node){
12060 return this.selMap[node.id] ? true : false;
12064 * Returns an array of the selected nodes
12067 getSelectedNodes : function(){
12068 return this.selNodes;
12071 onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12073 selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12075 selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12078 * Ext JS Library 1.1.1
12079 * Copyright(c) 2006-2007, Ext JS, LLC.
12081 * Originally Released Under LGPL - original licence link has changed is not relivant.
12084 * <script type="text/javascript">
12088 * @class Roo.tree.TreeNode
12089 * @extends Roo.data.Node
12090 * @cfg {String} text The text for this node
12091 * @cfg {Boolean} expanded true to start the node expanded
12092 * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12093 * @cfg {Boolean} allowDrop false if this node cannot be drop on
12094 * @cfg {Boolean} disabled true to start the node disabled
12095 * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12096 * is to use the cls or iconCls attributes and add the icon via a CSS background image.
12097 * @cfg {String} cls A css class to be added to the node
12098 * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12099 * @cfg {String} href URL of the link used for the node (defaults to #)
12100 * @cfg {String} hrefTarget target frame for the link
12101 * @cfg {String} qtip An Ext QuickTip for the node
12102 * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12103 * @cfg {Boolean} singleClickExpand True for single click expand on this node
12104 * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12105 * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12106 * (defaults to undefined with no checkbox rendered)
12108 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12110 Roo.tree.TreeNode = function(attributes){
12111 attributes = attributes || {};
12112 if(typeof attributes == "string"){
12113 attributes = {text: attributes};
12115 this.childrenRendered = false;
12116 this.rendered = false;
12117 Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12118 this.expanded = attributes.expanded === true;
12119 this.isTarget = attributes.isTarget !== false;
12120 this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12121 this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12124 * Read-only. The text for this node. To change it use setText().
12127 this.text = attributes.text;
12129 * True if this node is disabled.
12132 this.disabled = attributes.disabled === true;
12136 * @event textchange
12137 * Fires when the text for this node is changed
12138 * @param {Node} this This node
12139 * @param {String} text The new text
12140 * @param {String} oldText The old text
12142 "textchange" : true,
12144 * @event beforeexpand
12145 * Fires before this node is expanded, return false to cancel.
12146 * @param {Node} this This node
12147 * @param {Boolean} deep
12148 * @param {Boolean} anim
12150 "beforeexpand" : true,
12152 * @event beforecollapse
12153 * Fires before this node is collapsed, return false to cancel.
12154 * @param {Node} this This node
12155 * @param {Boolean} deep
12156 * @param {Boolean} anim
12158 "beforecollapse" : true,
12161 * Fires when this node is expanded
12162 * @param {Node} this This node
12166 * @event disabledchange
12167 * Fires when the disabled status of this node changes
12168 * @param {Node} this This node
12169 * @param {Boolean} disabled
12171 "disabledchange" : true,
12174 * Fires when this node is collapsed
12175 * @param {Node} this This node
12179 * @event beforeclick
12180 * Fires before click processing. Return false to cancel the default action.
12181 * @param {Node} this This node
12182 * @param {Roo.EventObject} e The event object
12184 "beforeclick":true,
12186 * @event checkchange
12187 * Fires when a node with a checkbox's checked property changes
12188 * @param {Node} this This node
12189 * @param {Boolean} checked
12191 "checkchange":true,
12194 * Fires when this node is clicked
12195 * @param {Node} this This node
12196 * @param {Roo.EventObject} e The event object
12201 * Fires when this node is double clicked
12202 * @param {Node} this This node
12203 * @param {Roo.EventObject} e The event object
12207 * @event contextmenu
12208 * Fires when this node is right clicked
12209 * @param {Node} this This node
12210 * @param {Roo.EventObject} e The event object
12212 "contextmenu":true,
12214 * @event beforechildrenrendered
12215 * Fires right before the child nodes for this node are rendered
12216 * @param {Node} this This node
12218 "beforechildrenrendered":true
12221 var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12224 * Read-only. The UI for this node
12227 this.ui = new uiClass(this);
12229 // finally support items[]
12230 if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12235 Roo.each(this.attributes.items, function(c) {
12236 this.appendChild(Roo.factory(c,Roo.Tree));
12238 delete this.attributes.items;
12243 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12244 preventHScroll: true,
12246 * Returns true if this node is expanded
12247 * @return {Boolean}
12249 isExpanded : function(){
12250 return this.expanded;
12254 * Returns the UI object for this node
12255 * @return {TreeNodeUI}
12257 getUI : function(){
12261 // private override
12262 setFirstChild : function(node){
12263 var of = this.firstChild;
12264 Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12265 if(this.childrenRendered && of && node != of){
12266 of.renderIndent(true, true);
12269 this.renderIndent(true, true);
12273 // private override
12274 setLastChild : function(node){
12275 var ol = this.lastChild;
12276 Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12277 if(this.childrenRendered && ol && node != ol){
12278 ol.renderIndent(true, true);
12281 this.renderIndent(true, true);
12285 // these methods are overridden to provide lazy rendering support
12286 // private override
12287 appendChild : function()
12289 var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12290 if(node && this.childrenRendered){
12293 this.ui.updateExpandIcon();
12297 // private override
12298 removeChild : function(node){
12299 this.ownerTree.getSelectionModel().unselect(node);
12300 Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12301 // if it's been rendered remove dom node
12302 if(this.childrenRendered){
12305 if(this.childNodes.length < 1){
12306 this.collapse(false, false);
12308 this.ui.updateExpandIcon();
12310 if(!this.firstChild) {
12311 this.childrenRendered = false;
12316 // private override
12317 insertBefore : function(node, refNode){
12318 var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12319 if(newNode && refNode && this.childrenRendered){
12322 this.ui.updateExpandIcon();
12327 * Sets the text for this node
12328 * @param {String} text
12330 setText : function(text){
12331 var oldText = this.text;
12333 this.attributes.text = text;
12334 if(this.rendered){ // event without subscribing
12335 this.ui.onTextChange(this, text, oldText);
12337 this.fireEvent("textchange", this, text, oldText);
12341 * Triggers selection of this node
12343 select : function(){
12344 this.getOwnerTree().getSelectionModel().select(this);
12348 * Triggers deselection of this node
12350 unselect : function(){
12351 this.getOwnerTree().getSelectionModel().unselect(this);
12355 * Returns true if this node is selected
12356 * @return {Boolean}
12358 isSelected : function(){
12359 return this.getOwnerTree().getSelectionModel().isSelected(this);
12363 * Expand this node.
12364 * @param {Boolean} deep (optional) True to expand all children as well
12365 * @param {Boolean} anim (optional) false to cancel the default animation
12366 * @param {Function} callback (optional) A callback to be called when
12367 * expanding this node completes (does not wait for deep expand to complete).
12368 * Called with 1 parameter, this node.
12370 expand : function(deep, anim, callback){
12371 if(!this.expanded){
12372 if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12375 if(!this.childrenRendered){
12376 this.renderChildren();
12378 this.expanded = true;
12380 if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12381 this.ui.animExpand(function(){
12382 this.fireEvent("expand", this);
12383 if(typeof callback == "function"){
12387 this.expandChildNodes(true);
12389 }.createDelegate(this));
12393 this.fireEvent("expand", this);
12394 if(typeof callback == "function"){
12399 if(typeof callback == "function"){
12404 this.expandChildNodes(true);
12408 isHiddenRoot : function(){
12409 return this.isRoot && !this.getOwnerTree().rootVisible;
12413 * Collapse this node.
12414 * @param {Boolean} deep (optional) True to collapse all children as well
12415 * @param {Boolean} anim (optional) false to cancel the default animation
12417 collapse : function(deep, anim){
12418 if(this.expanded && !this.isHiddenRoot()){
12419 if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12422 this.expanded = false;
12423 if((this.getOwnerTree().animate && anim !== false) || anim){
12424 this.ui.animCollapse(function(){
12425 this.fireEvent("collapse", this);
12427 this.collapseChildNodes(true);
12429 }.createDelegate(this));
12432 this.ui.collapse();
12433 this.fireEvent("collapse", this);
12437 var cs = this.childNodes;
12438 for(var i = 0, len = cs.length; i < len; i++) {
12439 cs[i].collapse(true, false);
12445 delayedExpand : function(delay){
12446 if(!this.expandProcId){
12447 this.expandProcId = this.expand.defer(delay, this);
12452 cancelExpand : function(){
12453 if(this.expandProcId){
12454 clearTimeout(this.expandProcId);
12456 this.expandProcId = false;
12460 * Toggles expanded/collapsed state of the node
12462 toggle : function(){
12471 * Ensures all parent nodes are expanded
12473 ensureVisible : function(callback){
12474 var tree = this.getOwnerTree();
12475 tree.expandPath(this.parentNode.getPath(), false, function(){
12476 tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12477 Roo.callback(callback);
12478 }.createDelegate(this));
12482 * Expand all child nodes
12483 * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12485 expandChildNodes : function(deep){
12486 var cs = this.childNodes;
12487 for(var i = 0, len = cs.length; i < len; i++) {
12488 cs[i].expand(deep);
12493 * Collapse all child nodes
12494 * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12496 collapseChildNodes : function(deep){
12497 var cs = this.childNodes;
12498 for(var i = 0, len = cs.length; i < len; i++) {
12499 cs[i].collapse(deep);
12504 * Disables this node
12506 disable : function(){
12507 this.disabled = true;
12509 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12510 this.ui.onDisableChange(this, true);
12512 this.fireEvent("disabledchange", this, true);
12516 * Enables this node
12518 enable : function(){
12519 this.disabled = false;
12520 if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12521 this.ui.onDisableChange(this, false);
12523 this.fireEvent("disabledchange", this, false);
12527 renderChildren : function(suppressEvent){
12528 if(suppressEvent !== false){
12529 this.fireEvent("beforechildrenrendered", this);
12531 var cs = this.childNodes;
12532 for(var i = 0, len = cs.length; i < len; i++){
12533 cs[i].render(true);
12535 this.childrenRendered = true;
12539 sort : function(fn, scope){
12540 Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12541 if(this.childrenRendered){
12542 var cs = this.childNodes;
12543 for(var i = 0, len = cs.length; i < len; i++){
12544 cs[i].render(true);
12550 render : function(bulkRender){
12551 this.ui.render(bulkRender);
12552 if(!this.rendered){
12553 this.rendered = true;
12555 this.expanded = false;
12556 this.expand(false, false);
12562 renderIndent : function(deep, refresh){
12564 this.ui.childIndent = null;
12566 this.ui.renderIndent();
12567 if(deep === true && this.childrenRendered){
12568 var cs = this.childNodes;
12569 for(var i = 0, len = cs.length; i < len; i++){
12570 cs[i].renderIndent(true, refresh);
12576 * Ext JS Library 1.1.1
12577 * Copyright(c) 2006-2007, Ext JS, LLC.
12579 * Originally Released Under LGPL - original licence link has changed is not relivant.
12582 * <script type="text/javascript">
12586 * @class Roo.tree.AsyncTreeNode
12587 * @extends Roo.tree.TreeNode
12588 * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12590 * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12592 Roo.tree.AsyncTreeNode = function(config){
12593 this.loaded = false;
12594 this.loading = false;
12595 Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12597 * @event beforeload
12598 * Fires before this node is loaded, return false to cancel
12599 * @param {Node} this This node
12601 this.addEvents({'beforeload':true, 'load': true});
12604 * Fires when this node is loaded
12605 * @param {Node} this This node
12608 * The loader used by this node (defaults to using the tree's defined loader)
12613 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12614 expand : function(deep, anim, callback){
12615 if(this.loading){ // if an async load is already running, waiting til it's done
12617 var f = function(){
12618 if(!this.loading){ // done loading
12619 clearInterval(timer);
12620 this.expand(deep, anim, callback);
12622 }.createDelegate(this);
12623 timer = setInterval(f, 200);
12627 if(this.fireEvent("beforeload", this) === false){
12630 this.loading = true;
12631 this.ui.beforeLoad(this);
12632 var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12634 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12638 Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12642 * Returns true if this node is currently loading
12643 * @return {Boolean}
12645 isLoading : function(){
12646 return this.loading;
12649 loadComplete : function(deep, anim, callback){
12650 this.loading = false;
12651 this.loaded = true;
12652 this.ui.afterLoad(this);
12653 this.fireEvent("load", this);
12654 this.expand(deep, anim, callback);
12658 * Returns true if this node has been loaded
12659 * @return {Boolean}
12661 isLoaded : function(){
12662 return this.loaded;
12665 hasChildNodes : function(){
12666 if(!this.isLeaf() && !this.loaded){
12669 return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12674 * Trigger a reload for this node
12675 * @param {Function} callback
12677 reload : function(callback){
12678 this.collapse(false, false);
12679 while(this.firstChild){
12680 this.removeChild(this.firstChild);
12682 this.childrenRendered = false;
12683 this.loaded = false;
12684 if(this.isHiddenRoot()){
12685 this.expanded = false;
12687 this.expand(false, false, callback);
12691 * Ext JS Library 1.1.1
12692 * Copyright(c) 2006-2007, Ext JS, LLC.
12694 * Originally Released Under LGPL - original licence link has changed is not relivant.
12697 * <script type="text/javascript">
12701 * @class Roo.tree.TreeNodeUI
12703 * @param {Object} node The node to render
12704 * The TreeNode UI implementation is separate from the
12705 * tree implementation. Unless you are customizing the tree UI,
12706 * you should never have to use this directly.
12708 Roo.tree.TreeNodeUI = function(node){
12710 this.rendered = false;
12711 this.animating = false;
12712 this.emptyIcon = Roo.BLANK_IMAGE_URL;
12715 Roo.tree.TreeNodeUI.prototype = {
12716 removeChild : function(node){
12718 this.ctNode.removeChild(node.ui.getEl());
12722 beforeLoad : function(){
12723 this.addClass("x-tree-node-loading");
12726 afterLoad : function(){
12727 this.removeClass("x-tree-node-loading");
12730 onTextChange : function(node, text, oldText){
12732 this.textNode.innerHTML = text;
12736 onDisableChange : function(node, state){
12737 this.disabled = state;
12739 this.addClass("x-tree-node-disabled");
12741 this.removeClass("x-tree-node-disabled");
12745 onSelectedChange : function(state){
12748 this.addClass("x-tree-selected");
12751 this.removeClass("x-tree-selected");
12755 onMove : function(tree, node, oldParent, newParent, index, refNode){
12756 this.childIndent = null;
12758 var targetNode = newParent.ui.getContainer();
12759 if(!targetNode){//target not rendered
12760 this.holder = document.createElement("div");
12761 this.holder.appendChild(this.wrap);
12764 var insertBefore = refNode ? refNode.ui.getEl() : null;
12766 targetNode.insertBefore(this.wrap, insertBefore);
12768 targetNode.appendChild(this.wrap);
12770 this.node.renderIndent(true);
12774 addClass : function(cls){
12776 Roo.fly(this.elNode).addClass(cls);
12780 removeClass : function(cls){
12782 Roo.fly(this.elNode).removeClass(cls);
12786 remove : function(){
12788 this.holder = document.createElement("div");
12789 this.holder.appendChild(this.wrap);
12793 fireEvent : function(){
12794 return this.node.fireEvent.apply(this.node, arguments);
12797 initEvents : function(){
12798 this.node.on("move", this.onMove, this);
12799 var E = Roo.EventManager;
12800 var a = this.anchor;
12802 var el = Roo.fly(a, '_treeui');
12804 if(Roo.isOpera){ // opera render bug ignores the CSS
12805 el.setStyle("text-decoration", "none");
12808 el.on("click", this.onClick, this);
12809 el.on("dblclick", this.onDblClick, this);
12812 Roo.EventManager.on(this.checkbox,
12813 Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12816 el.on("contextmenu", this.onContextMenu, this);
12818 var icon = Roo.fly(this.iconNode);
12819 icon.on("click", this.onClick, this);
12820 icon.on("dblclick", this.onDblClick, this);
12821 icon.on("contextmenu", this.onContextMenu, this);
12822 E.on(this.ecNode, "click", this.ecClick, this, true);
12824 if(this.node.disabled){
12825 this.addClass("x-tree-node-disabled");
12827 if(this.node.hidden){
12828 this.addClass("x-tree-node-disabled");
12830 var ot = this.node.getOwnerTree();
12831 var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12832 if(dd && (!this.node.isRoot || ot.rootVisible)){
12833 Roo.dd.Registry.register(this.elNode, {
12835 handles: this.getDDHandles(),
12841 getDDHandles : function(){
12842 return [this.iconNode, this.textNode];
12847 this.wrap.style.display = "none";
12853 this.wrap.style.display = "";
12857 onContextMenu : function(e){
12858 if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12859 e.preventDefault();
12861 this.fireEvent("contextmenu", this.node, e);
12865 onClick : function(e){
12870 if(this.fireEvent("beforeclick", this.node, e) !== false){
12871 if(!this.disabled && this.node.attributes.href){
12872 this.fireEvent("click", this.node, e);
12875 e.preventDefault();
12880 if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12881 this.node.toggle();
12884 this.fireEvent("click", this.node, e);
12890 onDblClick : function(e){
12891 e.preventDefault();
12896 this.toggleCheck();
12898 if(!this.animating && this.node.hasChildNodes()){
12899 this.node.toggle();
12901 this.fireEvent("dblclick", this.node, e);
12904 onCheckChange : function(){
12905 var checked = this.checkbox.checked;
12906 this.node.attributes.checked = checked;
12907 this.fireEvent('checkchange', this.node, checked);
12910 ecClick : function(e){
12911 if(!this.animating && this.node.hasChildNodes()){
12912 this.node.toggle();
12916 startDrop : function(){
12917 this.dropping = true;
12920 // delayed drop so the click event doesn't get fired on a drop
12921 endDrop : function(){
12922 setTimeout(function(){
12923 this.dropping = false;
12924 }.createDelegate(this), 50);
12927 expand : function(){
12928 this.updateExpandIcon();
12929 this.ctNode.style.display = "";
12932 focus : function(){
12933 if(!this.node.preventHScroll){
12934 try{this.anchor.focus();
12936 }else if(!Roo.isIE){
12938 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12939 var l = noscroll.scrollLeft;
12940 this.anchor.focus();
12941 noscroll.scrollLeft = l;
12946 toggleCheck : function(value){
12947 var cb = this.checkbox;
12949 cb.checked = (value === undefined ? !cb.checked : value);
12955 this.anchor.blur();
12959 animExpand : function(callback){
12960 var ct = Roo.get(this.ctNode);
12962 if(!this.node.hasChildNodes()){
12963 this.updateExpandIcon();
12964 this.ctNode.style.display = "";
12965 Roo.callback(callback);
12968 this.animating = true;
12969 this.updateExpandIcon();
12972 callback : function(){
12973 this.animating = false;
12974 Roo.callback(callback);
12977 duration: this.node.ownerTree.duration || .25
12981 highlight : function(){
12982 var tree = this.node.getOwnerTree();
12983 Roo.fly(this.wrap).highlight(
12984 tree.hlColor || "C3DAF9",
12985 {endColor: tree.hlBaseColor}
12989 collapse : function(){
12990 this.updateExpandIcon();
12991 this.ctNode.style.display = "none";
12994 animCollapse : function(callback){
12995 var ct = Roo.get(this.ctNode);
12996 ct.enableDisplayMode('block');
12999 this.animating = true;
13000 this.updateExpandIcon();
13003 callback : function(){
13004 this.animating = false;
13005 Roo.callback(callback);
13008 duration: this.node.ownerTree.duration || .25
13012 getContainer : function(){
13013 return this.ctNode;
13016 getEl : function(){
13020 appendDDGhost : function(ghostNode){
13021 ghostNode.appendChild(this.elNode.cloneNode(true));
13024 getDDRepairXY : function(){
13025 return Roo.lib.Dom.getXY(this.iconNode);
13028 onRender : function(){
13032 render : function(bulkRender){
13033 var n = this.node, a = n.attributes;
13034 var targetNode = n.parentNode ?
13035 n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13037 if(!this.rendered){
13038 this.rendered = true;
13040 this.renderElements(n, a, targetNode, bulkRender);
13043 if(this.textNode.setAttributeNS){
13044 this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13046 this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13049 this.textNode.setAttribute("ext:qtip", a.qtip);
13051 this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13054 }else if(a.qtipCfg){
13055 a.qtipCfg.target = Roo.id(this.textNode);
13056 Roo.QuickTips.register(a.qtipCfg);
13059 if(!this.node.expanded){
13060 this.updateExpandIcon();
13063 if(bulkRender === true) {
13064 targetNode.appendChild(this.wrap);
13069 renderElements : function(n, a, targetNode, bulkRender)
13071 // add some indent caching, this helps performance when rendering a large tree
13072 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13073 var t = n.getOwnerTree();
13074 var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13075 if (typeof(n.attributes.html) != 'undefined') {
13076 txt = n.attributes.html;
13078 var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13079 var cb = typeof a.checked == 'boolean';
13080 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13081 var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13082 '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13083 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13084 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13085 cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13086 '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13087 a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "",
13088 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13089 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13092 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13093 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13094 n.nextSibling.ui.getEl(), buf.join(""));
13096 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13099 this.elNode = this.wrap.childNodes[0];
13100 this.ctNode = this.wrap.childNodes[1];
13101 var cs = this.elNode.childNodes;
13102 this.indentNode = cs[0];
13103 this.ecNode = cs[1];
13104 this.iconNode = cs[2];
13107 this.checkbox = cs[3];
13110 this.anchor = cs[index];
13111 this.textNode = cs[index].firstChild;
13114 getAnchor : function(){
13115 return this.anchor;
13118 getTextEl : function(){
13119 return this.textNode;
13122 getIconEl : function(){
13123 return this.iconNode;
13126 isChecked : function(){
13127 return this.checkbox ? this.checkbox.checked : false;
13130 updateExpandIcon : function(){
13132 var n = this.node, c1, c2;
13133 var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13134 var hasChild = n.hasChildNodes();
13138 c1 = "x-tree-node-collapsed";
13139 c2 = "x-tree-node-expanded";
13142 c1 = "x-tree-node-expanded";
13143 c2 = "x-tree-node-collapsed";
13146 this.removeClass("x-tree-node-leaf");
13147 this.wasLeaf = false;
13149 if(this.c1 != c1 || this.c2 != c2){
13150 Roo.fly(this.elNode).replaceClass(c1, c2);
13151 this.c1 = c1; this.c2 = c2;
13154 // this changes non-leafs into leafs if they have no children.
13155 // it's not very rational behaviour..
13157 if(!this.wasLeaf && this.node.leaf){
13158 Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13161 this.wasLeaf = true;
13164 var ecc = "x-tree-ec-icon "+cls;
13165 if(this.ecc != ecc){
13166 this.ecNode.className = ecc;
13172 getChildIndent : function(){
13173 if(!this.childIndent){
13177 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13179 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13181 buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13186 this.childIndent = buf.join("");
13188 return this.childIndent;
13191 renderIndent : function(){
13194 var p = this.node.parentNode;
13196 indent = p.ui.getChildIndent();
13198 if(this.indentMarkup != indent){ // don't rerender if not required
13199 this.indentNode.innerHTML = indent;
13200 this.indentMarkup = indent;
13202 this.updateExpandIcon();
13207 Roo.tree.RootTreeNodeUI = function(){
13208 Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13210 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13211 render : function(){
13212 if(!this.rendered){
13213 var targetNode = this.node.ownerTree.innerCt.dom;
13214 this.node.expanded = true;
13215 targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13216 this.wrap = this.ctNode = targetNode.firstChild;
13219 collapse : function(){
13221 expand : function(){
13225 * Ext JS Library 1.1.1
13226 * Copyright(c) 2006-2007, Ext JS, LLC.
13228 * Originally Released Under LGPL - original licence link has changed is not relivant.
13231 * <script type="text/javascript">
13234 * @class Roo.tree.TreeLoader
13235 * @extends Roo.util.Observable
13236 * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13237 * nodes from a specified URL. The response must be a javascript Array definition
13238 * who's elements are node definition objects. eg:
13243 { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13244 { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13251 * The old style respose with just an array is still supported, but not recommended.
13254 * A server request is sent, and child nodes are loaded only when a node is expanded.
13255 * The loading node's id is passed to the server under the parameter name "node" to
13256 * enable the server to produce the correct child nodes.
13258 * To pass extra parameters, an event handler may be attached to the "beforeload"
13259 * event, and the parameters specified in the TreeLoader's baseParams property:
13261 myTreeLoader.on("beforeload", function(treeLoader, node) {
13262 this.baseParams.category = node.attributes.category;
13267 * This would pass an HTTP parameter called "category" to the server containing
13268 * the value of the Node's "category" attribute.
13270 * Creates a new Treeloader.
13271 * @param {Object} config A config object containing config properties.
13273 Roo.tree.TreeLoader = function(config){
13274 this.baseParams = {};
13275 this.requestMethod = "POST";
13276 Roo.apply(this, config);
13281 * @event beforeload
13282 * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13283 * @param {Object} This TreeLoader object.
13284 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13285 * @param {Object} callback The callback function specified in the {@link #load} call.
13290 * Fires when the node has been successfuly loaded.
13291 * @param {Object} This TreeLoader object.
13292 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13293 * @param {Object} response The response object containing the data from the server.
13297 * @event loadexception
13298 * Fires if the network request failed.
13299 * @param {Object} This TreeLoader object.
13300 * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13301 * @param {Object} response The response object containing the data from the server.
13303 loadexception : true,
13306 * Fires before a node is created, enabling you to return custom Node types
13307 * @param {Object} This TreeLoader object.
13308 * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13313 Roo.tree.TreeLoader.superclass.constructor.call(this);
13316 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13318 * @cfg {String} dataUrl The URL from which to request a Json string which
13319 * specifies an array of node definition object representing the child nodes
13323 * @cfg {String} requestMethod either GET or POST
13324 * defaults to POST (due to BC)
13328 * @cfg {Object} baseParams (optional) An object containing properties which
13329 * specify HTTP parameters to be passed to each request for child nodes.
13332 * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13333 * created by this loader. If the attributes sent by the server have an attribute in this object,
13334 * they take priority.
13337 * @cfg {Object} uiProviders (optional) An object containing properties which
13339 * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13340 * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13341 * <i>uiProvider</i> attribute of a returned child node is a string rather
13342 * than a reference to a TreeNodeUI implementation, this that string value
13343 * is used as a property name in the uiProviders object. You can define the provider named
13344 * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13349 * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13350 * child nodes before loading.
13352 clearOnLoad : true,
13355 * @cfg {String} root (optional) Default to false. Use this to read data from an object
13356 * property on loading, rather than expecting an array. (eg. more compatible to a standard
13357 * Grid query { data : [ .....] }
13362 * @cfg {String} queryParam (optional)
13363 * Name of the query as it will be passed on the querystring (defaults to 'node')
13364 * eg. the request will be ?node=[id]
13371 * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13372 * This is called automatically when a node is expanded, but may be used to reload
13373 * a node (or append new children if the {@link #clearOnLoad} option is false.)
13374 * @param {Roo.tree.TreeNode} node
13375 * @param {Function} callback
13377 load : function(node, callback){
13378 if(this.clearOnLoad){
13379 while(node.firstChild){
13380 node.removeChild(node.firstChild);
13383 if(node.attributes.children){ // preloaded json children
13384 var cs = node.attributes.children;
13385 for(var i = 0, len = cs.length; i < len; i++){
13386 node.appendChild(this.createNode(cs[i]));
13388 if(typeof callback == "function"){
13391 }else if(this.dataUrl){
13392 this.requestData(node, callback);
13396 getParams: function(node){
13397 var buf = [], bp = this.baseParams;
13398 for(var key in bp){
13399 if(typeof bp[key] != "function"){
13400 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13403 var n = this.queryParam === false ? 'node' : this.queryParam;
13404 buf.push(n + "=", encodeURIComponent(node.id));
13405 return buf.join("");
13408 requestData : function(node, callback){
13409 if(this.fireEvent("beforeload", this, node, callback) !== false){
13410 this.transId = Roo.Ajax.request({
13411 method:this.requestMethod,
13412 url: this.dataUrl||this.url,
13413 success: this.handleResponse,
13414 failure: this.handleFailure,
13416 argument: {callback: callback, node: node},
13417 params: this.getParams(node)
13420 // if the load is cancelled, make sure we notify
13421 // the node that we are done
13422 if(typeof callback == "function"){
13428 isLoading : function(){
13429 return this.transId ? true : false;
13432 abort : function(){
13433 if(this.isLoading()){
13434 Roo.Ajax.abort(this.transId);
13439 createNode : function(attr)
13441 // apply baseAttrs, nice idea Corey!
13442 if(this.baseAttrs){
13443 Roo.applyIf(attr, this.baseAttrs);
13445 if(this.applyLoader !== false){
13446 attr.loader = this;
13448 // uiProvider = depreciated..
13450 if(typeof(attr.uiProvider) == 'string'){
13451 attr.uiProvider = this.uiProviders[attr.uiProvider] ||
13452 /** eval:var:attr */ eval(attr.uiProvider);
13454 if(typeof(this.uiProviders['default']) != 'undefined') {
13455 attr.uiProvider = this.uiProviders['default'];
13458 this.fireEvent('create', this, attr);
13460 attr.leaf = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13462 new Roo.tree.TreeNode(attr) :
13463 new Roo.tree.AsyncTreeNode(attr));
13466 processResponse : function(response, node, callback)
13468 var json = response.responseText;
13471 var o = Roo.decode(json);
13473 if (this.root === false && typeof(o.success) != undefined) {
13474 this.root = 'data'; // the default behaviour for list like data..
13477 if (this.root !== false && !o.success) {
13478 // it's a failure condition.
13479 var a = response.argument;
13480 this.fireEvent("loadexception", this, a.node, response);
13481 Roo.log("Load failed - should have a handler really");
13487 if (this.root !== false) {
13491 for(var i = 0, len = o.length; i < len; i++){
13492 var n = this.createNode(o[i]);
13494 node.appendChild(n);
13497 if(typeof callback == "function"){
13498 callback(this, node);
13501 this.handleFailure(response);
13505 handleResponse : function(response){
13506 this.transId = false;
13507 var a = response.argument;
13508 this.processResponse(response, a.node, a.callback);
13509 this.fireEvent("load", this, a.node, response);
13512 handleFailure : function(response)
13514 // should handle failure better..
13515 this.transId = false;
13516 var a = response.argument;
13517 this.fireEvent("loadexception", this, a.node, response);
13518 if(typeof a.callback == "function"){
13519 a.callback(this, a.node);
13524 * Ext JS Library 1.1.1
13525 * Copyright(c) 2006-2007, Ext JS, LLC.
13527 * Originally Released Under LGPL - original licence link has changed is not relivant.
13530 * <script type="text/javascript">
13534 * @class Roo.tree.TreeFilter
13535 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13536 * @param {TreePanel} tree
13537 * @param {Object} config (optional)
13539 Roo.tree.TreeFilter = function(tree, config){
13541 this.filtered = {};
13542 Roo.apply(this, config);
13545 Roo.tree.TreeFilter.prototype = {
13552 * Filter the data by a specific attribute.
13553 * @param {String/RegExp} value Either string that the attribute value
13554 * should start with or a RegExp to test against the attribute
13555 * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13556 * @param {TreeNode} startNode (optional) The node to start the filter at.
13558 filter : function(value, attr, startNode){
13559 attr = attr || "text";
13561 if(typeof value == "string"){
13562 var vlen = value.length;
13563 // auto clear empty filter
13564 if(vlen == 0 && this.clearBlank){
13568 value = value.toLowerCase();
13570 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13572 }else if(value.exec){ // regex?
13574 return value.test(n.attributes[attr]);
13577 throw 'Illegal filter type, must be string or regex';
13579 this.filterBy(f, null, startNode);
13583 * Filter by a function. The passed function will be called with each
13584 * node in the tree (or from the startNode). If the function returns true, the node is kept
13585 * otherwise it is filtered. If a node is filtered, its children are also filtered.
13586 * @param {Function} fn The filter function
13587 * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13589 filterBy : function(fn, scope, startNode){
13590 startNode = startNode || this.tree.root;
13591 if(this.autoClear){
13594 var af = this.filtered, rv = this.reverse;
13595 var f = function(n){
13596 if(n == startNode){
13602 var m = fn.call(scope || n, n);
13610 startNode.cascade(f);
13613 if(typeof id != "function"){
13615 if(n && n.parentNode){
13616 n.parentNode.removeChild(n);
13624 * Clears the current filter. Note: with the "remove" option
13625 * set a filter cannot be cleared.
13627 clear : function(){
13629 var af = this.filtered;
13631 if(typeof id != "function"){
13638 this.filtered = {};
13643 * Ext JS Library 1.1.1
13644 * Copyright(c) 2006-2007, Ext JS, LLC.
13646 * Originally Released Under LGPL - original licence link has changed is not relivant.
13649 * <script type="text/javascript">
13654 * @class Roo.tree.TreeSorter
13655 * Provides sorting of nodes in a TreePanel
13657 * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13658 * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13659 * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13660 * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13661 * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13662 * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13664 * @param {TreePanel} tree
13665 * @param {Object} config
13667 Roo.tree.TreeSorter = function(tree, config){
13668 Roo.apply(this, config);
13669 tree.on("beforechildrenrendered", this.doSort, this);
13670 tree.on("append", this.updateSort, this);
13671 tree.on("insert", this.updateSort, this);
13673 var dsc = this.dir && this.dir.toLowerCase() == "desc";
13674 var p = this.property || "text";
13675 var sortType = this.sortType;
13676 var fs = this.folderSort;
13677 var cs = this.caseSensitive === true;
13678 var leafAttr = this.leafAttr || 'leaf';
13680 this.sortFn = function(n1, n2){
13682 if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13685 if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13689 var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13690 var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13692 return dsc ? +1 : -1;
13694 return dsc ? -1 : +1;
13701 Roo.tree.TreeSorter.prototype = {
13702 doSort : function(node){
13703 node.sort(this.sortFn);
13706 compareNodes : function(n1, n2){
13707 return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13710 updateSort : function(tree, node){
13711 if(node.childrenRendered){
13712 this.doSort.defer(1, this, [node]);
13717 * Ext JS Library 1.1.1
13718 * Copyright(c) 2006-2007, Ext JS, LLC.
13720 * Originally Released Under LGPL - original licence link has changed is not relivant.
13723 * <script type="text/javascript">
13726 if(Roo.dd.DropZone){
13728 Roo.tree.TreeDropZone = function(tree, config){
13729 this.allowParentInsert = false;
13730 this.allowContainerDrop = false;
13731 this.appendOnly = false;
13732 Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13734 this.lastInsertClass = "x-tree-no-status";
13735 this.dragOverData = {};
13738 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13739 ddGroup : "TreeDD",
13742 expandDelay : 1000,
13744 expandNode : function(node){
13745 if(node.hasChildNodes() && !node.isExpanded()){
13746 node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13750 queueExpand : function(node){
13751 this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13754 cancelExpand : function(){
13755 if(this.expandProcId){
13756 clearTimeout(this.expandProcId);
13757 this.expandProcId = false;
13761 isValidDropPoint : function(n, pt, dd, e, data){
13762 if(!n || !data){ return false; }
13763 var targetNode = n.node;
13764 var dropNode = data.node;
13765 // default drop rules
13766 if(!(targetNode && targetNode.isTarget && pt)){
13769 if(pt == "append" && targetNode.allowChildren === false){
13772 if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13775 if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13778 // reuse the object
13779 var overEvent = this.dragOverData;
13780 overEvent.tree = this.tree;
13781 overEvent.target = targetNode;
13782 overEvent.data = data;
13783 overEvent.point = pt;
13784 overEvent.source = dd;
13785 overEvent.rawEvent = e;
13786 overEvent.dropNode = dropNode;
13787 overEvent.cancel = false;
13788 var result = this.tree.fireEvent("nodedragover", overEvent);
13789 return overEvent.cancel === false && result !== false;
13792 getDropPoint : function(e, n, dd)
13796 return tn.allowChildren !== false ? "append" : false; // always append for root
13798 var dragEl = n.ddel;
13799 var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13800 var y = Roo.lib.Event.getPageY(e);
13801 //var noAppend = tn.allowChildren === false || tn.isLeaf();
13803 // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13804 var noAppend = tn.allowChildren === false;
13805 if(this.appendOnly || tn.parentNode.allowChildren === false){
13806 return noAppend ? false : "append";
13808 var noBelow = false;
13809 if(!this.allowParentInsert){
13810 noBelow = tn.hasChildNodes() && tn.isExpanded();
13812 var q = (b - t) / (noAppend ? 2 : 3);
13813 if(y >= t && y < (t + q)){
13815 }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13822 onNodeEnter : function(n, dd, e, data)
13824 this.cancelExpand();
13827 onNodeOver : function(n, dd, e, data)
13830 var pt = this.getDropPoint(e, n, dd);
13833 // auto node expand check
13834 if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13835 this.queueExpand(node);
13836 }else if(pt != "append"){
13837 this.cancelExpand();
13840 // set the insert point style on the target node
13841 var returnCls = this.dropNotAllowed;
13842 if(this.isValidDropPoint(n, pt, dd, e, data)){
13847 returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13848 cls = "x-tree-drag-insert-above";
13849 }else if(pt == "below"){
13850 returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13851 cls = "x-tree-drag-insert-below";
13853 returnCls = "x-tree-drop-ok-append";
13854 cls = "x-tree-drag-append";
13856 if(this.lastInsertClass != cls){
13857 Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13858 this.lastInsertClass = cls;
13865 onNodeOut : function(n, dd, e, data){
13867 this.cancelExpand();
13868 this.removeDropIndicators(n);
13871 onNodeDrop : function(n, dd, e, data){
13872 var point = this.getDropPoint(e, n, dd);
13873 var targetNode = n.node;
13874 targetNode.ui.startDrop();
13875 if(!this.isValidDropPoint(n, point, dd, e, data)){
13876 targetNode.ui.endDrop();
13879 // first try to find the drop node
13880 var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13883 target: targetNode,
13888 dropNode: dropNode,
13891 var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13892 if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13893 targetNode.ui.endDrop();
13896 // allow target changing
13897 targetNode = dropEvent.target;
13898 if(point == "append" && !targetNode.isExpanded()){
13899 targetNode.expand(false, null, function(){
13900 this.completeDrop(dropEvent);
13901 }.createDelegate(this));
13903 this.completeDrop(dropEvent);
13908 completeDrop : function(de){
13909 var ns = de.dropNode, p = de.point, t = de.target;
13910 if(!(ns instanceof Array)){
13914 for(var i = 0, len = ns.length; i < len; i++){
13917 t.parentNode.insertBefore(n, t);
13918 }else if(p == "below"){
13919 t.parentNode.insertBefore(n, t.nextSibling);
13925 if(this.tree.hlDrop){
13929 this.tree.fireEvent("nodedrop", de);
13932 afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13933 if(this.tree.hlDrop){
13934 dropNode.ui.focus();
13935 dropNode.ui.highlight();
13937 this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13940 getTree : function(){
13944 removeDropIndicators : function(n){
13947 Roo.fly(el).removeClass([
13948 "x-tree-drag-insert-above",
13949 "x-tree-drag-insert-below",
13950 "x-tree-drag-append"]);
13951 this.lastInsertClass = "_noclass";
13955 beforeDragDrop : function(target, e, id){
13956 this.cancelExpand();
13960 afterRepair : function(data){
13961 if(data && Roo.enableFx){
13962 data.node.ui.highlight();
13972 * Ext JS Library 1.1.1
13973 * Copyright(c) 2006-2007, Ext JS, LLC.
13975 * Originally Released Under LGPL - original licence link has changed is not relivant.
13978 * <script type="text/javascript">
13982 if(Roo.dd.DragZone){
13983 Roo.tree.TreeDragZone = function(tree, config){
13984 Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13988 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13989 ddGroup : "TreeDD",
13991 onBeforeDrag : function(data, e){
13993 return n && n.draggable && !n.disabled;
13997 onInitDrag : function(e){
13998 var data = this.dragData;
13999 this.tree.getSelectionModel().select(data.node);
14000 this.proxy.update("");
14001 data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14002 this.tree.fireEvent("startdrag", this.tree, data.node, e);
14005 getRepairXY : function(e, data){
14006 return data.node.ui.getDDRepairXY();
14009 onEndDrag : function(data, e){
14010 this.tree.fireEvent("enddrag", this.tree, data.node, e);
14015 onValidDrop : function(dd, e, id){
14016 this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14020 beforeInvalidDrop : function(e, id){
14021 // this scrolls the original position back into view
14022 var sm = this.tree.getSelectionModel();
14023 sm.clearSelections();
14024 sm.select(this.dragData.node);
14029 * Ext JS Library 1.1.1
14030 * Copyright(c) 2006-2007, Ext JS, LLC.
14032 * Originally Released Under LGPL - original licence link has changed is not relivant.
14035 * <script type="text/javascript">
14038 * @class Roo.tree.TreeEditor
14039 * @extends Roo.Editor
14040 * Provides editor functionality for inline tree node editing. Any valid {@link Roo.form.Field} can be used
14041 * as the editor field.
14043 * @param {Object} config (used to be the tree panel.)
14044 * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14046 * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14047 * @cfg {Roo.form.TextField} field [required] The field configuration
14051 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14054 if (oldconfig) { // old style..
14055 field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14058 tree = config.tree;
14059 config.field = config.field || {};
14060 config.field.xtype = 'TextField';
14061 field = Roo.factory(config.field, Roo.form);
14063 config = config || {};
14068 * @event beforenodeedit
14069 * Fires when editing is initiated, but before the value changes. Editing can be canceled by returning
14070 * false from the handler of this event.
14071 * @param {Editor} this
14072 * @param {Roo.tree.Node} node
14074 "beforenodeedit" : true
14078 Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14082 tree.on('beforeclick', this.beforeNodeClick, this);
14083 tree.getTreeEl().on('mousedown', this.hide, this);
14084 this.on('complete', this.updateNode, this);
14085 this.on('beforestartedit', this.fitToTree, this);
14086 this.on('startedit', this.bindScroll, this, {delay:10});
14087 this.on('specialkey', this.onSpecialKey, this);
14090 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14092 * @cfg {String} alignment
14093 * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14099 * @cfg {Boolean} hideEl
14100 * True to hide the bound element while the editor is displayed (defaults to false)
14104 * @cfg {String} cls
14105 * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14107 cls: "x-small-editor x-tree-editor",
14109 * @cfg {Boolean} shim
14110 * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14116 * @cfg {Number} maxWidth
14117 * The maximum width in pixels of the editor field (defaults to 250). Note that if the maxWidth would exceed
14118 * the containing tree element's size, it will be automatically limited for you to the container width, taking
14119 * scroll and client offsets into account prior to each edit.
14126 fitToTree : function(ed, el){
14127 var td = this.tree.getTreeEl().dom, nd = el.dom;
14128 if(td.scrollLeft > nd.offsetLeft){ // ensure the node left point is visible
14129 td.scrollLeft = nd.offsetLeft;
14133 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14134 this.setSize(w, '');
14136 return this.fireEvent('beforenodeedit', this, this.editNode);
14141 triggerEdit : function(node){
14142 this.completeEdit();
14143 this.editNode = node;
14144 this.startEdit(node.ui.textNode, node.text);
14148 bindScroll : function(){
14149 this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14153 beforeNodeClick : function(node, e){
14154 var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14155 this.lastClick = new Date();
14156 if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14158 this.triggerEdit(node);
14165 updateNode : function(ed, value){
14166 this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14167 this.editNode.setText(value);
14171 onHide : function(){
14172 Roo.tree.TreeEditor.superclass.onHide.call(this);
14174 this.editNode.ui.focus();
14179 onSpecialKey : function(field, e){
14180 var k = e.getKey();
14184 }else if(k == e.ENTER && !e.hasModifier()){
14186 this.completeEdit();
14189 });//<Script type="text/javascript">
14192 * Ext JS Library 1.1.1
14193 * Copyright(c) 2006-2007, Ext JS, LLC.
14195 * Originally Released Under LGPL - original licence link has changed is not relivant.
14198 * <script type="text/javascript">
14202 * Not documented??? - probably should be...
14205 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14206 //focus: Roo.emptyFn, // prevent odd scrolling behavior
14208 renderElements : function(n, a, targetNode, bulkRender){
14209 //consel.log("renderElements?");
14210 this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14212 var t = n.getOwnerTree();
14213 var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14215 var cols = t.columns;
14216 var bw = t.borderWidth;
14218 var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14219 var cb = typeof a.checked == "boolean";
14220 var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14221 var colcls = 'x-t-' + tid + '-c0';
14223 '<li class="x-tree-node">',
14226 '<div class="x-tree-node-el ', a.cls,'">',
14228 '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14231 '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14232 '<img src="', this.emptyIcon, '" class="x-tree-ec-icon " />',
14233 '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14234 (a.icon ? ' x-tree-node-inline-icon' : ''),
14235 (a.iconCls ? ' '+a.iconCls : ''),
14236 '" unselectable="on" />',
14237 (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' +
14238 (a.checked ? 'checked="checked" />' : ' />')) : ''),
14240 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14241 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14242 '<span unselectable="on" qtip="' + tx + '">',
14246 '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14247 (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14249 for(var i = 1, len = cols.length; i < len; i++){
14251 colcls = 'x-t-' + tid + '-c' +i;
14252 tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14253 buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14254 '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14260 '<div class="x-clear"></div></div>',
14261 '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14264 if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14265 this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14266 n.nextSibling.ui.getEl(), buf.join(""));
14268 this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14270 var el = this.wrap.firstChild;
14272 this.elNode = el.firstChild;
14273 this.ranchor = el.childNodes[1];
14274 this.ctNode = this.wrap.childNodes[1];
14275 var cs = el.firstChild.childNodes;
14276 this.indentNode = cs[0];
14277 this.ecNode = cs[1];
14278 this.iconNode = cs[2];
14281 this.checkbox = cs[3];
14284 this.anchor = cs[index];
14286 this.textNode = cs[index].firstChild;
14288 //el.on("click", this.onClick, this);
14289 //el.on("dblclick", this.onDblClick, this);
14292 // console.log(this);
14294 initEvents : function(){
14295 Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14298 var a = this.ranchor;
14300 var el = Roo.get(a);
14302 if(Roo.isOpera){ // opera render bug ignores the CSS
14303 el.setStyle("text-decoration", "none");
14306 el.on("click", this.onClick, this);
14307 el.on("dblclick", this.onDblClick, this);
14308 el.on("contextmenu", this.onContextMenu, this);
14312 /*onSelectedChange : function(state){
14315 this.addClass("x-tree-selected");
14318 this.removeClass("x-tree-selected");
14321 addClass : function(cls){
14323 Roo.fly(this.elRow).addClass(cls);
14329 removeClass : function(cls){
14331 Roo.fly(this.elRow).removeClass(cls);
14337 });//<Script type="text/javascript">
14341 * Ext JS Library 1.1.1
14342 * Copyright(c) 2006-2007, Ext JS, LLC.
14344 * Originally Released Under LGPL - original licence link has changed is not relivant.
14347 * <script type="text/javascript">
14352 * @class Roo.tree.ColumnTree
14353 * @extends Roo.tree.TreePanel
14354 * @cfg {Object} columns Including width, header, renderer, cls, dataIndex
14355 * @cfg {int} borderWidth compined right/left border allowance
14357 * @param {String/HTMLElement/Element} el The container element
14358 * @param {Object} config
14360 Roo.tree.ColumnTree = function(el, config)
14362 Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14366 * Fire this event on a container when it resizes
14367 * @param {int} w Width
14368 * @param {int} h Height
14372 this.on('resize', this.onResize, this);
14375 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14379 borderWidth: Roo.isBorderBox ? 0 : 2,
14382 render : function(){
14383 // add the header.....
14385 Roo.tree.ColumnTree.superclass.render.apply(this);
14387 this.el.addClass('x-column-tree');
14389 this.headers = this.el.createChild(
14390 {cls:'x-tree-headers'},this.innerCt.dom);
14392 var cols = this.columns, c;
14393 var totalWidth = 0;
14395 var len = cols.length;
14396 for(var i = 0; i < len; i++){
14398 totalWidth += c.width;
14399 this.headEls.push(this.headers.createChild({
14400 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14402 cls:'x-tree-hd-text',
14405 style:'width:'+(c.width-this.borderWidth)+'px;'
14408 this.headers.createChild({cls:'x-clear'});
14409 // prevent floats from wrapping when clipped
14410 this.headers.setWidth(totalWidth);
14411 //this.innerCt.setWidth(totalWidth);
14412 this.innerCt.setStyle({ overflow: 'auto' });
14413 this.onResize(this.width, this.height);
14417 onResize : function(w,h)
14422 this.innerCt.setWidth(this.width);
14423 this.innerCt.setHeight(this.height-20);
14426 var cols = this.columns, c;
14427 var totalWidth = 0;
14429 var len = cols.length;
14430 for(var i = 0; i < len; i++){
14432 if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14433 // it's the expander..
14434 expEl = this.headEls[i];
14437 totalWidth += c.width;
14441 expEl.setWidth( ((w - totalWidth)-this.borderWidth - 20));
14443 this.headers.setWidth(w-20);
14452 * Ext JS Library 1.1.1
14453 * Copyright(c) 2006-2007, Ext JS, LLC.
14455 * Originally Released Under LGPL - original licence link has changed is not relivant.
14458 * <script type="text/javascript">
14462 * @class Roo.menu.Menu
14463 * @extends Roo.util.Observable
14464 * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
14465 * A menu object. This is the container to which you add all other menu items. Menu can also serve a as a base class
14466 * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14468 * Creates a new Menu
14469 * @param {Object} config Configuration options
14471 Roo.menu.Menu = function(config){
14473 Roo.menu.Menu.superclass.constructor.call(this, config);
14475 this.id = this.id || Roo.id();
14478 * @event beforeshow
14479 * Fires before this menu is displayed
14480 * @param {Roo.menu.Menu} this
14484 * @event beforehide
14485 * Fires before this menu is hidden
14486 * @param {Roo.menu.Menu} this
14491 * Fires after this menu is displayed
14492 * @param {Roo.menu.Menu} this
14497 * Fires after this menu is hidden
14498 * @param {Roo.menu.Menu} this
14503 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14504 * @param {Roo.menu.Menu} this
14505 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14506 * @param {Roo.EventObject} e
14511 * Fires when the mouse is hovering over this menu
14512 * @param {Roo.menu.Menu} this
14513 * @param {Roo.EventObject} e
14514 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14519 * Fires when the mouse exits this menu
14520 * @param {Roo.menu.Menu} this
14521 * @param {Roo.EventObject} e
14522 * @param {Roo.menu.Item} menuItem The menu item that was clicked
14527 * Fires when a menu item contained in this menu is clicked
14528 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14529 * @param {Roo.EventObject} e
14533 if (this.registerMenu) {
14534 Roo.menu.MenuMgr.register(this);
14537 var mis = this.items;
14538 this.items = new Roo.util.MixedCollection();
14540 this.add.apply(this, mis);
14544 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14546 * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14550 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14551 * for bottom-right shadow (defaults to "sides")
14555 * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14556 * this menu (defaults to "tl-tr?")
14558 subMenuAlign : "tl-tr?",
14560 * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14561 * relative to its element of origin (defaults to "tl-bl?")
14563 defaultAlign : "tl-bl?",
14565 * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14567 allowOtherMenus : false,
14569 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14571 registerMenu : true,
14576 render : function(){
14580 var el = this.el = new Roo.Layer({
14582 shadow:this.shadow,
14584 parentEl: this.parentEl || document.body,
14588 this.keyNav = new Roo.menu.MenuNav(this);
14591 el.addClass("x-menu-plain");
14594 el.addClass(this.cls);
14596 // generic focus element
14597 this.focusEl = el.createChild({
14598 tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14600 var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14601 //disabling touch- as it's causing issues ..
14602 //ul.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
14603 ul.on('click' , this.onClick, this);
14606 ul.on("mouseover", this.onMouseOver, this);
14607 ul.on("mouseout", this.onMouseOut, this);
14608 this.items.each(function(item){
14613 var li = document.createElement("li");
14614 li.className = "x-menu-list-item";
14615 ul.dom.appendChild(li);
14616 item.render(li, this);
14623 autoWidth : function(){
14624 var el = this.el, ul = this.ul;
14628 var w = this.width;
14631 }else if(Roo.isIE){
14632 el.setWidth(this.minWidth);
14633 var t = el.dom.offsetWidth; // force recalc
14634 el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14639 delayAutoWidth : function(){
14642 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14644 this.awTask.delay(20);
14649 findTargetItem : function(e){
14650 var t = e.getTarget(".x-menu-list-item", this.ul, true);
14651 if(t && t.menuItemId){
14652 return this.items.get(t.menuItemId);
14657 onClick : function(e){
14658 Roo.log("menu.onClick");
14659 var t = this.findTargetItem(e);
14664 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
14665 if(t == this.activeItem && t.shouldDeactivate(e)){
14666 this.activeItem.deactivate();
14667 delete this.activeItem;
14671 this.setActiveItem(t, true);
14679 this.fireEvent("click", this, t, e);
14683 setActiveItem : function(item, autoExpand){
14684 if(item != this.activeItem){
14685 if(this.activeItem){
14686 this.activeItem.deactivate();
14688 this.activeItem = item;
14689 item.activate(autoExpand);
14690 }else if(autoExpand){
14696 tryActivate : function(start, step){
14697 var items = this.items;
14698 for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14699 var item = items.get(i);
14700 if(!item.disabled && item.canActivate){
14701 this.setActiveItem(item, false);
14709 onMouseOver : function(e){
14711 if(t = this.findTargetItem(e)){
14712 if(t.canActivate && !t.disabled){
14713 this.setActiveItem(t, true);
14716 this.fireEvent("mouseover", this, e, t);
14720 onMouseOut : function(e){
14722 if(t = this.findTargetItem(e)){
14723 if(t == this.activeItem && t.shouldDeactivate(e)){
14724 this.activeItem.deactivate();
14725 delete this.activeItem;
14728 this.fireEvent("mouseout", this, e, t);
14732 * Read-only. Returns true if the menu is currently displayed, else false.
14735 isVisible : function(){
14736 return this.el && !this.hidden;
14740 * Displays this menu relative to another element
14741 * @param {String/HTMLElement/Roo.Element} element The element to align to
14742 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14743 * the element (defaults to this.defaultAlign)
14744 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14746 show : function(el, pos, parentMenu){
14747 this.parentMenu = parentMenu;
14751 this.fireEvent("beforeshow", this);
14752 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14756 * Displays this menu at a specific xy position
14757 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14758 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14760 showAt : function(xy, parentMenu, /* private: */_e){
14761 this.parentMenu = parentMenu;
14766 this.fireEvent("beforeshow", this);
14767 xy = this.el.adjustForConstraints(xy);
14771 this.hidden = false;
14773 this.fireEvent("show", this);
14776 focus : function(){
14778 this.doFocus.defer(50, this);
14782 doFocus : function(){
14784 this.focusEl.focus();
14789 * Hides this menu and optionally all parent menus
14790 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14792 hide : function(deep){
14793 if(this.el && this.isVisible()){
14794 this.fireEvent("beforehide", this);
14795 if(this.activeItem){
14796 this.activeItem.deactivate();
14797 this.activeItem = null;
14800 this.hidden = true;
14801 this.fireEvent("hide", this);
14803 if(deep === true && this.parentMenu){
14804 this.parentMenu.hide(true);
14809 * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14810 * Any of the following are valid:
14812 * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14813 * <li>An HTMLElement object which will be converted to a menu item</li>
14814 * <li>A menu item config object that will be created as a new menu item</li>
14815 * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14816 * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14821 var menu = new Roo.menu.Menu();
14823 // Create a menu item to add by reference
14824 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14826 // Add a bunch of items at once using different methods.
14827 // Only the last item added will be returned.
14828 var item = menu.add(
14829 menuItem, // add existing item by ref
14830 'Dynamic Item', // new TextItem
14831 '-', // new separator
14832 { text: 'Config Item' } // new item by config
14835 * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14836 * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14839 var a = arguments, l = a.length, item;
14840 for(var i = 0; i < l; i++){
14842 if ((typeof(el) == "object") && el.xtype && el.xns) {
14843 el = Roo.factory(el, Roo.menu);
14846 if(el.render){ // some kind of Item
14847 item = this.addItem(el);
14848 }else if(typeof el == "string"){ // string
14849 if(el == "separator" || el == "-"){
14850 item = this.addSeparator();
14852 item = this.addText(el);
14854 }else if(el.tagName || el.el){ // element
14855 item = this.addElement(el);
14856 }else if(typeof el == "object"){ // must be menu item config?
14857 item = this.addMenuItem(el);
14864 * Returns this menu's underlying {@link Roo.Element} object
14865 * @return {Roo.Element} The element
14867 getEl : function(){
14875 * Adds a separator bar to the menu
14876 * @return {Roo.menu.Item} The menu item that was added
14878 addSeparator : function(){
14879 return this.addItem(new Roo.menu.Separator());
14883 * Adds an {@link Roo.Element} object to the menu
14884 * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14885 * @return {Roo.menu.Item} The menu item that was added
14887 addElement : function(el){
14888 return this.addItem(new Roo.menu.BaseItem(el));
14892 * Adds an existing object based on {@link Roo.menu.Item} to the menu
14893 * @param {Roo.menu.Item} item The menu item to add
14894 * @return {Roo.menu.Item} The menu item that was added
14896 addItem : function(item){
14897 this.items.add(item);
14899 var li = document.createElement("li");
14900 li.className = "x-menu-list-item";
14901 this.ul.dom.appendChild(li);
14902 item.render(li, this);
14903 this.delayAutoWidth();
14909 * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14910 * @param {Object} config A MenuItem config object
14911 * @return {Roo.menu.Item} The menu item that was added
14913 addMenuItem : function(config){
14914 if(!(config instanceof Roo.menu.Item)){
14915 if(typeof config.checked == "boolean"){ // must be check menu item config?
14916 config = new Roo.menu.CheckItem(config);
14918 config = new Roo.menu.Item(config);
14921 return this.addItem(config);
14925 * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14926 * @param {String} text The text to display in the menu item
14927 * @return {Roo.menu.Item} The menu item that was added
14929 addText : function(text){
14930 return this.addItem(new Roo.menu.TextItem({ text : text }));
14934 * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14935 * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14936 * @param {Roo.menu.Item} item The menu item to add
14937 * @return {Roo.menu.Item} The menu item that was added
14939 insert : function(index, item){
14940 this.items.insert(index, item);
14942 var li = document.createElement("li");
14943 li.className = "x-menu-list-item";
14944 this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14945 item.render(li, this);
14946 this.delayAutoWidth();
14952 * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14953 * @param {Roo.menu.Item} item The menu item to remove
14955 remove : function(item){
14956 this.items.removeKey(item.id);
14961 * Removes and destroys all items in the menu
14963 removeAll : function(){
14965 while(f = this.items.first()){
14971 // MenuNav is a private utility class used internally by the Menu
14972 Roo.menu.MenuNav = function(menu){
14973 Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14974 this.scope = this.menu = menu;
14977 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14978 doRelay : function(e, h){
14979 var k = e.getKey();
14980 if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14981 this.menu.tryActivate(0, 1);
14984 return h.call(this.scope || this, e, this.menu);
14987 up : function(e, m){
14988 if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14989 m.tryActivate(m.items.length-1, -1);
14993 down : function(e, m){
14994 if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14995 m.tryActivate(0, 1);
14999 right : function(e, m){
15001 m.activeItem.expandMenu(true);
15005 left : function(e, m){
15007 if(m.parentMenu && m.parentMenu.activeItem){
15008 m.parentMenu.activeItem.activate();
15012 enter : function(e, m){
15014 e.stopPropagation();
15015 m.activeItem.onClick(e);
15016 m.fireEvent("click", this, m.activeItem);
15022 * Ext JS Library 1.1.1
15023 * Copyright(c) 2006-2007, Ext JS, LLC.
15025 * Originally Released Under LGPL - original licence link has changed is not relivant.
15028 * <script type="text/javascript">
15032 * @class Roo.menu.MenuMgr
15033 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15036 Roo.menu.MenuMgr = function(){
15037 var menus, active, groups = {}, attached = false, lastShow = new Date();
15039 // private - called when first menu is created
15042 active = new Roo.util.MixedCollection();
15043 Roo.get(document).addKeyListener(27, function(){
15044 if(active.length > 0){
15051 function hideAll(){
15052 if(active && active.length > 0){
15053 var c = active.clone();
15054 c.each(function(m){
15061 function onHide(m){
15063 if(active.length < 1){
15064 Roo.get(document).un("mousedown", onMouseDown);
15070 function onShow(m){
15071 var last = active.last();
15072 lastShow = new Date();
15075 Roo.get(document).on("mousedown", onMouseDown);
15079 m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15080 m.parentMenu.activeChild = m;
15081 }else if(last && last.isVisible()){
15082 m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15087 function onBeforeHide(m){
15089 m.activeChild.hide();
15091 if(m.autoHideTimer){
15092 clearTimeout(m.autoHideTimer);
15093 delete m.autoHideTimer;
15098 function onBeforeShow(m){
15099 var pm = m.parentMenu;
15100 if(!pm && !m.allowOtherMenus){
15102 }else if(pm && pm.activeChild && active != m){
15103 pm.activeChild.hide();
15108 function onMouseDown(e){
15109 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15115 function onBeforeCheck(mi, state){
15117 var g = groups[mi.group];
15118 for(var i = 0, l = g.length; i < l; i++){
15120 g[i].setChecked(false);
15129 * Hides all menus that are currently visible
15131 hideAll : function(){
15136 register : function(menu){
15140 menus[menu.id] = menu;
15141 menu.on("beforehide", onBeforeHide);
15142 menu.on("hide", onHide);
15143 menu.on("beforeshow", onBeforeShow);
15144 menu.on("show", onShow);
15145 var g = menu.group;
15146 if(g && menu.events["checkchange"]){
15150 groups[g].push(menu);
15151 menu.on("checkchange", onCheck);
15156 * Returns a {@link Roo.menu.Menu} object
15157 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15158 * be used to generate and return a new Menu instance.
15160 get : function(menu){
15161 if(typeof menu == "string"){ // menu id
15162 return menus[menu];
15163 }else if(menu.events){ // menu instance
15165 }else if(typeof menu.length == 'number'){ // array of menu items?
15166 return new Roo.menu.Menu({items:menu});
15167 }else{ // otherwise, must be a config
15168 return new Roo.menu.Menu(menu);
15173 unregister : function(menu){
15174 delete menus[menu.id];
15175 menu.un("beforehide", onBeforeHide);
15176 menu.un("hide", onHide);
15177 menu.un("beforeshow", onBeforeShow);
15178 menu.un("show", onShow);
15179 var g = menu.group;
15180 if(g && menu.events["checkchange"]){
15181 groups[g].remove(menu);
15182 menu.un("checkchange", onCheck);
15187 registerCheckable : function(menuItem){
15188 var g = menuItem.group;
15193 groups[g].push(menuItem);
15194 menuItem.on("beforecheckchange", onBeforeCheck);
15199 unregisterCheckable : function(menuItem){
15200 var g = menuItem.group;
15202 groups[g].remove(menuItem);
15203 menuItem.un("beforecheckchange", onBeforeCheck);
15209 * Ext JS Library 1.1.1
15210 * Copyright(c) 2006-2007, Ext JS, LLC.
15212 * Originally Released Under LGPL - original licence link has changed is not relivant.
15215 * <script type="text/javascript">
15220 * @class Roo.menu.BaseItem
15221 * @extends Roo.Component
15223 * The base class for all items that render into menus. BaseItem provides default rendering, activated state
15224 * management and base configuration options shared by all menu components.
15226 * Creates a new BaseItem
15227 * @param {Object} config Configuration options
15229 Roo.menu.BaseItem = function(config){
15230 Roo.menu.BaseItem.superclass.constructor.call(this, config);
15235 * Fires when this item is clicked
15236 * @param {Roo.menu.BaseItem} this
15237 * @param {Roo.EventObject} e
15242 * Fires when this item is activated
15243 * @param {Roo.menu.BaseItem} this
15247 * @event deactivate
15248 * Fires when this item is deactivated
15249 * @param {Roo.menu.BaseItem} this
15255 this.on("click", this.handler, this.scope, true);
15259 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15261 * @cfg {Function} handler
15262 * A function that will handle the click event of this menu item (defaults to undefined)
15265 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15267 canActivate : false,
15270 * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15275 * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15277 activeClass : "x-menu-item-active",
15279 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15281 hideOnClick : true,
15283 * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15288 ctype: "Roo.menu.BaseItem",
15291 actionMode : "container",
15294 render : function(container, parentMenu){
15295 this.parentMenu = parentMenu;
15296 Roo.menu.BaseItem.superclass.render.call(this, container);
15297 this.container.menuItemId = this.id;
15301 onRender : function(container, position){
15302 this.el = Roo.get(this.el);
15303 container.dom.appendChild(this.el.dom);
15307 onClick : function(e){
15308 if(!this.disabled && this.fireEvent("click", this, e) !== false
15309 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15310 this.handleClick(e);
15317 activate : function(){
15321 var li = this.container;
15322 li.addClass(this.activeClass);
15323 this.region = li.getRegion().adjust(2, 2, -2, -2);
15324 this.fireEvent("activate", this);
15329 deactivate : function(){
15330 this.container.removeClass(this.activeClass);
15331 this.fireEvent("deactivate", this);
15335 shouldDeactivate : function(e){
15336 return !this.region || !this.region.contains(e.getPoint());
15340 handleClick : function(e){
15341 if(this.hideOnClick){
15342 this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15347 expandMenu : function(autoActivate){
15352 hideMenu : function(){
15357 * Ext JS Library 1.1.1
15358 * Copyright(c) 2006-2007, Ext JS, LLC.
15360 * Originally Released Under LGPL - original licence link has changed is not relivant.
15363 * <script type="text/javascript">
15367 * @class Roo.menu.Adapter
15368 * @extends Roo.menu.BaseItem
15370 * 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.
15371 * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15373 * Creates a new Adapter
15374 * @param {Object} config Configuration options
15376 Roo.menu.Adapter = function(component, config){
15377 Roo.menu.Adapter.superclass.constructor.call(this, config);
15378 this.component = component;
15380 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15382 canActivate : true,
15385 onRender : function(container, position){
15386 this.component.render(container);
15387 this.el = this.component.getEl();
15391 activate : function(){
15395 this.component.focus();
15396 this.fireEvent("activate", this);
15401 deactivate : function(){
15402 this.fireEvent("deactivate", this);
15406 disable : function(){
15407 this.component.disable();
15408 Roo.menu.Adapter.superclass.disable.call(this);
15412 enable : function(){
15413 this.component.enable();
15414 Roo.menu.Adapter.superclass.enable.call(this);
15418 * Ext JS Library 1.1.1
15419 * Copyright(c) 2006-2007, Ext JS, LLC.
15421 * Originally Released Under LGPL - original licence link has changed is not relivant.
15424 * <script type="text/javascript">
15428 * @class Roo.menu.TextItem
15429 * @extends Roo.menu.BaseItem
15430 * Adds a static text string to a menu, usually used as either a heading or group separator.
15431 * Note: old style constructor with text is still supported.
15434 * Creates a new TextItem
15435 * @param {Object} cfg Configuration
15437 Roo.menu.TextItem = function(cfg){
15438 if (typeof(cfg) == 'string') {
15441 Roo.apply(this,cfg);
15444 Roo.menu.TextItem.superclass.constructor.call(this);
15447 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15449 * @cfg {String} text Text to show on item.
15454 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15456 hideOnClick : false,
15458 * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15460 itemCls : "x-menu-text",
15463 onRender : function(){
15464 var s = document.createElement("span");
15465 s.className = this.itemCls;
15466 s.innerHTML = this.text;
15468 Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15472 * Ext JS Library 1.1.1
15473 * Copyright(c) 2006-2007, Ext JS, LLC.
15475 * Originally Released Under LGPL - original licence link has changed is not relivant.
15478 * <script type="text/javascript">
15482 * @class Roo.menu.Separator
15483 * @extends Roo.menu.BaseItem
15484 * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15485 * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15487 * @param {Object} config Configuration options
15489 Roo.menu.Separator = function(config){
15490 Roo.menu.Separator.superclass.constructor.call(this, config);
15493 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15495 * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15497 itemCls : "x-menu-sep",
15499 * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15501 hideOnClick : false,
15504 onRender : function(li){
15505 var s = document.createElement("span");
15506 s.className = this.itemCls;
15507 s.innerHTML = " ";
15509 li.addClass("x-menu-sep-li");
15510 Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15514 * Ext JS Library 1.1.1
15515 * Copyright(c) 2006-2007, Ext JS, LLC.
15517 * Originally Released Under LGPL - original licence link has changed is not relivant.
15520 * <script type="text/javascript">
15523 * @class Roo.menu.Item
15524 * @extends Roo.menu.BaseItem
15525 * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15526 * display items. Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15527 * activation and click handling.
15529 * Creates a new Item
15530 * @param {Object} config Configuration options
15532 Roo.menu.Item = function(config){
15533 Roo.menu.Item.superclass.constructor.call(this, config);
15535 this.menu = Roo.menu.MenuMgr.get(this.menu);
15538 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15540 * @cfg {Roo.menu.Menu} menu
15544 * @cfg {String} text
15545 * The text to show on the menu item.
15549 * @cfg {String} html to render in menu
15550 * The text to show on the menu item (HTML version).
15554 * @cfg {String} icon
15555 * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15559 * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15561 itemCls : "x-menu-item",
15563 * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15565 canActivate : true,
15567 * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15570 // doc'd in BaseItem
15574 ctype: "Roo.menu.Item",
15577 onRender : function(container, position){
15578 var el = document.createElement("a");
15579 el.hideFocus = true;
15580 el.unselectable = "on";
15581 el.href = this.href || "#";
15582 if(this.hrefTarget){
15583 el.target = this.hrefTarget;
15585 el.className = this.itemCls + (this.menu ? " x-menu-item-arrow" : "") + (this.cls ? " " + this.cls : "");
15587 var html = this.html.length ? this.html : String.format('{0}',this.text);
15589 el.innerHTML = String.format(
15590 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15591 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15593 Roo.menu.Item.superclass.onRender.call(this, container, position);
15597 * Sets the text to display in this menu item
15598 * @param {String} text The text to display
15599 * @param {Boolean} isHTML true to indicate text is pure html.
15601 setText : function(text, isHTML){
15609 var html = this.html.length ? this.html : String.format('{0}',this.text);
15611 this.el.update(String.format(
15612 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15613 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15614 this.parentMenu.autoWidth();
15619 handleClick : function(e){
15620 if(!this.href){ // if no link defined, stop the event automatically
15623 Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15627 activate : function(autoExpand){
15628 if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15638 shouldDeactivate : function(e){
15639 if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15640 if(this.menu && this.menu.isVisible()){
15641 return !this.menu.getEl().getRegion().contains(e.getPoint());
15649 deactivate : function(){
15650 Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15655 expandMenu : function(autoActivate){
15656 if(!this.disabled && this.menu){
15657 clearTimeout(this.hideTimer);
15658 delete this.hideTimer;
15659 if(!this.menu.isVisible() && !this.showTimer){
15660 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15661 }else if (this.menu.isVisible() && autoActivate){
15662 this.menu.tryActivate(0, 1);
15668 deferExpand : function(autoActivate){
15669 delete this.showTimer;
15670 this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15672 this.menu.tryActivate(0, 1);
15677 hideMenu : function(){
15678 clearTimeout(this.showTimer);
15679 delete this.showTimer;
15680 if(!this.hideTimer && this.menu && this.menu.isVisible()){
15681 this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15686 deferHide : function(){
15687 delete this.hideTimer;
15692 * Ext JS Library 1.1.1
15693 * Copyright(c) 2006-2007, Ext JS, LLC.
15695 * Originally Released Under LGPL - original licence link has changed is not relivant.
15698 * <script type="text/javascript">
15702 * @class Roo.menu.CheckItem
15703 * @extends Roo.menu.Item
15704 * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15706 * Creates a new CheckItem
15707 * @param {Object} config Configuration options
15709 Roo.menu.CheckItem = function(config){
15710 Roo.menu.CheckItem.superclass.constructor.call(this, config);
15713 * @event beforecheckchange
15714 * Fires before the checked value is set, providing an opportunity to cancel if needed
15715 * @param {Roo.menu.CheckItem} this
15716 * @param {Boolean} checked The new checked value that will be set
15718 "beforecheckchange" : true,
15720 * @event checkchange
15721 * Fires after the checked value has been set
15722 * @param {Roo.menu.CheckItem} this
15723 * @param {Boolean} checked The checked value that was set
15725 "checkchange" : true
15727 if(this.checkHandler){
15728 this.on('checkchange', this.checkHandler, this.scope);
15731 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15733 * @cfg {String} group
15734 * All check items with the same group name will automatically be grouped into a single-select
15735 * radio button group (defaults to '')
15738 * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15740 itemCls : "x-menu-item x-menu-check-item",
15742 * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15744 groupClass : "x-menu-group-item",
15747 * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false). Note that
15748 * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15749 * initialized with checked = true will be rendered as checked.
15754 ctype: "Roo.menu.CheckItem",
15757 onRender : function(c){
15758 Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15760 this.el.addClass(this.groupClass);
15762 Roo.menu.MenuMgr.registerCheckable(this);
15764 this.checked = false;
15765 this.setChecked(true, true);
15770 destroy : function(){
15772 Roo.menu.MenuMgr.unregisterCheckable(this);
15774 Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15778 * Set the checked state of this item
15779 * @param {Boolean} checked The new checked value
15780 * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15782 setChecked : function(state, suppressEvent){
15783 if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15784 if(this.container){
15785 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15787 this.checked = state;
15788 if(suppressEvent !== true){
15789 this.fireEvent("checkchange", this, state);
15795 handleClick : function(e){
15796 if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15797 this.setChecked(!this.checked);
15799 Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15803 * Ext JS Library 1.1.1
15804 * Copyright(c) 2006-2007, Ext JS, LLC.
15806 * Originally Released Under LGPL - original licence link has changed is not relivant.
15809 * <script type="text/javascript">
15813 * @class Roo.menu.DateItem
15814 * @extends Roo.menu.Adapter
15815 * A menu item that wraps the {@link Roo.DatPicker} component.
15817 * Creates a new DateItem
15818 * @param {Object} config Configuration options
15820 Roo.menu.DateItem = function(config){
15821 Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15822 /** The Roo.DatePicker object @type Roo.DatePicker */
15823 this.picker = this.component;
15824 this.addEvents({select: true});
15826 this.picker.on("render", function(picker){
15827 picker.getEl().swallowEvent("click");
15828 picker.container.addClass("x-menu-date-item");
15831 this.picker.on("select", this.onSelect, this);
15834 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15836 onSelect : function(picker, date){
15837 this.fireEvent("select", this, date, picker);
15838 Roo.menu.DateItem.superclass.handleClick.call(this);
15842 * Ext JS Library 1.1.1
15843 * Copyright(c) 2006-2007, Ext JS, LLC.
15845 * Originally Released Under LGPL - original licence link has changed is not relivant.
15848 * <script type="text/javascript">
15852 * @class Roo.menu.ColorItem
15853 * @extends Roo.menu.Adapter
15854 * A menu item that wraps the {@link Roo.ColorPalette} component.
15856 * Creates a new ColorItem
15857 * @param {Object} config Configuration options
15859 Roo.menu.ColorItem = function(config){
15860 Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15861 /** The Roo.ColorPalette object @type Roo.ColorPalette */
15862 this.palette = this.component;
15863 this.relayEvents(this.palette, ["select"]);
15864 if(this.selectHandler){
15865 this.on('select', this.selectHandler, this.scope);
15868 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15870 * Ext JS Library 1.1.1
15871 * Copyright(c) 2006-2007, Ext JS, LLC.
15873 * Originally Released Under LGPL - original licence link has changed is not relivant.
15876 * <script type="text/javascript">
15881 * @class Roo.menu.DateMenu
15882 * @extends Roo.menu.Menu
15883 * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15885 * Creates a new DateMenu
15886 * @param {Object} config Configuration options
15888 Roo.menu.DateMenu = function(config){
15889 Roo.menu.DateMenu.superclass.constructor.call(this, config);
15891 var di = new Roo.menu.DateItem(config);
15894 * The {@link Roo.DatePicker} instance for this DateMenu
15897 this.picker = di.picker;
15900 * @param {DatePicker} picker
15901 * @param {Date} date
15903 this.relayEvents(di, ["select"]);
15904 this.on('beforeshow', function(){
15906 this.picker.hideMonthPicker(false);
15910 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15914 * Ext JS Library 1.1.1
15915 * Copyright(c) 2006-2007, Ext JS, LLC.
15917 * Originally Released Under LGPL - original licence link has changed is not relivant.
15920 * <script type="text/javascript">
15925 * @class Roo.menu.ColorMenu
15926 * @extends Roo.menu.Menu
15927 * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15929 * Creates a new ColorMenu
15930 * @param {Object} config Configuration options
15932 Roo.menu.ColorMenu = function(config){
15933 Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15935 var ci = new Roo.menu.ColorItem(config);
15938 * The {@link Roo.ColorPalette} instance for this ColorMenu
15939 * @type ColorPalette
15941 this.palette = ci.palette;
15944 * @param {ColorPalette} palette
15945 * @param {String} color
15947 this.relayEvents(ci, ["select"]);
15949 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15951 * Ext JS Library 1.1.1
15952 * Copyright(c) 2006-2007, Ext JS, LLC.
15954 * Originally Released Under LGPL - original licence link has changed is not relivant.
15957 * <script type="text/javascript">
15961 * @class Roo.form.TextItem
15962 * @extends Roo.BoxComponent
15963 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15965 * Creates a new TextItem
15966 * @param {Object} config Configuration options
15968 Roo.form.TextItem = function(config){
15969 Roo.form.TextItem.superclass.constructor.call(this, config);
15972 Roo.extend(Roo.form.TextItem, Roo.BoxComponent, {
15975 * @cfg {String} tag the tag for this item (default div)
15979 * @cfg {String} html the content for this item
15983 getAutoCreate : function()
15996 onRender : function(ct, position)
15998 Roo.form.TextItem.superclass.onRender.call(this, ct, position);
16001 var cfg = this.getAutoCreate();
16003 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16005 if (!cfg.name.length) {
16008 this.el = ct.createChild(cfg, position);
16013 * @param {String} html update the Contents of the element.
16015 setHTML : function(html)
16017 this.fieldEl.dom.innerHTML = html;
16022 * Ext JS Library 1.1.1
16023 * Copyright(c) 2006-2007, Ext JS, LLC.
16025 * Originally Released Under LGPL - original licence link has changed is not relivant.
16028 * <script type="text/javascript">
16032 * @class Roo.form.Field
16033 * @extends Roo.BoxComponent
16034 * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16036 * Creates a new Field
16037 * @param {Object} config Configuration options
16039 Roo.form.Field = function(config){
16040 Roo.form.Field.superclass.constructor.call(this, config);
16043 Roo.extend(Roo.form.Field, Roo.BoxComponent, {
16045 * @cfg {String} fieldLabel Label to use when rendering a form.
16048 * @cfg {String} qtip Mouse over tip
16052 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16054 invalidClass : "x-form-invalid",
16056 * @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")
16058 invalidText : "The value in this field is invalid",
16060 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16062 focusClass : "x-form-focus",
16064 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16065 automatic validation (defaults to "keyup").
16067 validationEvent : "keyup",
16069 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16071 validateOnBlur : true,
16073 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16075 validationDelay : 250,
16077 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16078 * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16080 defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16082 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16084 fieldClass : "x-form-field",
16086 * @cfg {String} msgTarget The location where error text should display. Should be one of the following values (defaults to 'qtip'):
16089 ----------- ----------------------------------------------------------------------
16090 qtip Display a quick tip when the user hovers over the field
16091 title Display a default browser title attribute popup
16092 under Add a block div beneath the field containing the error text
16093 side Add an error icon to the right of the field with a popup on hover
16094 [element id] Add the error text directly to the innerHTML of the specified element
16097 msgTarget : 'qtip',
16099 * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16104 * @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.
16109 * @cfg {Boolean} disabled True to disable the field (defaults to false).
16114 * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16116 inputType : undefined,
16119 * @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).
16121 tabIndex : undefined,
16124 isFormField : true,
16129 * @property {Roo.Element} fieldEl
16130 * Element Containing the rendered Field (with label etc.)
16133 * @cfg {Mixed} value A value to initialize this field with.
16138 * @cfg {String} name The field's HTML name attribute.
16141 * @cfg {String} cls A CSS class to apply to the field's underlying element.
16144 loadedValue : false,
16148 initComponent : function(){
16149 Roo.form.Field.superclass.initComponent.call(this);
16153 * Fires when this field receives input focus.
16154 * @param {Roo.form.Field} this
16159 * Fires when this field loses input focus.
16160 * @param {Roo.form.Field} this
16164 * @event specialkey
16165 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
16166 * {@link Roo.EventObject#getKey} to determine which key was pressed.
16167 * @param {Roo.form.Field} this
16168 * @param {Roo.EventObject} e The event object
16173 * Fires just before the field blurs if the field value has changed.
16174 * @param {Roo.form.Field} this
16175 * @param {Mixed} newValue The new value
16176 * @param {Mixed} oldValue The original value
16181 * Fires after the field has been marked as invalid.
16182 * @param {Roo.form.Field} this
16183 * @param {String} msg The validation message
16188 * Fires after the field has been validated with no errors.
16189 * @param {Roo.form.Field} this
16194 * Fires after the key up
16195 * @param {Roo.form.Field} this
16196 * @param {Roo.EventObject} e The event Object
16203 * Returns the name attribute of the field if available
16204 * @return {String} name The field name
16206 getName: function(){
16207 return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16211 onRender : function(ct, position){
16212 Roo.form.Field.superclass.onRender.call(this, ct, position);
16214 var cfg = this.getAutoCreate();
16216 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16218 if (!cfg.name.length) {
16221 if(this.inputType){
16222 cfg.type = this.inputType;
16224 this.el = ct.createChild(cfg, position);
16226 var type = this.el.dom.type;
16228 if(type == 'password'){
16231 this.el.addClass('x-form-'+type);
16234 this.el.dom.readOnly = true;
16236 if(this.tabIndex !== undefined){
16237 this.el.dom.setAttribute('tabIndex', this.tabIndex);
16240 this.el.addClass([this.fieldClass, this.cls]);
16245 * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16246 * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16247 * @return {Roo.form.Field} this
16249 applyTo : function(target){
16250 this.allowDomMove = false;
16251 this.el = Roo.get(target);
16252 this.render(this.el.dom.parentNode);
16257 initValue : function(){
16258 if(this.value !== undefined){
16259 this.setValue(this.value);
16260 }else if(this.el.dom.value.length > 0){
16261 this.setValue(this.el.dom.value);
16266 * Returns true if this field has been changed since it was originally loaded and is not disabled.
16267 * DEPRICATED - it never worked well - use hasChanged/resetHasChanged.
16269 isDirty : function() {
16270 if(this.disabled) {
16273 return String(this.getValue()) !== String(this.originalValue);
16277 * stores the current value in loadedValue
16279 resetHasChanged : function()
16281 this.loadedValue = String(this.getValue());
16284 * checks the current value against the 'loaded' value.
16285 * Note - will return false if 'resetHasChanged' has not been called first.
16287 hasChanged : function()
16289 if(this.disabled || this.readOnly) {
16292 return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16298 afterRender : function(){
16299 Roo.form.Field.superclass.afterRender.call(this);
16304 fireKey : function(e){
16305 //Roo.log('field ' + e.getKey());
16306 if(e.isNavKeyPress()){
16307 this.fireEvent("specialkey", this, e);
16312 * Resets the current field value to the originally loaded value and clears any validation messages
16314 reset : function(){
16315 this.setValue(this.resetValue);
16316 this.originalValue = this.getValue();
16317 this.clearInvalid();
16321 initEvents : function(){
16322 // safari killled keypress - so keydown is now used..
16323 this.el.on("keydown" , this.fireKey, this);
16324 this.el.on("focus", this.onFocus, this);
16325 this.el.on("blur", this.onBlur, this);
16326 this.el.relayEvent('keyup', this);
16328 // reference to original value for reset
16329 this.originalValue = this.getValue();
16330 this.resetValue = this.getValue();
16334 onFocus : function(){
16335 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16336 this.el.addClass(this.focusClass);
16338 if(!this.hasFocus){
16339 this.hasFocus = true;
16340 this.startValue = this.getValue();
16341 this.fireEvent("focus", this);
16345 beforeBlur : Roo.emptyFn,
16348 onBlur : function(){
16350 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16351 this.el.removeClass(this.focusClass);
16353 this.hasFocus = false;
16354 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16357 var v = this.getValue();
16358 if(String(v) !== String(this.startValue)){
16359 this.fireEvent('change', this, v, this.startValue);
16361 this.fireEvent("blur", this);
16365 * Returns whether or not the field value is currently valid
16366 * @param {Boolean} preventMark True to disable marking the field invalid
16367 * @return {Boolean} True if the value is valid, else false
16369 isValid : function(preventMark){
16373 var restore = this.preventMark;
16374 this.preventMark = preventMark === true;
16375 var v = this.validateValue(this.processValue(this.getRawValue()));
16376 this.preventMark = restore;
16381 * Validates the field value
16382 * @return {Boolean} True if the value is valid, else false
16384 validate : function(){
16385 if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16386 this.clearInvalid();
16392 processValue : function(value){
16397 // Subclasses should provide the validation implementation by overriding this
16398 validateValue : function(value){
16403 * Mark this field as invalid
16404 * @param {String} msg The validation message
16406 markInvalid : function(msg){
16407 if(!this.rendered || this.preventMark){ // not rendered
16411 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16413 obj.el.addClass(this.invalidClass);
16414 msg = msg || this.invalidText;
16415 switch(this.msgTarget){
16417 obj.el.dom.qtip = msg;
16418 obj.el.dom.qclass = 'x-form-invalid-tip';
16419 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16420 Roo.QuickTips.enable();
16424 this.el.dom.title = msg;
16428 var elp = this.el.findParent('.x-form-element', 5, true);
16429 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16430 this.errorEl.setWidth(elp.getWidth(true)-20);
16432 this.errorEl.update(msg);
16433 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16436 if(!this.errorIcon){
16437 var elp = this.el.findParent('.x-form-element', 5, true);
16438 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16440 this.alignErrorIcon();
16441 this.errorIcon.dom.qtip = msg;
16442 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16443 this.errorIcon.show();
16444 this.on('resize', this.alignErrorIcon, this);
16447 var t = Roo.getDom(this.msgTarget);
16449 t.style.display = this.msgDisplay;
16452 this.fireEvent('invalid', this, msg);
16456 alignErrorIcon : function(){
16457 this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16461 * Clear any invalid styles/messages for this field
16463 clearInvalid : function(){
16464 if(!this.rendered || this.preventMark){ // not rendered
16467 var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16469 obj.el.removeClass(this.invalidClass);
16470 switch(this.msgTarget){
16472 obj.el.dom.qtip = '';
16475 this.el.dom.title = '';
16479 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16483 if(this.errorIcon){
16484 this.errorIcon.dom.qtip = '';
16485 this.errorIcon.hide();
16486 this.un('resize', this.alignErrorIcon, this);
16490 var t = Roo.getDom(this.msgTarget);
16492 t.style.display = 'none';
16495 this.fireEvent('valid', this);
16499 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
16500 * @return {Mixed} value The field value
16502 getRawValue : function(){
16503 var v = this.el.getValue();
16509 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16510 * @return {Mixed} value The field value
16512 getValue : function(){
16513 var v = this.el.getValue();
16519 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
16520 * @param {Mixed} value The value to set
16522 setRawValue : function(v){
16523 return this.el.dom.value = (v === null || v === undefined ? '' : v);
16527 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
16528 * @param {Mixed} value The value to set
16530 setValue : function(v){
16533 this.el.dom.value = (v === null || v === undefined ? '' : v);
16538 adjustSize : function(w, h){
16539 var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16540 s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16544 adjustWidth : function(tag, w){
16545 tag = tag.toLowerCase();
16546 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16547 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16548 if(tag == 'input'){
16551 if(tag == 'textarea'){
16554 }else if(Roo.isOpera){
16555 if(tag == 'input'){
16558 if(tag == 'textarea'){
16568 // anything other than normal should be considered experimental
16569 Roo.form.Field.msgFx = {
16571 show: function(msgEl, f){
16572 msgEl.setDisplayed('block');
16575 hide : function(msgEl, f){
16576 msgEl.setDisplayed(false).update('');
16581 show: function(msgEl, f){
16582 msgEl.slideIn('t', {stopFx:true});
16585 hide : function(msgEl, f){
16586 msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16591 show: function(msgEl, f){
16592 msgEl.fixDisplay();
16593 msgEl.alignTo(f.el, 'tl-tr');
16594 msgEl.slideIn('l', {stopFx:true});
16597 hide : function(msgEl, f){
16598 msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16603 * Ext JS Library 1.1.1
16604 * Copyright(c) 2006-2007, Ext JS, LLC.
16606 * Originally Released Under LGPL - original licence link has changed is not relivant.
16609 * <script type="text/javascript">
16614 * @class Roo.form.TextField
16615 * @extends Roo.form.Field
16616 * Basic text field. Can be used as a direct replacement for traditional text inputs, or as the base
16617 * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16619 * Creates a new TextField
16620 * @param {Object} config Configuration options
16622 Roo.form.TextField = function(config){
16623 Roo.form.TextField.superclass.constructor.call(this, config);
16627 * Fires when the autosize function is triggered. The field may or may not have actually changed size
16628 * according to the default logic, but this event provides a hook for the developer to apply additional
16629 * logic at runtime to resize the field if needed.
16630 * @param {Roo.form.Field} this This text field
16631 * @param {Number} width The new field width
16637 Roo.extend(Roo.form.TextField, Roo.form.Field, {
16639 * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16643 * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16647 * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16651 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16655 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16659 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16661 disableKeyFilter : false,
16663 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16667 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16671 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16673 maxLength : Number.MAX_VALUE,
16675 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16677 minLengthText : "The minimum length for this field is {0}",
16679 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16681 maxLengthText : "The maximum length for this field is {0}",
16683 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16685 selectOnFocus : false,
16687 * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space
16689 allowLeadingSpace : false,
16691 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16693 blankText : "This field is required",
16695 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16696 * If available, this function will be called only after the basic validators all return true, and will be passed the
16697 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16701 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16702 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16703 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
16707 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16711 * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16717 initEvents : function()
16719 if (this.emptyText) {
16720 this.el.attr('placeholder', this.emptyText);
16723 Roo.form.TextField.superclass.initEvents.call(this);
16724 if(this.validationEvent == 'keyup'){
16725 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16726 this.el.on('keyup', this.filterValidation, this);
16728 else if(this.validationEvent !== false){
16729 this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16732 if(this.selectOnFocus){
16733 this.on("focus", this.preFocus, this);
16735 if (!this.allowLeadingSpace) {
16736 this.on('blur', this.cleanLeadingSpace, this);
16739 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16740 this.el.on("keypress", this.filterKeys, this);
16743 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
16744 this.el.on("click", this.autoSize, this);
16746 if(this.el.is('input[type=password]') && Roo.isSafari){
16747 this.el.on('keydown', this.SafariOnKeyDown, this);
16751 processValue : function(value){
16752 if(this.stripCharsRe){
16753 var newValue = value.replace(this.stripCharsRe, '');
16754 if(newValue !== value){
16755 this.setRawValue(newValue);
16762 filterValidation : function(e){
16763 if(!e.isNavKeyPress()){
16764 this.validationTask.delay(this.validationDelay);
16769 onKeyUp : function(e){
16770 if(!e.isNavKeyPress()){
16774 // private - clean the leading white space
16775 cleanLeadingSpace : function(e)
16777 if ( this.inputType == 'file') {
16781 this.setValue((this.getValue() + '').replace(/^\s+/,''));
16784 * Resets the current field value to the originally-loaded value and clears any validation messages.
16787 reset : function(){
16788 Roo.form.TextField.superclass.reset.call(this);
16792 preFocus : function(){
16794 if(this.selectOnFocus){
16795 this.el.dom.select();
16801 filterKeys : function(e){
16802 var k = e.getKey();
16803 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16806 var c = e.getCharCode(), cc = String.fromCharCode(c);
16807 if(Roo.isIE && (e.isSpecialKey() || !cc)){
16810 if(!this.maskRe.test(cc)){
16815 setValue : function(v){
16817 Roo.form.TextField.superclass.setValue.apply(this, arguments);
16823 * Validates a value according to the field's validation rules and marks the field as invalid
16824 * if the validation fails
16825 * @param {Mixed} value The value to validate
16826 * @return {Boolean} True if the value is valid, else false
16828 validateValue : function(value){
16829 if(value.length < 1) { // if it's blank
16830 if(this.allowBlank){
16831 this.clearInvalid();
16834 this.markInvalid(this.blankText);
16838 if(value.length < this.minLength){
16839 this.markInvalid(String.format(this.minLengthText, this.minLength));
16842 if(value.length > this.maxLength){
16843 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16847 var vt = Roo.form.VTypes;
16848 if(!vt[this.vtype](value, this)){
16849 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16853 if(typeof this.validator == "function"){
16854 var msg = this.validator(value);
16856 this.markInvalid(msg);
16860 if(this.regex && !this.regex.test(value)){
16861 this.markInvalid(this.regexText);
16868 * Selects text in this field
16869 * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16870 * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16872 selectText : function(start, end){
16873 var v = this.getRawValue();
16875 start = start === undefined ? 0 : start;
16876 end = end === undefined ? v.length : end;
16877 var d = this.el.dom;
16878 if(d.setSelectionRange){
16879 d.setSelectionRange(start, end);
16880 }else if(d.createTextRange){
16881 var range = d.createTextRange();
16882 range.moveStart("character", start);
16883 range.moveEnd("character", v.length-end);
16890 * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16891 * This only takes effect if grow = true, and fires the autosize event.
16893 autoSize : function(){
16894 if(!this.grow || !this.rendered){
16898 this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16901 var v = el.dom.value;
16902 var d = document.createElement('div');
16903 d.appendChild(document.createTextNode(v));
16907 var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16908 this.el.setWidth(w);
16909 this.fireEvent("autosize", this, w);
16913 SafariOnKeyDown : function(event)
16915 // this is a workaround for a password hang bug on chrome/ webkit.
16917 var isSelectAll = false;
16919 if(this.el.dom.selectionEnd > 0){
16920 isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16922 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16923 event.preventDefault();
16928 if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16930 event.preventDefault();
16931 // this is very hacky as keydown always get's upper case.
16933 var cc = String.fromCharCode(event.getCharCode());
16936 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
16944 * Ext JS Library 1.1.1
16945 * Copyright(c) 2006-2007, Ext JS, LLC.
16947 * Originally Released Under LGPL - original licence link has changed is not relivant.
16950 * <script type="text/javascript">
16954 * @class Roo.form.Hidden
16955 * @extends Roo.form.TextField
16956 * Simple Hidden element used on forms
16958 * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16961 * Creates a new Hidden form element.
16962 * @param {Object} config Configuration options
16967 // easy hidden field...
16968 Roo.form.Hidden = function(config){
16969 Roo.form.Hidden.superclass.constructor.call(this, config);
16972 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16974 inputType: 'hidden',
16977 labelSeparator: '',
16979 itemCls : 'x-form-item-display-none'
16987 * Ext JS Library 1.1.1
16988 * Copyright(c) 2006-2007, Ext JS, LLC.
16990 * Originally Released Under LGPL - original licence link has changed is not relivant.
16993 * <script type="text/javascript">
16997 * @class Roo.form.TriggerField
16998 * @extends Roo.form.TextField
16999 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17000 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17001 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17002 * for which you can provide a custom implementation. For example:
17004 var trigger = new Roo.form.TriggerField();
17005 trigger.onTriggerClick = myTriggerFn;
17006 trigger.applyTo('my-field');
17009 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17010 * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17011 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
17012 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17014 * Create a new TriggerField.
17015 * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17016 * to the base TextField)
17018 Roo.form.TriggerField = function(config){
17019 this.mimicing = false;
17020 Roo.form.TriggerField.superclass.constructor.call(this, config);
17023 Roo.extend(Roo.form.TriggerField, Roo.form.TextField, {
17025 * @cfg {String} triggerClass A CSS class to apply to the trigger
17028 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17029 * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17031 defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17033 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17037 /** @cfg {Boolean} grow @hide */
17038 /** @cfg {Number} growMin @hide */
17039 /** @cfg {Number} growMax @hide */
17045 autoSize: Roo.emptyFn,
17049 deferHeight : true,
17052 actionMode : 'wrap',
17054 onResize : function(w, h){
17055 Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17056 if(typeof w == 'number'){
17057 var x = w - this.trigger.getWidth();
17058 this.el.setWidth(this.adjustWidth('input', x));
17059 this.trigger.setStyle('left', x+'px');
17064 adjustSize : Roo.BoxComponent.prototype.adjustSize,
17067 getResizeEl : function(){
17072 getPositionEl : function(){
17077 alignErrorIcon : function(){
17078 this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17082 onRender : function(ct, position){
17083 Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17084 this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17085 this.trigger = this.wrap.createChild(this.triggerConfig ||
17086 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17087 if(this.hideTrigger){
17088 this.trigger.setDisplayed(false);
17090 this.initTrigger();
17092 this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17097 initTrigger : function(){
17098 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17099 this.trigger.addClassOnOver('x-form-trigger-over');
17100 this.trigger.addClassOnClick('x-form-trigger-click');
17104 onDestroy : function(){
17106 this.trigger.removeAllListeners();
17107 this.trigger.remove();
17110 this.wrap.remove();
17112 Roo.form.TriggerField.superclass.onDestroy.call(this);
17116 onFocus : function(){
17117 Roo.form.TriggerField.superclass.onFocus.call(this);
17118 if(!this.mimicing){
17119 this.wrap.addClass('x-trigger-wrap-focus');
17120 this.mimicing = true;
17121 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17122 if(this.monitorTab){
17123 this.el.on("keydown", this.checkTab, this);
17129 checkTab : function(e){
17130 if(e.getKey() == e.TAB){
17131 this.triggerBlur();
17136 onBlur : function(){
17141 mimicBlur : function(e, t){
17142 if(!this.wrap.contains(t) && this.validateBlur()){
17143 this.triggerBlur();
17148 triggerBlur : function(){
17149 this.mimicing = false;
17150 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17151 if(this.monitorTab){
17152 this.el.un("keydown", this.checkTab, this);
17154 this.wrap.removeClass('x-trigger-wrap-focus');
17155 Roo.form.TriggerField.superclass.onBlur.call(this);
17159 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17160 validateBlur : function(e, t){
17165 onDisable : function(){
17166 Roo.form.TriggerField.superclass.onDisable.call(this);
17168 this.wrap.addClass('x-item-disabled');
17173 onEnable : function(){
17174 Roo.form.TriggerField.superclass.onEnable.call(this);
17176 this.wrap.removeClass('x-item-disabled');
17181 onShow : function(){
17182 var ae = this.getActionEl();
17185 ae.dom.style.display = '';
17186 ae.dom.style.visibility = 'visible';
17192 onHide : function(){
17193 var ae = this.getActionEl();
17194 ae.dom.style.display = 'none';
17198 * The function that should handle the trigger's click event. This method does nothing by default until overridden
17199 * by an implementing function.
17201 * @param {EventObject} e
17203 onTriggerClick : Roo.emptyFn
17206 // TwinTriggerField is not a public class to be used directly. It is meant as an abstract base class
17207 // to be extended by an implementing class. For an example of implementing this class, see the custom
17208 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17209 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17210 initComponent : function(){
17211 Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17213 this.triggerConfig = {
17214 tag:'span', cls:'x-form-twin-triggers', cn:[
17215 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17216 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17220 getTrigger : function(index){
17221 return this.triggers[index];
17224 initTrigger : function(){
17225 var ts = this.trigger.select('.x-form-trigger', true);
17226 this.wrap.setStyle('overflow', 'hidden');
17227 var triggerField = this;
17228 ts.each(function(t, all, index){
17229 t.hide = function(){
17230 var w = triggerField.wrap.getWidth();
17231 this.dom.style.display = 'none';
17232 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17234 t.show = function(){
17235 var w = triggerField.wrap.getWidth();
17236 this.dom.style.display = '';
17237 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17239 var triggerIndex = 'Trigger'+(index+1);
17241 if(this['hide'+triggerIndex]){
17242 t.dom.style.display = 'none';
17244 t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17245 t.addClassOnOver('x-form-trigger-over');
17246 t.addClassOnClick('x-form-trigger-click');
17248 this.triggers = ts.elements;
17251 onTrigger1Click : Roo.emptyFn,
17252 onTrigger2Click : Roo.emptyFn
17255 * Ext JS Library 1.1.1
17256 * Copyright(c) 2006-2007, Ext JS, LLC.
17258 * Originally Released Under LGPL - original licence link has changed is not relivant.
17261 * <script type="text/javascript">
17265 * @class Roo.form.TextArea
17266 * @extends Roo.form.TextField
17267 * Multiline text field. Can be used as a direct replacement for traditional textarea fields, plus adds
17268 * support for auto-sizing.
17270 * Creates a new TextArea
17271 * @param {Object} config Configuration options
17273 Roo.form.TextArea = function(config){
17274 Roo.form.TextArea.superclass.constructor.call(this, config);
17275 // these are provided exchanges for backwards compat
17276 // minHeight/maxHeight were replaced by growMin/growMax to be
17277 // compatible with TextField growing config values
17278 if(this.minHeight !== undefined){
17279 this.growMin = this.minHeight;
17281 if(this.maxHeight !== undefined){
17282 this.growMax = this.maxHeight;
17286 Roo.extend(Roo.form.TextArea, Roo.form.TextField, {
17288 * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17292 * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17296 * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17297 * in the field (equivalent to setting overflow: hidden, defaults to false)
17299 preventScrollbars: false,
17301 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17302 * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17306 onRender : function(ct, position){
17308 this.defaultAutoCreate = {
17310 style:"width:300px;height:60px;",
17311 autocomplete: "new-password"
17314 Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17316 this.textSizeEl = Roo.DomHelper.append(document.body, {
17317 tag: "pre", cls: "x-form-grow-sizer"
17319 if(this.preventScrollbars){
17320 this.el.setStyle("overflow", "hidden");
17322 this.el.setHeight(this.growMin);
17326 onDestroy : function(){
17327 if(this.textSizeEl){
17328 this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17330 Roo.form.TextArea.superclass.onDestroy.call(this);
17334 onKeyUp : function(e){
17335 if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17341 * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17342 * This only takes effect if grow = true, and fires the autosize event if the height changes.
17344 autoSize : function(){
17345 if(!this.grow || !this.textSizeEl){
17349 var v = el.dom.value;
17350 var ts = this.textSizeEl;
17353 ts.appendChild(document.createTextNode(v));
17356 Roo.fly(ts).setWidth(this.el.getWidth());
17358 v = "  ";
17361 v = v.replace(/\n/g, '<p> </p>');
17363 v += " \n ";
17366 var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17367 if(h != this.lastHeight){
17368 this.lastHeight = h;
17369 this.el.setHeight(h);
17370 this.fireEvent("autosize", this, h);
17375 * Ext JS Library 1.1.1
17376 * Copyright(c) 2006-2007, Ext JS, LLC.
17378 * Originally Released Under LGPL - original licence link has changed is not relivant.
17381 * <script type="text/javascript">
17386 * @class Roo.form.NumberField
17387 * @extends Roo.form.TextField
17388 * Numeric text field that provides automatic keystroke filtering and numeric validation.
17390 * Creates a new NumberField
17391 * @param {Object} config Configuration options
17393 Roo.form.NumberField = function(config){
17394 Roo.form.NumberField.superclass.constructor.call(this, config);
17397 Roo.extend(Roo.form.NumberField, Roo.form.TextField, {
17399 * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17401 fieldClass: "x-form-field x-form-num-field",
17403 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17405 allowDecimals : true,
17407 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17409 decimalSeparator : ".",
17411 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17413 decimalPrecision : 2,
17415 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17417 allowNegative : true,
17419 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17421 minValue : Number.NEGATIVE_INFINITY,
17423 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17425 maxValue : Number.MAX_VALUE,
17427 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17429 minText : "The minimum value for this field is {0}",
17431 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17433 maxText : "The maximum value for this field is {0}",
17435 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
17436 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17438 nanText : "{0} is not a valid number",
17441 initEvents : function(){
17442 Roo.form.NumberField.superclass.initEvents.call(this);
17443 var allowed = "0123456789";
17444 if(this.allowDecimals){
17445 allowed += this.decimalSeparator;
17447 if(this.allowNegative){
17450 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17451 var keyPress = function(e){
17452 var k = e.getKey();
17453 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17456 var c = e.getCharCode();
17457 if(allowed.indexOf(String.fromCharCode(c)) === -1){
17461 this.el.on("keypress", keyPress, this);
17465 validateValue : function(value){
17466 if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17469 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17472 var num = this.parseValue(value);
17474 this.markInvalid(String.format(this.nanText, value));
17477 if(num < this.minValue){
17478 this.markInvalid(String.format(this.minText, this.minValue));
17481 if(num > this.maxValue){
17482 this.markInvalid(String.format(this.maxText, this.maxValue));
17488 getValue : function(){
17489 return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17493 parseValue : function(value){
17494 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17495 return isNaN(value) ? '' : value;
17499 fixPrecision : function(value){
17500 var nan = isNaN(value);
17501 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17502 return nan ? '' : value;
17504 return parseFloat(value).toFixed(this.decimalPrecision);
17507 setValue : function(v){
17508 v = this.fixPrecision(v);
17509 Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17513 decimalPrecisionFcn : function(v){
17514 return Math.floor(v);
17517 beforeBlur : function(){
17518 var v = this.parseValue(this.getRawValue());
17525 * Ext JS Library 1.1.1
17526 * Copyright(c) 2006-2007, Ext JS, LLC.
17528 * Originally Released Under LGPL - original licence link has changed is not relivant.
17531 * <script type="text/javascript">
17535 * @class Roo.form.DateField
17536 * @extends Roo.form.TriggerField
17537 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17539 * Create a new DateField
17540 * @param {Object} config
17542 Roo.form.DateField = function(config)
17544 Roo.form.DateField.superclass.constructor.call(this, config);
17550 * Fires when a date is selected
17551 * @param {Roo.form.DateField} combo This combo box
17552 * @param {Date} date The date selected
17559 if(typeof this.minValue == "string") {
17560 this.minValue = this.parseDate(this.minValue);
17562 if(typeof this.maxValue == "string") {
17563 this.maxValue = this.parseDate(this.maxValue);
17565 this.ddMatch = null;
17566 if(this.disabledDates){
17567 var dd = this.disabledDates;
17569 for(var i = 0; i < dd.length; i++){
17571 if(i != dd.length-1) {
17575 this.ddMatch = new RegExp(re + ")");
17579 Roo.extend(Roo.form.DateField, Roo.form.TriggerField, {
17581 * @cfg {String} format
17582 * The default date format string which can be overriden for localization support. The format must be
17583 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17587 * @cfg {String} altFormats
17588 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17589 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17591 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17593 * @cfg {Array} disabledDays
17594 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17596 disabledDays : null,
17598 * @cfg {String} disabledDaysText
17599 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17601 disabledDaysText : "Disabled",
17603 * @cfg {Array} disabledDates
17604 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17605 * expression so they are very powerful. Some examples:
17607 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17608 * <li>["03/08", "09/16"] would disable those days for every year</li>
17609 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17610 * <li>["03/../2006"] would disable every day in March 2006</li>
17611 * <li>["^03"] would disable every day in every March</li>
17613 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17614 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17616 disabledDates : null,
17618 * @cfg {String} disabledDatesText
17619 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17621 disabledDatesText : "Disabled",
17625 * @cfg {Date/String} zeroValue
17626 * if the date is less that this number, then the field is rendered as empty
17629 zeroValue : '1800-01-01',
17633 * @cfg {Date/String} minValue
17634 * The minimum allowed date. Can be either a Javascript date object or a string date in a
17635 * valid format (defaults to null).
17639 * @cfg {Date/String} maxValue
17640 * The maximum allowed date. Can be either a Javascript date object or a string date in a
17641 * valid format (defaults to null).
17645 * @cfg {String} minText
17646 * The error text to display when the date in the cell is before minValue (defaults to
17647 * 'The date in this field must be after {minValue}').
17649 minText : "The date in this field must be equal to or after {0}",
17651 * @cfg {String} maxText
17652 * The error text to display when the date in the cell is after maxValue (defaults to
17653 * 'The date in this field must be before {maxValue}').
17655 maxText : "The date in this field must be equal to or before {0}",
17657 * @cfg {String} invalidText
17658 * The error text to display when the date in the field is invalid (defaults to
17659 * '{value} is not a valid date - it must be in the format {format}').
17661 invalidText : "{0} is not a valid date - it must be in the format {1}",
17663 * @cfg {String} triggerClass
17664 * An additional CSS class used to style the trigger button. The trigger will always get the
17665 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17666 * which displays a calendar icon).
17668 triggerClass : 'x-form-date-trigger',
17672 * @cfg {Boolean} useIso
17673 * if enabled, then the date field will use a hidden field to store the
17674 * real value as iso formated date. default (false)
17678 * @cfg {String/Object} autoCreate
17679 * A DomHelper element spec, or true for a default element spec (defaults to
17680 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17683 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17686 hiddenField: false,
17688 onRender : function(ct, position)
17690 Roo.form.DateField.superclass.onRender.call(this, ct, position);
17692 //this.el.dom.removeAttribute('name');
17693 Roo.log("Changing name?");
17694 this.el.dom.setAttribute('name', this.name + '____hidden___' );
17695 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17697 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17698 // prevent input submission
17699 this.hiddenName = this.name;
17706 validateValue : function(value)
17708 value = this.formatDate(value);
17709 if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17710 Roo.log('super failed');
17713 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17716 var svalue = value;
17717 value = this.parseDate(value);
17719 Roo.log('parse date failed' + svalue);
17720 this.markInvalid(String.format(this.invalidText, svalue, this.format));
17723 var time = value.getTime();
17724 if(this.minValue && time < this.minValue.getTime()){
17725 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17728 if(this.maxValue && time > this.maxValue.getTime()){
17729 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17732 if(this.disabledDays){
17733 var day = value.getDay();
17734 for(var i = 0; i < this.disabledDays.length; i++) {
17735 if(day === this.disabledDays[i]){
17736 this.markInvalid(this.disabledDaysText);
17741 var fvalue = this.formatDate(value);
17742 if(this.ddMatch && this.ddMatch.test(fvalue)){
17743 this.markInvalid(String.format(this.disabledDatesText, fvalue));
17750 // Provides logic to override the default TriggerField.validateBlur which just returns true
17751 validateBlur : function(){
17752 return !this.menu || !this.menu.isVisible();
17755 getName: function()
17757 // returns hidden if it's set..
17758 if (!this.rendered) {return ''};
17759 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
17764 * Returns the current date value of the date field.
17765 * @return {Date} The date value
17767 getValue : function(){
17769 return this.hiddenField ?
17770 this.hiddenField.value :
17771 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17775 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
17776 * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17777 * (the default format used is "m/d/y").
17780 //All of these calls set the same date value (May 4, 2006)
17782 //Pass a date object:
17783 var dt = new Date('5/4/06');
17784 dateField.setValue(dt);
17786 //Pass a date string (default format):
17787 dateField.setValue('5/4/06');
17789 //Pass a date string (custom format):
17790 dateField.format = 'Y-m-d';
17791 dateField.setValue('2006-5-4');
17793 * @param {String/Date} date The date or valid date string
17795 setValue : function(date){
17796 if (this.hiddenField) {
17797 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17799 Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17800 // make sure the value field is always stored as a date..
17801 this.value = this.parseDate(date);
17807 parseDate : function(value){
17809 if (value instanceof Date) {
17810 if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17817 if(!value || value instanceof Date){
17820 var v = Date.parseDate(value, this.format);
17821 if (!v && this.useIso) {
17822 v = Date.parseDate(value, 'Y-m-d');
17824 if(!v && this.altFormats){
17825 if(!this.altFormatsArray){
17826 this.altFormatsArray = this.altFormats.split("|");
17828 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17829 v = Date.parseDate(value, this.altFormatsArray[i]);
17832 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
17839 formatDate : function(date, fmt){
17840 return (!date || !(date instanceof Date)) ?
17841 date : date.dateFormat(fmt || this.format);
17846 select: function(m, d){
17849 this.fireEvent('select', this, d);
17851 show : function(){ // retain focus styling
17855 this.focus.defer(10, this);
17856 var ml = this.menuListeners;
17857 this.menu.un("select", ml.select, this);
17858 this.menu.un("show", ml.show, this);
17859 this.menu.un("hide", ml.hide, this);
17864 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17865 onTriggerClick : function(){
17869 if(this.menu == null){
17870 this.menu = new Roo.menu.DateMenu();
17872 Roo.apply(this.menu.picker, {
17873 showClear: this.allowBlank,
17874 minDate : this.minValue,
17875 maxDate : this.maxValue,
17876 disabledDatesRE : this.ddMatch,
17877 disabledDatesText : this.disabledDatesText,
17878 disabledDays : this.disabledDays,
17879 disabledDaysText : this.disabledDaysText,
17880 format : this.useIso ? 'Y-m-d' : this.format,
17881 minText : String.format(this.minText, this.formatDate(this.minValue)),
17882 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17884 this.menu.on(Roo.apply({}, this.menuListeners, {
17887 this.menu.picker.setValue(this.getValue() || new Date());
17888 this.menu.show(this.el, "tl-bl?");
17891 beforeBlur : function(){
17892 var v = this.parseDate(this.getRawValue());
17902 isDirty : function() {
17903 if(this.disabled) {
17907 if(typeof(this.startValue) === 'undefined'){
17911 return String(this.getValue()) !== String(this.startValue);
17915 cleanLeadingSpace : function(e)
17922 * Ext JS Library 1.1.1
17923 * Copyright(c) 2006-2007, Ext JS, LLC.
17925 * Originally Released Under LGPL - original licence link has changed is not relivant.
17928 * <script type="text/javascript">
17932 * @class Roo.form.MonthField
17933 * @extends Roo.form.TriggerField
17934 * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17936 * Create a new MonthField
17937 * @param {Object} config
17939 Roo.form.MonthField = function(config){
17941 Roo.form.MonthField.superclass.constructor.call(this, config);
17947 * Fires when a date is selected
17948 * @param {Roo.form.MonthFieeld} combo This combo box
17949 * @param {Date} date The date selected
17956 if(typeof this.minValue == "string") {
17957 this.minValue = this.parseDate(this.minValue);
17959 if(typeof this.maxValue == "string") {
17960 this.maxValue = this.parseDate(this.maxValue);
17962 this.ddMatch = null;
17963 if(this.disabledDates){
17964 var dd = this.disabledDates;
17966 for(var i = 0; i < dd.length; i++){
17968 if(i != dd.length-1) {
17972 this.ddMatch = new RegExp(re + ")");
17976 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField, {
17978 * @cfg {String} format
17979 * The default date format string which can be overriden for localization support. The format must be
17980 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17984 * @cfg {String} altFormats
17985 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17986 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17988 altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17990 * @cfg {Array} disabledDays
17991 * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17993 disabledDays : [0,1,2,3,4,5,6],
17995 * @cfg {String} disabledDaysText
17996 * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17998 disabledDaysText : "Disabled",
18000 * @cfg {Array} disabledDates
18001 * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18002 * expression so they are very powerful. Some examples:
18004 * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18005 * <li>["03/08", "09/16"] would disable those days for every year</li>
18006 * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18007 * <li>["03/../2006"] would disable every day in March 2006</li>
18008 * <li>["^03"] would disable every day in every March</li>
18010 * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18011 * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18013 disabledDates : null,
18015 * @cfg {String} disabledDatesText
18016 * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18018 disabledDatesText : "Disabled",
18020 * @cfg {Date/String} minValue
18021 * The minimum allowed date. Can be either a Javascript date object or a string date in a
18022 * valid format (defaults to null).
18026 * @cfg {Date/String} maxValue
18027 * The maximum allowed date. Can be either a Javascript date object or a string date in a
18028 * valid format (defaults to null).
18032 * @cfg {String} minText
18033 * The error text to display when the date in the cell is before minValue (defaults to
18034 * 'The date in this field must be after {minValue}').
18036 minText : "The date in this field must be equal to or after {0}",
18038 * @cfg {String} maxTextf
18039 * The error text to display when the date in the cell is after maxValue (defaults to
18040 * 'The date in this field must be before {maxValue}').
18042 maxText : "The date in this field must be equal to or before {0}",
18044 * @cfg {String} invalidText
18045 * The error text to display when the date in the field is invalid (defaults to
18046 * '{value} is not a valid date - it must be in the format {format}').
18048 invalidText : "{0} is not a valid date - it must be in the format {1}",
18050 * @cfg {String} triggerClass
18051 * An additional CSS class used to style the trigger button. The trigger will always get the
18052 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18053 * which displays a calendar icon).
18055 triggerClass : 'x-form-date-trigger',
18059 * @cfg {Boolean} useIso
18060 * if enabled, then the date field will use a hidden field to store the
18061 * real value as iso formated date. default (true)
18065 * @cfg {String/Object} autoCreate
18066 * A DomHelper element spec, or true for a default element spec (defaults to
18067 * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18070 defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18073 hiddenField: false,
18075 hideMonthPicker : false,
18077 onRender : function(ct, position)
18079 Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18081 this.el.dom.removeAttribute('name');
18082 this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18084 this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18085 // prevent input submission
18086 this.hiddenName = this.name;
18093 validateValue : function(value)
18095 value = this.formatDate(value);
18096 if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18099 if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18102 var svalue = value;
18103 value = this.parseDate(value);
18105 this.markInvalid(String.format(this.invalidText, svalue, this.format));
18108 var time = value.getTime();
18109 if(this.minValue && time < this.minValue.getTime()){
18110 this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18113 if(this.maxValue && time > this.maxValue.getTime()){
18114 this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18117 /*if(this.disabledDays){
18118 var day = value.getDay();
18119 for(var i = 0; i < this.disabledDays.length; i++) {
18120 if(day === this.disabledDays[i]){
18121 this.markInvalid(this.disabledDaysText);
18127 var fvalue = this.formatDate(value);
18128 /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18129 this.markInvalid(String.format(this.disabledDatesText, fvalue));
18137 // Provides logic to override the default TriggerField.validateBlur which just returns true
18138 validateBlur : function(){
18139 return !this.menu || !this.menu.isVisible();
18143 * Returns the current date value of the date field.
18144 * @return {Date} The date value
18146 getValue : function(){
18150 return this.hiddenField ?
18151 this.hiddenField.value :
18152 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18156 * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid
18157 * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18158 * (the default format used is "m/d/y").
18161 //All of these calls set the same date value (May 4, 2006)
18163 //Pass a date object:
18164 var dt = new Date('5/4/06');
18165 monthField.setValue(dt);
18167 //Pass a date string (default format):
18168 monthField.setValue('5/4/06');
18170 //Pass a date string (custom format):
18171 monthField.format = 'Y-m-d';
18172 monthField.setValue('2006-5-4');
18174 * @param {String/Date} date The date or valid date string
18176 setValue : function(date){
18177 Roo.log('month setValue' + date);
18178 // can only be first of month..
18180 var val = this.parseDate(date);
18182 if (this.hiddenField) {
18183 this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18185 Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18186 this.value = this.parseDate(date);
18190 parseDate : function(value){
18191 if(!value || value instanceof Date){
18192 value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18195 var v = Date.parseDate(value, this.format);
18196 if (!v && this.useIso) {
18197 v = Date.parseDate(value, 'Y-m-d');
18201 v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18205 if(!v && this.altFormats){
18206 if(!this.altFormatsArray){
18207 this.altFormatsArray = this.altFormats.split("|");
18209 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18210 v = Date.parseDate(value, this.altFormatsArray[i]);
18217 formatDate : function(date, fmt){
18218 return (!date || !(date instanceof Date)) ?
18219 date : date.dateFormat(fmt || this.format);
18224 select: function(m, d){
18226 this.fireEvent('select', this, d);
18228 show : function(){ // retain focus styling
18232 this.focus.defer(10, this);
18233 var ml = this.menuListeners;
18234 this.menu.un("select", ml.select, this);
18235 this.menu.un("show", ml.show, this);
18236 this.menu.un("hide", ml.hide, this);
18240 // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18241 onTriggerClick : function(){
18245 if(this.menu == null){
18246 this.menu = new Roo.menu.DateMenu();
18250 Roo.apply(this.menu.picker, {
18252 showClear: this.allowBlank,
18253 minDate : this.minValue,
18254 maxDate : this.maxValue,
18255 disabledDatesRE : this.ddMatch,
18256 disabledDatesText : this.disabledDatesText,
18258 format : this.useIso ? 'Y-m-d' : this.format,
18259 minText : String.format(this.minText, this.formatDate(this.minValue)),
18260 maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18263 this.menu.on(Roo.apply({}, this.menuListeners, {
18271 // hide month picker get's called when we called by 'before hide';
18273 var ignorehide = true;
18274 p.hideMonthPicker = function(disableAnim){
18278 if(this.monthPicker){
18279 Roo.log("hideMonthPicker called");
18280 if(disableAnim === true){
18281 this.monthPicker.hide();
18283 this.monthPicker.slideOut('t', {duration:.2});
18284 p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18285 p.fireEvent("select", this, this.value);
18291 Roo.log('picker set value');
18292 Roo.log(this.getValue());
18293 p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18294 m.show(this.el, 'tl-bl?');
18295 ignorehide = false;
18296 // this will trigger hideMonthPicker..
18299 // hidden the day picker
18300 Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18306 p.showMonthPicker.defer(100, p);
18312 beforeBlur : function(){
18313 var v = this.parseDate(this.getRawValue());
18319 /** @cfg {Boolean} grow @hide */
18320 /** @cfg {Number} growMin @hide */
18321 /** @cfg {Number} growMax @hide */
18328 * Ext JS Library 1.1.1
18329 * Copyright(c) 2006-2007, Ext JS, LLC.
18331 * Originally Released Under LGPL - original licence link has changed is not relivant.
18334 * <script type="text/javascript">
18339 * @class Roo.form.ComboBox
18340 * @extends Roo.form.TriggerField
18341 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18343 * Create a new ComboBox.
18344 * @param {Object} config Configuration options
18346 Roo.form.ComboBox = function(config){
18347 Roo.form.ComboBox.superclass.constructor.call(this, config);
18351 * Fires when the dropdown list is expanded
18352 * @param {Roo.form.ComboBox} combo This combo box
18357 * Fires when the dropdown list is collapsed
18358 * @param {Roo.form.ComboBox} combo This combo box
18362 * @event beforeselect
18363 * Fires before a list item is selected. Return false to cancel the selection.
18364 * @param {Roo.form.ComboBox} combo This combo box
18365 * @param {Roo.data.Record} record The data record returned from the underlying store
18366 * @param {Number} index The index of the selected item in the dropdown list
18368 'beforeselect' : true,
18371 * Fires when a list item is selected
18372 * @param {Roo.form.ComboBox} combo This combo box
18373 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18374 * @param {Number} index The index of the selected item in the dropdown list
18378 * @event beforequery
18379 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18380 * The event object passed has these properties:
18381 * @param {Roo.form.ComboBox} combo This combo box
18382 * @param {String} query The query
18383 * @param {Boolean} forceAll true to force "all" query
18384 * @param {Boolean} cancel true to cancel the query
18385 * @param {Object} e The query event object
18387 'beforequery': true,
18390 * Fires when the 'add' icon is pressed (add a listener to enable add button)
18391 * @param {Roo.form.ComboBox} combo This combo box
18396 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18397 * @param {Roo.form.ComboBox} combo This combo box
18398 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18404 if(this.transform){
18405 this.allowDomMove = false;
18406 var s = Roo.getDom(this.transform);
18407 if(!this.hiddenName){
18408 this.hiddenName = s.name;
18411 this.mode = 'local';
18412 var d = [], opts = s.options;
18413 for(var i = 0, len = opts.length;i < len; i++){
18415 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18417 this.value = value;
18419 d.push([value, o.text]);
18421 this.store = new Roo.data.SimpleStore({
18423 fields: ['value', 'text'],
18426 this.valueField = 'value';
18427 this.displayField = 'text';
18429 s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18430 if(!this.lazyRender){
18431 this.target = true;
18432 this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18433 s.parentNode.removeChild(s); // remove it
18434 this.render(this.el.parentNode);
18436 s.parentNode.removeChild(s); // remove it
18441 this.store = Roo.factory(this.store, Roo.data);
18444 this.selectedIndex = -1;
18445 if(this.mode == 'local'){
18446 if(config.queryDelay === undefined){
18447 this.queryDelay = 10;
18449 if(config.minChars === undefined){
18455 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18457 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18460 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18461 * rendering into an Roo.Editor, defaults to false)
18464 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18465 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18468 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18471 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18472 * the dropdown list (defaults to undefined, with no header element)
18476 * @cfg {String/Roo.Template} tpl The template to use to render the output
18480 defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18482 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18484 listWidth: undefined,
18486 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18487 * mode = 'remote' or 'text' if mode = 'local')
18489 displayField: undefined,
18491 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18492 * mode = 'remote' or 'value' if mode = 'local').
18493 * Note: use of a valueField requires the user make a selection
18494 * in order for a value to be mapped.
18496 valueField: undefined,
18500 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18501 * field's data value (defaults to the underlying DOM element's name)
18503 hiddenName: undefined,
18505 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18509 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18511 selectedClass: 'x-combo-selected',
18513 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
18514 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18515 * which displays a downward arrow icon).
18517 triggerClass : 'x-form-arrow-trigger',
18519 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18523 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18524 * anchor positions (defaults to 'tl-bl')
18526 listAlign: 'tl-bl?',
18528 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18532 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
18533 * query specified by the allQuery config option (defaults to 'query')
18535 triggerAction: 'query',
18537 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18538 * (defaults to 4, does not apply if editable = false)
18542 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18543 * delay (typeAheadDelay) if it matches a known value (defaults to false)
18547 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18548 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18552 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18553 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
18557 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
18558 * when editable = true (defaults to false)
18560 selectOnFocus:false,
18562 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18564 queryParam: 'query',
18566 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
18567 * when mode = 'remote' (defaults to 'Loading...')
18569 loadingText: 'Loading...',
18571 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18575 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18579 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18580 * traditional select (defaults to true)
18584 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18588 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18592 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18593 * listWidth has a higher value)
18597 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18598 * allow the user to set arbitrary text into the field (defaults to false)
18600 forceSelection:false,
18602 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18603 * if typeAhead = true (defaults to 250)
18605 typeAheadDelay : 250,
18607 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18608 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18610 valueNotFoundText : undefined,
18612 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18614 blockFocus : false,
18617 * @cfg {Boolean} disableClear Disable showing of clear button.
18619 disableClear : false,
18621 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
18623 alwaysQuery : false,
18629 // element that contains real text value.. (when hidden is used..)
18632 onRender : function(ct, position)
18634 Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18636 if(this.hiddenName){
18637 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
18639 this.hiddenField.value =
18640 this.hiddenValue !== undefined ? this.hiddenValue :
18641 this.value !== undefined ? this.value : '';
18643 // prevent input submission
18644 this.el.dom.removeAttribute('name');
18650 this.el.dom.setAttribute('autocomplete', 'off');
18653 var cls = 'x-combo-list';
18655 this.list = new Roo.Layer({
18656 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18659 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18660 this.list.setWidth(lw);
18661 this.list.swallowEvent('mousewheel');
18662 this.assetHeight = 0;
18665 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18666 this.assetHeight += this.header.getHeight();
18669 this.innerList = this.list.createChild({cls:cls+'-inner'});
18670 this.innerList.on('mouseover', this.onViewOver, this);
18671 this.innerList.on('mousemove', this.onViewMove, this);
18672 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18674 if(this.allowBlank && !this.pageSize && !this.disableClear){
18675 this.footer = this.list.createChild({cls:cls+'-ft'});
18676 this.pageTb = new Roo.Toolbar(this.footer);
18680 this.footer = this.list.createChild({cls:cls+'-ft'});
18681 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18682 {pageSize: this.pageSize});
18686 if (this.pageTb && this.allowBlank && !this.disableClear) {
18688 this.pageTb.add(new Roo.Toolbar.Fill(), {
18689 cls: 'x-btn-icon x-btn-clear',
18691 handler: function()
18694 _this.clearValue();
18695 _this.onSelect(false, -1);
18700 this.assetHeight += this.footer.getHeight();
18705 this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18708 this.view = new Roo.View(this.innerList, this.tpl, {
18711 selectedClass: this.selectedClass
18714 this.view.on('click', this.onViewClick, this);
18716 this.store.on('beforeload', this.onBeforeLoad, this);
18717 this.store.on('load', this.onLoad, this);
18718 this.store.on('loadexception', this.onLoadException, this);
18720 if(this.resizable){
18721 this.resizer = new Roo.Resizable(this.list, {
18722 pinned:true, handles:'se'
18724 this.resizer.on('resize', function(r, w, h){
18725 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18726 this.listWidth = w;
18727 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18728 this.restrictHeight();
18730 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18732 if(!this.editable){
18733 this.editable = true;
18734 this.setEditable(false);
18738 if (typeof(this.events.add.listeners) != 'undefined') {
18740 this.addicon = this.wrap.createChild(
18741 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
18743 this.addicon.on('click', function(e) {
18744 this.fireEvent('add', this);
18747 if (typeof(this.events.edit.listeners) != 'undefined') {
18749 this.editicon = this.wrap.createChild(
18750 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
18751 if (this.addicon) {
18752 this.editicon.setStyle('margin-left', '40px');
18754 this.editicon.on('click', function(e) {
18756 // we fire even if inothing is selected..
18757 this.fireEvent('edit', this, this.lastData );
18767 initEvents : function(){
18768 Roo.form.ComboBox.superclass.initEvents.call(this);
18770 this.keyNav = new Roo.KeyNav(this.el, {
18771 "up" : function(e){
18772 this.inKeyMode = true;
18776 "down" : function(e){
18777 if(!this.isExpanded()){
18778 this.onTriggerClick();
18780 this.inKeyMode = true;
18785 "enter" : function(e){
18786 this.onViewClick();
18790 "esc" : function(e){
18794 "tab" : function(e){
18795 this.onViewClick(false);
18796 this.fireEvent("specialkey", this, e);
18802 doRelay : function(foo, bar, hname){
18803 if(hname == 'down' || this.scope.isExpanded()){
18804 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18811 this.queryDelay = Math.max(this.queryDelay || 10,
18812 this.mode == 'local' ? 10 : 250);
18813 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18814 if(this.typeAhead){
18815 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18817 if(this.editable !== false){
18818 this.el.on("keyup", this.onKeyUp, this);
18820 if(this.forceSelection){
18821 this.on('blur', this.doForce, this);
18825 onDestroy : function(){
18827 this.view.setStore(null);
18828 this.view.el.removeAllListeners();
18829 this.view.el.remove();
18830 this.view.purgeListeners();
18833 this.list.destroy();
18836 this.store.un('beforeload', this.onBeforeLoad, this);
18837 this.store.un('load', this.onLoad, this);
18838 this.store.un('loadexception', this.onLoadException, this);
18840 Roo.form.ComboBox.superclass.onDestroy.call(this);
18844 fireKey : function(e){
18845 if(e.isNavKeyPress() && !this.list.isVisible()){
18846 this.fireEvent("specialkey", this, e);
18851 onResize: function(w, h){
18852 Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18854 if(typeof w != 'number'){
18855 // we do not handle it!?!?
18858 var tw = this.trigger.getWidth();
18859 tw += this.addicon ? this.addicon.getWidth() : 0;
18860 tw += this.editicon ? this.editicon.getWidth() : 0;
18862 this.el.setWidth( this.adjustWidth('input', x));
18864 this.trigger.setStyle('left', x+'px');
18866 if(this.list && this.listWidth === undefined){
18867 var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18868 this.list.setWidth(lw);
18869 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18877 * Allow or prevent the user from directly editing the field text. If false is passed,
18878 * the user will only be able to select from the items defined in the dropdown list. This method
18879 * is the runtime equivalent of setting the 'editable' config option at config time.
18880 * @param {Boolean} value True to allow the user to directly edit the field text
18882 setEditable : function(value){
18883 if(value == this.editable){
18886 this.editable = value;
18888 this.el.dom.setAttribute('readOnly', true);
18889 this.el.on('mousedown', this.onTriggerClick, this);
18890 this.el.addClass('x-combo-noedit');
18892 this.el.dom.setAttribute('readOnly', false);
18893 this.el.un('mousedown', this.onTriggerClick, this);
18894 this.el.removeClass('x-combo-noedit');
18899 onBeforeLoad : function(){
18900 if(!this.hasFocus){
18903 this.innerList.update(this.loadingText ?
18904 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18905 this.restrictHeight();
18906 this.selectedIndex = -1;
18910 onLoad : function(){
18911 if(!this.hasFocus){
18914 if(this.store.getCount() > 0){
18916 this.restrictHeight();
18917 if(this.lastQuery == this.allQuery){
18919 this.el.dom.select();
18921 if(!this.selectByValue(this.value, true)){
18922 this.select(0, true);
18926 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18927 this.taTask.delay(this.typeAheadDelay);
18931 this.onEmptyResults();
18936 onLoadException : function()
18939 Roo.log(this.store.reader.jsonData);
18940 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18941 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18947 onTypeAhead : function(){
18948 if(this.store.getCount() > 0){
18949 var r = this.store.getAt(0);
18950 var newValue = r.data[this.displayField];
18951 var len = newValue.length;
18952 var selStart = this.getRawValue().length;
18953 if(selStart != len){
18954 this.setRawValue(newValue);
18955 this.selectText(selStart, newValue.length);
18961 onSelect : function(record, index){
18962 if(this.fireEvent('beforeselect', this, record, index) !== false){
18963 this.setFromData(index > -1 ? record.data : false);
18965 this.fireEvent('select', this, record, index);
18970 * Returns the currently selected field value or empty string if no value is set.
18971 * @return {String} value The selected value
18973 getValue : function(){
18974 if(this.valueField){
18975 return typeof this.value != 'undefined' ? this.value : '';
18977 return Roo.form.ComboBox.superclass.getValue.call(this);
18981 * Clears any text/value currently set in the field
18983 clearValue : function(){
18984 if(this.hiddenField){
18985 this.hiddenField.value = '';
18988 this.setRawValue('');
18989 this.lastSelectionText = '';
18994 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18995 * will be displayed in the field. If the value does not match the data value of an existing item,
18996 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18997 * Otherwise the field will be blank (although the value will still be set).
18998 * @param {String} value The value to match
19000 setValue : function(v){
19002 if(this.valueField){
19003 var r = this.findRecord(this.valueField, v);
19005 text = r.data[this.displayField];
19006 }else if(this.valueNotFoundText !== undefined){
19007 text = this.valueNotFoundText;
19010 this.lastSelectionText = text;
19011 if(this.hiddenField){
19012 this.hiddenField.value = v;
19014 Roo.form.ComboBox.superclass.setValue.call(this, text);
19018 * @property {Object} the last set data for the element
19023 * Sets the value of the field based on a object which is related to the record format for the store.
19024 * @param {Object} value the value to set as. or false on reset?
19026 setFromData : function(o){
19027 var dv = ''; // display value
19028 var vv = ''; // value value..
19030 if (this.displayField) {
19031 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19033 // this is an error condition!!!
19034 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
19037 if(this.valueField){
19038 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19040 if(this.hiddenField){
19041 this.hiddenField.value = vv;
19043 this.lastSelectionText = dv;
19044 Roo.form.ComboBox.superclass.setValue.call(this, dv);
19048 // no hidden field.. - we store the value in 'value', but still display
19049 // display field!!!!
19050 this.lastSelectionText = dv;
19051 Roo.form.ComboBox.superclass.setValue.call(this, dv);
19057 reset : function(){
19058 // overridden so that last data is reset..
19059 this.setValue(this.resetValue);
19060 this.originalValue = this.getValue();
19061 this.clearInvalid();
19062 this.lastData = false;
19064 this.view.clearSelections();
19068 findRecord : function(prop, value){
19070 if(this.store.getCount() > 0){
19071 this.store.each(function(r){
19072 if(r.data[prop] == value){
19082 getName: function()
19084 // returns hidden if it's set..
19085 if (!this.rendered) {return ''};
19086 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
19090 onViewMove : function(e, t){
19091 this.inKeyMode = false;
19095 onViewOver : function(e, t){
19096 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19099 var item = this.view.findItemFromChild(t);
19101 var index = this.view.indexOf(item);
19102 this.select(index, false);
19107 onViewClick : function(doFocus)
19109 var index = this.view.getSelectedIndexes()[0];
19110 var r = this.store.getAt(index);
19112 this.onSelect(r, index);
19114 if(doFocus !== false && !this.blockFocus){
19120 restrictHeight : function(){
19121 this.innerList.dom.style.height = '';
19122 var inner = this.innerList.dom;
19123 var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19124 this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19125 this.list.beginUpdate();
19126 this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19127 this.list.alignTo(this.el, this.listAlign);
19128 this.list.endUpdate();
19132 onEmptyResults : function(){
19137 * Returns true if the dropdown list is expanded, else false.
19139 isExpanded : function(){
19140 return this.list.isVisible();
19144 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19145 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19146 * @param {String} value The data value of the item to select
19147 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19148 * selected item if it is not currently in view (defaults to true)
19149 * @return {Boolean} True if the value matched an item in the list, else false
19151 selectByValue : function(v, scrollIntoView){
19152 if(v !== undefined && v !== null){
19153 var r = this.findRecord(this.valueField || this.displayField, v);
19155 this.select(this.store.indexOf(r), scrollIntoView);
19163 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19164 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19165 * @param {Number} index The zero-based index of the list item to select
19166 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19167 * selected item if it is not currently in view (defaults to true)
19169 select : function(index, scrollIntoView){
19170 this.selectedIndex = index;
19171 this.view.select(index);
19172 if(scrollIntoView !== false){
19173 var el = this.view.getNode(index);
19175 this.innerList.scrollChildIntoView(el, false);
19181 selectNext : function(){
19182 var ct = this.store.getCount();
19184 if(this.selectedIndex == -1){
19186 }else if(this.selectedIndex < ct-1){
19187 this.select(this.selectedIndex+1);
19193 selectPrev : function(){
19194 var ct = this.store.getCount();
19196 if(this.selectedIndex == -1){
19198 }else if(this.selectedIndex != 0){
19199 this.select(this.selectedIndex-1);
19205 onKeyUp : function(e){
19206 if(this.editable !== false && !e.isSpecialKey()){
19207 this.lastKey = e.getKey();
19208 this.dqTask.delay(this.queryDelay);
19213 validateBlur : function(){
19214 return !this.list || !this.list.isVisible();
19218 initQuery : function(){
19219 this.doQuery(this.getRawValue());
19223 doForce : function(){
19224 if(this.el.dom.value.length > 0){
19225 this.el.dom.value =
19226 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19232 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
19233 * query allowing the query action to be canceled if needed.
19234 * @param {String} query The SQL query to execute
19235 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19236 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
19237 * saved in the current store (defaults to false)
19239 doQuery : function(q, forceAll){
19240 if(q === undefined || q === null){
19245 forceAll: forceAll,
19249 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19253 forceAll = qe.forceAll;
19254 if(forceAll === true || (q.length >= this.minChars)){
19255 if(this.lastQuery != q || this.alwaysQuery){
19256 this.lastQuery = q;
19257 if(this.mode == 'local'){
19258 this.selectedIndex = -1;
19260 this.store.clearFilter();
19262 this.store.filter(this.displayField, q);
19266 this.store.baseParams[this.queryParam] = q;
19268 params: this.getParams(q)
19273 this.selectedIndex = -1;
19280 getParams : function(q){
19282 //p[this.queryParam] = q;
19285 p.limit = this.pageSize;
19291 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19293 collapse : function(){
19294 if(!this.isExpanded()){
19298 Roo.get(document).un('mousedown', this.collapseIf, this);
19299 Roo.get(document).un('mousewheel', this.collapseIf, this);
19300 if (!this.editable) {
19301 Roo.get(document).un('keydown', this.listKeyPress, this);
19303 this.fireEvent('collapse', this);
19307 collapseIf : function(e){
19308 if(!e.within(this.wrap) && !e.within(this.list)){
19314 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19316 expand : function(){
19317 if(this.isExpanded() || !this.hasFocus){
19320 this.list.alignTo(this.el, this.listAlign);
19322 Roo.get(document).on('mousedown', this.collapseIf, this);
19323 Roo.get(document).on('mousewheel', this.collapseIf, this);
19324 if (!this.editable) {
19325 Roo.get(document).on('keydown', this.listKeyPress, this);
19328 this.fireEvent('expand', this);
19332 // Implements the default empty TriggerField.onTriggerClick function
19333 onTriggerClick : function(){
19337 if(this.isExpanded()){
19339 if (!this.blockFocus) {
19344 this.hasFocus = true;
19345 if(this.triggerAction == 'all') {
19346 this.doQuery(this.allQuery, true);
19348 this.doQuery(this.getRawValue());
19350 if (!this.blockFocus) {
19355 listKeyPress : function(e)
19357 //Roo.log('listkeypress');
19358 // scroll to first matching element based on key pres..
19359 if (e.isSpecialKey()) {
19362 var k = String.fromCharCode(e.getKey()).toUpperCase();
19365 var csel = this.view.getSelectedNodes();
19366 var cselitem = false;
19368 var ix = this.view.indexOf(csel[0]);
19369 cselitem = this.store.getAt(ix);
19370 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19376 this.store.each(function(v) {
19378 // start at existing selection.
19379 if (cselitem.id == v.id) {
19385 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19386 match = this.store.indexOf(v);
19391 if (match === false) {
19392 return true; // no more action?
19395 this.view.select(match);
19396 var sn = Roo.get(this.view.getSelectedNodes()[0]);
19397 sn.scrollIntoView(sn.dom.parentNode, false);
19401 * @cfg {Boolean} grow
19405 * @cfg {Number} growMin
19409 * @cfg {Number} growMax
19417 * Copyright(c) 2010-2012, Roo J Solutions Limited
19424 * @class Roo.form.ComboBoxArray
19425 * @extends Roo.form.TextField
19426 * A facebook style adder... for lists of email / people / countries etc...
19427 * pick multiple items from a combo box, and shows each one.
19429 * Fred [x] Brian [x] [Pick another |v]
19432 * For this to work: it needs various extra information
19433 * - normal combo problay has
19435 * + displayField, valueField
19437 * For our purpose...
19440 * If we change from 'extends' to wrapping...
19447 * Create a new ComboBoxArray.
19448 * @param {Object} config Configuration options
19452 Roo.form.ComboBoxArray = function(config)
19456 * @event beforeremove
19457 * Fires before remove the value from the list
19458 * @param {Roo.form.ComboBoxArray} _self This combo box array
19459 * @param {Roo.form.ComboBoxArray.Item} item removed item
19461 'beforeremove' : true,
19464 * Fires when remove the value from the list
19465 * @param {Roo.form.ComboBoxArray} _self This combo box array
19466 * @param {Roo.form.ComboBoxArray.Item} item removed item
19473 Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19475 this.items = new Roo.util.MixedCollection(false);
19477 // construct the child combo...
19487 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19490 * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
19495 // behavies liek a hiddne field
19496 inputType: 'hidden',
19498 * @cfg {Number} width The width of the box that displays the selected element
19505 * @cfg {String} name The name of the visable items on this form (eg. titles not ids)
19509 * @cfg {String} hiddenName The hidden name of the field, often contains an comma seperated list of names
19511 hiddenName : false,
19513 * @cfg {String} seperator The value seperator normally ','
19517 // private the array of items that are displayed..
19519 // private - the hidden field el.
19521 // private - the filed el..
19524 //validateValue : function() { return true; }, // all values are ok!
19525 //onAddClick: function() { },
19527 onRender : function(ct, position)
19530 // create the standard hidden element
19531 //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19534 // give fake names to child combo;
19535 this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19536 this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19538 this.combo = Roo.factory(this.combo, Roo.form);
19539 this.combo.onRender(ct, position);
19540 if (typeof(this.combo.width) != 'undefined') {
19541 this.combo.onResize(this.combo.width,0);
19544 this.combo.initEvents();
19546 // assigned so form know we need to do this..
19547 this.store = this.combo.store;
19548 this.valueField = this.combo.valueField;
19549 this.displayField = this.combo.displayField ;
19552 this.combo.wrap.addClass('x-cbarray-grp');
19554 var cbwrap = this.combo.wrap.createChild(
19555 {tag: 'div', cls: 'x-cbarray-cb'},
19560 this.hiddenEl = this.combo.wrap.createChild({
19561 tag: 'input', type:'hidden' , name: this.hiddenName, value : ''
19563 this.el = this.combo.wrap.createChild({
19564 tag: 'input', type:'hidden' , name: this.name, value : ''
19566 // this.el.dom.removeAttribute("name");
19569 this.outerWrap = this.combo.wrap;
19570 this.wrap = cbwrap;
19572 this.outerWrap.setWidth(this.width);
19573 this.outerWrap.dom.removeChild(this.el.dom);
19575 this.wrap.dom.appendChild(this.el.dom);
19576 this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19577 this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19579 this.combo.trigger.setStyle('position','relative');
19580 this.combo.trigger.setStyle('left', '0px');
19581 this.combo.trigger.setStyle('top', '2px');
19583 this.combo.el.setStyle('vertical-align', 'text-bottom');
19585 //this.trigger.setStyle('vertical-align', 'top');
19587 // this should use the code from combo really... on('add' ....)
19591 this.adder = this.outerWrap.createChild(
19592 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});
19594 this.adder.on('click', function(e) {
19595 _t.fireEvent('adderclick', this, e);
19599 //this.adder.on('click', this.onAddClick, _t);
19602 this.combo.on('select', function(cb, rec, ix) {
19603 this.addItem(rec.data);
19606 cb.el.dom.value = '';
19607 //cb.lastData = rec.data;
19616 getName: function()
19618 // returns hidden if it's set..
19619 if (!this.rendered) {return ''};
19620 return this.hiddenName ? this.hiddenName : this.name;
19625 onResize: function(w, h){
19628 // not sure if this is needed..
19629 //this.combo.onResize(w,h);
19631 if(typeof w != 'number'){
19632 // we do not handle it!?!?
19635 var tw = this.combo.trigger.getWidth();
19636 tw += this.addicon ? this.addicon.getWidth() : 0;
19637 tw += this.editicon ? this.editicon.getWidth() : 0;
19639 this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19641 this.combo.trigger.setStyle('left', '0px');
19643 if(this.list && this.listWidth === undefined){
19644 var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19645 this.list.setWidth(lw);
19646 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19653 addItem: function(rec)
19655 var valueField = this.combo.valueField;
19656 var displayField = this.combo.displayField;
19658 if (this.items.indexOfKey(rec[valueField]) > -1) {
19659 //console.log("GOT " + rec.data.id);
19663 var x = new Roo.form.ComboBoxArray.Item({
19664 //id : rec[this.idField],
19666 displayField : displayField ,
19667 tipField : displayField ,
19671 this.items.add(rec[valueField],x);
19672 // add it before the element..
19673 this.updateHiddenEl();
19674 x.render(this.outerWrap, this.wrap.dom);
19675 // add the image handler..
19678 updateHiddenEl : function()
19681 if (!this.hiddenEl) {
19685 var idField = this.combo.valueField;
19687 this.items.each(function(f) {
19688 ar.push(f.data[idField]);
19690 this.hiddenEl.dom.value = ar.join(this.seperator);
19696 this.items.clear();
19698 Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19702 this.el.dom.value = '';
19703 if (this.hiddenEl) {
19704 this.hiddenEl.dom.value = '';
19708 getValue: function()
19710 return this.hiddenEl ? this.hiddenEl.dom.value : '';
19712 setValue: function(v) // not a valid action - must use addItems..
19717 if (this.store.isLocal && (typeof(v) == 'string')) {
19718 // then we can use the store to find the values..
19719 // comma seperated at present.. this needs to allow JSON based encoding..
19720 this.hiddenEl.value = v;
19722 Roo.each(v.split(this.seperator), function(k) {
19723 Roo.log("CHECK " + this.valueField + ',' + k);
19724 var li = this.store.query(this.valueField, k);
19729 add[this.valueField] = k;
19730 add[this.displayField] = li.item(0).data[this.displayField];
19736 if (typeof(v) == 'object' ) {
19737 // then let's assume it's an array of objects..
19738 Roo.each(v, function(l) {
19740 if (typeof(l) == 'string') {
19742 add[this.valueField] = l;
19743 add[this.displayField] = l
19752 setFromData: function(v)
19754 // this recieves an object, if setValues is called.
19756 this.el.dom.value = v[this.displayField];
19757 this.hiddenEl.dom.value = v[this.valueField];
19758 if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19761 var kv = v[this.valueField];
19762 var dv = v[this.displayField];
19763 kv = typeof(kv) != 'string' ? '' : kv;
19764 dv = typeof(dv) != 'string' ? '' : dv;
19767 var keys = kv.split(this.seperator);
19768 var display = dv.split(this.seperator);
19769 for (var i = 0 ; i < keys.length; i++) {
19771 add[this.valueField] = keys[i];
19772 add[this.displayField] = display[i];
19780 * Validates the combox array value
19781 * @return {Boolean} True if the value is valid, else false
19783 validate : function(){
19784 if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19785 this.clearInvalid();
19791 validateValue : function(value){
19792 return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19800 isDirty : function() {
19801 if(this.disabled) {
19806 var d = Roo.decode(String(this.originalValue));
19808 return String(this.getValue()) !== String(this.originalValue);
19811 var originalValue = [];
19813 for (var i = 0; i < d.length; i++){
19814 originalValue.push(d[i][this.valueField]);
19817 return String(this.getValue()) !== String(originalValue.join(this.seperator));
19826 * @class Roo.form.ComboBoxArray.Item
19827 * @extends Roo.BoxComponent
19828 * A selected item in the list
19829 * Fred [x] Brian [x] [Pick another |v]
19832 * Create a new item.
19833 * @param {Object} config Configuration options
19836 Roo.form.ComboBoxArray.Item = function(config) {
19837 config.id = Roo.id();
19838 Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19841 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19844 displayField : false,
19848 defaultAutoCreate : {
19850 cls: 'x-cbarray-item',
19857 src : Roo.BLANK_IMAGE_URL ,
19865 onRender : function(ct, position)
19867 Roo.form.Field.superclass.onRender.call(this, ct, position);
19870 var cfg = this.getAutoCreate();
19871 this.el = ct.createChild(cfg, position);
19874 this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19876 this.el.child('div').dom.innerHTML = this.cb.renderer ?
19877 this.cb.renderer(this.data) :
19878 String.format('{0}',this.data[this.displayField]);
19881 this.el.child('div').dom.setAttribute('qtip',
19882 String.format('{0}',this.data[this.tipField])
19885 this.el.child('img').on('click', this.remove, this);
19889 remove : function()
19891 if(this.cb.disabled){
19895 if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19896 this.cb.items.remove(this);
19897 this.el.child('img').un('click', this.remove, this);
19899 this.cb.updateHiddenEl();
19901 this.cb.fireEvent('remove', this.cb, this);
19906 * RooJS Library 1.1.1
19907 * Copyright(c) 2008-2011 Alan Knowles
19914 * @class Roo.form.ComboNested
19915 * @extends Roo.form.ComboBox
19916 * A combobox for that allows selection of nested items in a list,
19931 * Create a new ComboNested
19932 * @param {Object} config Configuration options
19934 Roo.form.ComboNested = function(config){
19935 Roo.form.ComboCheck.superclass.constructor.call(this, config);
19936 // should verify some data...
19938 // hiddenName = required..
19939 // displayField = required
19940 // valudField == required
19941 var req= [ 'hiddenName', 'displayField', 'valueField' ];
19943 Roo.each(req, function(e) {
19944 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19945 throw "Roo.form.ComboNested : missing value for: " + e;
19952 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19955 * @config {Number} max Number of columns to show
19960 list : null, // the outermost div..
19961 innerLists : null, // the
19965 loadingChildren : false,
19967 onRender : function(ct, position)
19969 Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19971 if(this.hiddenName){
19972 this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)},
19974 this.hiddenField.value =
19975 this.hiddenValue !== undefined ? this.hiddenValue :
19976 this.value !== undefined ? this.value : '';
19978 // prevent input submission
19979 this.el.dom.removeAttribute('name');
19985 this.el.dom.setAttribute('autocomplete', 'off');
19988 var cls = 'x-combo-list';
19990 this.list = new Roo.Layer({
19991 shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19994 var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19995 this.list.setWidth(lw);
19996 this.list.swallowEvent('mousewheel');
19997 this.assetHeight = 0;
20000 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
20001 this.assetHeight += this.header.getHeight();
20003 this.innerLists = [];
20006 for (var i =0 ; i < this.maxColumns; i++) {
20007 this.onRenderList( cls, i);
20010 // always needs footer, as we are going to have an 'OK' button.
20011 this.footer = this.list.createChild({cls:cls+'-ft'});
20012 this.pageTb = new Roo.Toolbar(this.footer);
20017 handler: function()
20023 if ( this.allowBlank && !this.disableClear) {
20025 this.pageTb.add(new Roo.Toolbar.Fill(), {
20026 cls: 'x-btn-icon x-btn-clear',
20028 handler: function()
20031 _this.clearValue();
20032 _this.onSelect(false, -1);
20037 this.assetHeight += this.footer.getHeight();
20041 onRenderList : function ( cls, i)
20044 var lw = Math.floor(
20045 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20048 this.list.setWidth(lw); // default to '1'
20050 var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20051 //il.on('mouseover', this.onViewOver, this, { list: i });
20052 //il.on('mousemove', this.onViewMove, this, { list: i });
20054 il.setStyle({ 'overflow-x' : 'hidden'});
20057 this.tpl = new Roo.Template({
20058 html : '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20059 isEmpty: function (value, allValues) {
20061 var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20062 return dl ? 'has-children' : 'no-children'
20067 var store = this.store;
20069 store = new Roo.data.SimpleStore({
20070 //fields : this.store.reader.meta.fields,
20071 reader : this.store.reader,
20075 this.stores[i] = store;
20077 var view = this.views[i] = new Roo.View(
20083 selectedClass: this.selectedClass
20086 view.getEl().setWidth(lw);
20087 view.getEl().setStyle({
20088 position: i < 1 ? 'relative' : 'absolute',
20090 left: (i * lw ) + 'px',
20091 display : i > 0 ? 'none' : 'block'
20093 view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
20094 view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
20095 //view.on('click', this.onViewClick, this, { list : i });
20097 store.on('beforeload', this.onBeforeLoad, this);
20098 store.on('load', this.onLoad, this, { list : i});
20099 store.on('loadexception', this.onLoadException, this);
20101 // hide the other vies..
20107 restrictHeight : function()
20110 Roo.each(this.innerLists, function(il,i) {
20111 var el = this.views[i].getEl();
20112 el.dom.style.height = '';
20113 var inner = el.dom;
20114 var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
20115 // only adjust heights on other ones..
20116 mh = Math.max(h, mh);
20119 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20120 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20127 this.list.beginUpdate();
20128 this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20129 this.list.alignTo(this.el, this.listAlign);
20130 this.list.endUpdate();
20135 // -- store handlers..
20137 onBeforeLoad : function()
20139 if(!this.hasFocus){
20142 this.innerLists[0].update(this.loadingText ?
20143 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20144 this.restrictHeight();
20145 this.selectedIndex = -1;
20148 onLoad : function(a,b,c,d)
20150 if (!this.loadingChildren) {
20151 // then we are loading the top level. - hide the children
20152 for (var i = 1;i < this.views.length; i++) {
20153 this.views[i].getEl().setStyle({ display : 'none' });
20155 var lw = Math.floor(
20156 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20159 this.list.setWidth(lw); // default to '1'
20163 if(!this.hasFocus){
20167 if(this.store.getCount() > 0) {
20169 this.restrictHeight();
20171 this.onEmptyResults();
20174 if (!this.loadingChildren) {
20175 this.selectActive();
20178 this.stores[1].loadData([]);
20179 this.stores[2].loadData([]);
20188 onLoadException : function()
20191 Roo.log(this.store.reader.jsonData);
20192 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20193 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20198 // no cleaning of leading spaces on blur here.
20199 cleanLeadingSpace : function(e) { },
20202 onSelectChange : function (view, sels, opts )
20204 var ix = view.getSelectedIndexes();
20206 if (opts.list > this.maxColumns - 2) {
20207 if (view.store.getCount()< 1) {
20208 this.views[opts.list ].getEl().setStyle({ display : 'none' });
20212 // used to clear ?? but if we are loading unselected
20213 this.setFromData(view.store.getAt(ix[0]).data);
20222 // this get's fired when trigger opens..
20223 // this.setFromData({});
20224 var str = this.stores[opts.list+1];
20225 str.data.clear(); // removeall wihtout the fire events..
20229 var rec = view.store.getAt(ix[0]);
20231 this.setFromData(rec.data);
20232 this.fireEvent('select', this, rec, ix[0]);
20234 var lw = Math.floor(
20236 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20237 ) / this.maxColumns
20239 this.loadingChildren = true;
20240 this.stores[opts.list+1].loadDataFromChildren( rec );
20241 this.loadingChildren = false;
20242 var dl = this.stores[opts.list+1]. getTotalCount();
20244 this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20246 this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20247 for (var i = opts.list+2; i < this.views.length;i++) {
20248 this.views[i].getEl().setStyle({ display : 'none' });
20251 this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20252 this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20254 if (this.isLoading) {
20255 // this.selectActive(opts.list);
20263 onDoubleClick : function()
20265 this.collapse(); //??
20273 recordToStack : function(store, prop, value, stack)
20275 var cstore = new Roo.data.SimpleStore({
20276 //fields : this.store.reader.meta.fields, // we need array reader.. for
20277 reader : this.store.reader,
20281 var record = false;
20283 if(store.getCount() < 1){
20286 store.each(function(r){
20287 if(r.data[prop] == value){
20292 if (r.data.cn && r.data.cn.length) {
20293 cstore.loadDataFromChildren( r);
20294 var cret = _this.recordToStack(cstore, prop, value, stack);
20295 if (cret !== false) {
20304 if (record == false) {
20307 stack.unshift(srec);
20312 * find the stack of stores that match our value.
20317 selectActive : function ()
20319 // if store is not loaded, then we will need to wait for that to happen first.
20321 this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20322 for (var i = 0; i < stack.length; i++ ) {
20323 this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20335 * Ext JS Library 1.1.1
20336 * Copyright(c) 2006-2007, Ext JS, LLC.
20338 * Originally Released Under LGPL - original licence link has changed is not relivant.
20341 * <script type="text/javascript">
20344 * @class Roo.form.Checkbox
20345 * @extends Roo.form.Field
20346 * Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
20348 * Creates a new Checkbox
20349 * @param {Object} config Configuration options
20351 Roo.form.Checkbox = function(config){
20352 Roo.form.Checkbox.superclass.constructor.call(this, config);
20356 * Fires when the checkbox is checked or unchecked.
20357 * @param {Roo.form.Checkbox} this This checkbox
20358 * @param {Boolean} checked The new checked value
20364 Roo.extend(Roo.form.Checkbox, Roo.form.Field, {
20366 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20368 focusClass : undefined,
20370 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20372 fieldClass: "x-form-field",
20374 * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20378 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20379 * {tag: "input", type: "checkbox", autocomplete: "off"})
20381 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20383 * @cfg {String} boxLabel The text that appears beside the checkbox
20387 * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20391 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20393 valueOff: '0', // value when not checked..
20395 actionMode : 'viewEl',
20398 itemCls : 'x-menu-check-item x-form-item',
20399 groupClass : 'x-menu-group-item',
20400 inputType : 'hidden',
20403 inSetChecked: false, // check that we are not calling self...
20405 inputElement: false, // real input element?
20406 basedOn: false, // ????
20408 isFormField: true, // not sure where this is needed!!!!
20410 onResize : function(){
20411 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20412 if(!this.boxLabel){
20413 this.el.alignTo(this.wrap, 'c-c');
20417 initEvents : function(){
20418 Roo.form.Checkbox.superclass.initEvents.call(this);
20419 this.el.on("click", this.onClick, this);
20420 this.el.on("change", this.onClick, this);
20424 getResizeEl : function(){
20428 getPositionEl : function(){
20433 onRender : function(ct, position){
20434 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20436 if(this.inputValue !== undefined){
20437 this.el.dom.value = this.inputValue;
20440 //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20441 this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20442 var viewEl = this.wrap.createChild({
20443 tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20444 this.viewEl = viewEl;
20445 this.wrap.on('click', this.onClick, this);
20447 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20448 this.el.on('propertychange', this.setFromHidden, this); //ie
20453 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20454 // viewEl.on('click', this.onClick, this);
20456 //if(this.checked){
20457 this.setChecked(this.checked);
20459 //this.checked = this.el.dom;
20465 initValue : Roo.emptyFn,
20468 * Returns the checked state of the checkbox.
20469 * @return {Boolean} True if checked, else false
20471 getValue : function(){
20473 return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20475 return this.valueOff;
20480 onClick : function(){
20481 if (this.disabled) {
20484 this.setChecked(!this.checked);
20486 //if(this.el.dom.checked != this.checked){
20487 // this.setValue(this.el.dom.checked);
20492 * Sets the checked state of the checkbox.
20493 * On is always based on a string comparison between inputValue and the param.
20494 * @param {Boolean/String} value - the value to set
20495 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20497 setValue : function(v,suppressEvent){
20500 //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20501 //if(this.el && this.el.dom){
20502 // this.el.dom.checked = this.checked;
20503 // this.el.dom.defaultChecked = this.checked;
20505 this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20506 //this.fireEvent("check", this, this.checked);
20509 setChecked : function(state,suppressEvent)
20511 if (this.inSetChecked) {
20512 this.checked = state;
20518 this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20520 this.checked = state;
20521 if(suppressEvent !== true){
20522 this.fireEvent('check', this, state);
20524 this.inSetChecked = true;
20525 this.el.dom.value = state ? this.inputValue : this.valueOff;
20526 this.inSetChecked = false;
20529 // handle setting of hidden value by some other method!!?!?
20530 setFromHidden: function()
20535 //console.log("SET FROM HIDDEN");
20536 //alert('setFrom hidden');
20537 this.setValue(this.el.dom.value);
20540 onDestroy : function()
20543 Roo.get(this.viewEl).remove();
20546 Roo.form.Checkbox.superclass.onDestroy.call(this);
20549 setBoxLabel : function(str)
20551 this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20556 * Ext JS Library 1.1.1
20557 * Copyright(c) 2006-2007, Ext JS, LLC.
20559 * Originally Released Under LGPL - original licence link has changed is not relivant.
20562 * <script type="text/javascript">
20566 * @class Roo.form.Radio
20567 * @extends Roo.form.Checkbox
20568 * Single radio field. Same as Checkbox, but provided as a convenience for automatically setting the input type.
20569 * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20571 * Creates a new Radio
20572 * @param {Object} config Configuration options
20574 Roo.form.Radio = function(){
20575 Roo.form.Radio.superclass.constructor.apply(this, arguments);
20577 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20578 inputType: 'radio',
20581 * If this radio is part of a group, it will return the selected value
20584 getGroupValue : function(){
20585 return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20589 onRender : function(ct, position){
20590 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20592 if(this.inputValue !== undefined){
20593 this.el.dom.value = this.inputValue;
20596 this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20597 //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20598 //var viewEl = this.wrap.createChild({
20599 // tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20600 //this.viewEl = viewEl;
20601 //this.wrap.on('click', this.onClick, this);
20603 //this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
20604 //this.el.on('propertychange', this.setFromHidden, this); //ie
20609 this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20610 // viewEl.on('click', this.onClick, this);
20613 this.el.dom.checked = 'checked' ;
20619 });Roo.rtf = {}; // namespace
20620 Roo.rtf.Hex = function(hex)
20624 Roo.rtf.Paragraph = function(opts)
20626 this.content = []; ///??? is that used?
20627 };Roo.rtf.Span = function(opts)
20629 this.value = opts.value;
20632 Roo.rtf.Group = function(parent)
20634 // we dont want to acutally store parent - it will make debug a nightmare..
20642 Roo.rtf.Group.prototype = {
20646 addContent : function(node) {
20647 // could set styles...
20648 this.content.push(node);
20650 addChild : function(cn)
20654 // only for images really...
20655 toDataURL : function()
20657 var mimetype = false;
20659 case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0:
20660 mimetype = "image/png";
20662 case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
20663 mimetype = "image/jpeg";
20666 return 'about:blank'; // ?? error?
20670 var hexstring = this.content[this.content.length-1].value;
20672 return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
20673 return String.fromCharCode(parseInt(a, 16));
20678 // this looks like it's normally the {rtf{ .... }}
20679 Roo.rtf.Document = function()
20681 // we dont want to acutally store parent - it will make debug a nightmare..
20687 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, {
20688 addChild : function(cn)
20692 case 'rtlch': // most content seems to be inside this??
20695 this.rtlch.push(cn);
20698 this[cn.type] = cn;
20703 getElementsByType : function(type)
20706 this._getElementsByType(type, ret, this.cn, 'rtf');
20709 _getElementsByType : function (type, ret, search_array, path)
20711 search_array.forEach(function(n,i) {
20712 if (n.type == type) {
20713 n.path = path + '/' + n.type + ':' + i;
20716 if (n.cn.length > 0) {
20717 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
20724 Roo.rtf.Ctrl = function(opts)
20726 this.value = opts.value;
20727 this.param = opts.param;
20732 * based on this https://github.com/iarna/rtf-parser
20733 * it's really only designed to extract pict from pasted RTF
20737 * var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
20746 Roo.rtf.Parser = function(text) {
20747 //super({objectMode: true})
20749 this.parserState = this.parseText;
20751 // these are for interpeter...
20753 ///this.parserState = this.parseTop
20754 this.groupStack = [];
20755 this.hexStore = [];
20758 this.groups = []; // where we put the return.
20760 for (var ii = 0; ii < text.length; ++ii) {
20763 if (text[ii] === '\n') {
20769 this.parserState(text[ii]);
20775 Roo.rtf.Parser.prototype = {
20776 text : '', // string being parsed..
20778 controlWordParam : '',
20782 groupStack : false,
20787 row : 1, // reportin?
20791 push : function (el)
20793 var m = 'cmd'+ el.type;
20794 if (typeof(this[m]) == 'undefined') {
20795 Roo.log('invalid cmd:' + el.type);
20801 flushHexStore : function()
20803 if (this.hexStore.length < 1) {
20806 var hexstr = this.hexStore.map(
20811 this.group.addContent( new Roo.rtf.Hex( hexstr ));
20814 this.hexStore.splice(0)
20818 cmdgroupstart : function()
20820 this.flushHexStore();
20822 this.groupStack.push(this.group);
20825 if (this.doc === false) {
20826 this.group = this.doc = new Roo.rtf.Document();
20830 this.group = new Roo.rtf.Group(this.group);
20832 cmdignorable : function()
20834 this.flushHexStore();
20835 this.group.ignorable = true;
20837 cmdendparagraph : function()
20839 this.flushHexStore();
20840 this.group.addContent(new Roo.rtf.Paragraph());
20842 cmdgroupend : function ()
20844 this.flushHexStore();
20845 var endingGroup = this.group;
20848 this.group = this.groupStack.pop();
20850 this.group.addChild(endingGroup);
20855 var doc = this.group || this.doc;
20856 //if (endingGroup instanceof FontTable) {
20857 // doc.fonts = endingGroup.table
20858 //} else if (endingGroup instanceof ColorTable) {
20859 // doc.colors = endingGroup.table
20860 //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
20861 if (endingGroup.ignorable === false) {
20863 this.groups.push(endingGroup);
20864 // Roo.log( endingGroup );
20866 //Roo.each(endingGroup.content, function(item)) {
20867 // doc.addContent(item);
20869 //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
20872 cmdtext : function (cmd)
20874 this.flushHexStore();
20875 if (!this.group) { // an RTF fragment, missing the {\rtf1 header
20876 //this.group = this.doc
20877 return; // we really don't care about stray text...
20879 this.group.addContent(new Roo.rtf.Span(cmd));
20881 cmdcontrolword : function (cmd)
20883 this.flushHexStore();
20884 if (!this.group.type) {
20885 this.group.type = cmd.value;
20888 this.group.addContent(new Roo.rtf.Ctrl(cmd));
20889 // we actually don't care about ctrl words...
20892 var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
20893 if (this[method]) {
20894 this[method](cmd.param)
20896 if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
20900 cmdhexchar : function(cmd) {
20901 this.hexStore.push(cmd);
20903 cmderror : function(cmd) {
20909 if (this.text !== '\u0000') this.emitText()
20915 parseText : function(c)
20918 this.parserState = this.parseEscapes;
20919 } else if (c === '{') {
20920 this.emitStartGroup();
20921 } else if (c === '}') {
20922 this.emitEndGroup();
20923 } else if (c === '\x0A' || c === '\x0D') {
20924 // cr/lf are noise chars
20930 parseEscapes: function (c)
20932 if (c === '\\' || c === '{' || c === '}') {
20934 this.parserState = this.parseText;
20936 this.parserState = this.parseControlSymbol;
20937 this.parseControlSymbol(c);
20940 parseControlSymbol: function(c)
20943 this.text += '\u00a0'; // nbsp
20944 this.parserState = this.parseText
20945 } else if (c === '-') {
20946 this.text += '\u00ad'; // soft hyphen
20947 } else if (c === '_') {
20948 this.text += '\u2011'; // non-breaking hyphen
20949 } else if (c === '*') {
20950 this.emitIgnorable();
20951 this.parserState = this.parseText;
20952 } else if (c === "'") {
20953 this.parserState = this.parseHexChar;
20954 } else if (c === '|') { // formula cacter
20955 this.emitFormula();
20956 this.parserState = this.parseText;
20957 } else if (c === ':') { // subentry in an index entry
20958 this.emitIndexSubEntry();
20959 this.parserState = this.parseText;
20960 } else if (c === '\x0a') {
20961 this.emitEndParagraph();
20962 this.parserState = this.parseText;
20963 } else if (c === '\x0d') {
20964 this.emitEndParagraph();
20965 this.parserState = this.parseText;
20967 this.parserState = this.parseControlWord;
20968 this.parseControlWord(c);
20971 parseHexChar: function (c)
20973 if (/^[A-Fa-f0-9]$/.test(c)) {
20975 if (this.hexChar.length >= 2) {
20976 this.emitHexChar();
20977 this.parserState = this.parseText;
20981 this.emitError("Invalid character \"" + c + "\" in hex literal.");
20982 this.parserState = this.parseText;
20985 parseControlWord : function(c)
20988 this.emitControlWord();
20989 this.parserState = this.parseText;
20990 } else if (/^[-\d]$/.test(c)) {
20991 this.parserState = this.parseControlWordParam;
20992 this.controlWordParam += c;
20993 } else if (/^[A-Za-z]$/.test(c)) {
20994 this.controlWord += c;
20996 this.emitControlWord();
20997 this.parserState = this.parseText;
21001 parseControlWordParam : function (c) {
21002 if (/^\d$/.test(c)) {
21003 this.controlWordParam += c;
21004 } else if (c === ' ') {
21005 this.emitControlWord();
21006 this.parserState = this.parseText;
21008 this.emitControlWord();
21009 this.parserState = this.parseText;
21017 emitText : function () {
21018 if (this.text === '') {
21030 emitControlWord : function ()
21033 if (this.controlWord === '') {
21034 // do we want to track this - it seems just to cause problems.
21035 //this.emitError('empty control word');
21038 type: 'controlword',
21039 value: this.controlWord,
21040 param: this.controlWordParam !== '' && Number(this.controlWordParam),
21046 this.controlWord = '';
21047 this.controlWordParam = '';
21049 emitStartGroup : function ()
21053 type: 'groupstart',
21059 emitEndGroup : function ()
21069 emitIgnorable : function ()
21079 emitHexChar : function ()
21084 value: this.hexChar,
21091 emitError : function (message)
21099 char: this.cpos //,
21100 //stack: new Error().stack
21103 emitEndParagraph : function () {
21106 type: 'endparagraph',
21114 Roo.htmleditor = {};
21117 * @class Roo.htmleditor.Filter
21118 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
21119 * @cfg {DomElement} node The node to iterate and filter
21120 * @cfg {boolean|String|Array} tag Tags to replace
21122 * Create a new Filter.
21123 * @param {Object} config Configuration options
21128 Roo.htmleditor.Filter = function(cfg) {
21129 Roo.apply(this.cfg);
21130 // this does not actually call walk as it's really just a abstract class
21134 Roo.htmleditor.Filter.prototype = {
21140 // overrride to do replace comments.
21141 replaceComment : false,
21143 // overrride to do replace or do stuff with tags..
21144 replaceTag : false,
21146 walk : function(dom)
21148 Roo.each( Array.from(dom.childNodes), function( e ) {
21151 case e.nodeType == 8 && this.replaceComment !== false: // comment
21152 this.replaceComment(e);
21155 case e.nodeType != 1: //not a node.
21158 case this.tag === true: // everything
21159 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
21160 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
21161 if (this.replaceTag && false === this.replaceTag(e)) {
21164 if (e.hasChildNodes()) {
21169 default: // tags .. that do not match.
21170 if (e.hasChildNodes()) {
21181 * @class Roo.htmleditor.FilterAttributes
21182 * clean attributes and styles including http:// etc.. in attribute
21184 * Run a new Attribute Filter
21185 * @param {Object} config Configuration options
21187 Roo.htmleditor.FilterAttributes = function(cfg)
21189 Roo.apply(this, cfg);
21190 this.attrib_black = this.attrib_black || [];
21191 this.attrib_white = this.attrib_white || [];
21193 this.attrib_clean = this.attrib_clean || [];
21194 this.style_white = this.style_white || [];
21195 this.style_black = this.style_black || [];
21196 this.walk(cfg.node);
21199 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
21201 tag: true, // all tags
21203 attrib_black : false, // array
21204 attrib_clean : false,
21205 attrib_white : false,
21207 style_white : false,
21208 style_black : false,
21211 replaceTag : function(node)
21213 if (!node.attributes || !node.attributes.length) {
21217 for (var i = node.attributes.length-1; i > -1 ; i--) {
21218 var a = node.attributes[i];
21220 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
21221 node.removeAttribute(a.name);
21227 if (a.name.toLowerCase().substr(0,2)=='on') {
21228 node.removeAttribute(a.name);
21233 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
21234 node.removeAttribute(a.name);
21237 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
21238 this.cleanAttr(node,a.name,a.value); // fixme..
21241 if (a.name == 'style') {
21242 this.cleanStyle(node,a.name,a.value);
21245 /// clean up MS crap..
21246 // tecnically this should be a list of valid class'es..
21249 if (a.name == 'class') {
21250 if (a.value.match(/^Mso/)) {
21251 node.removeAttribute('class');
21254 if (a.value.match(/^body$/)) {
21255 node.removeAttribute('class');
21265 return true; // clean children
21268 cleanAttr: function(node, n,v)
21271 if (v.match(/^\./) || v.match(/^\//)) {
21274 if (v.match(/^(http|https):\/\//)
21275 || v.match(/^mailto:/)
21276 || v.match(/^ftp:/)
21277 || v.match(/^data:/)
21281 if (v.match(/^#/)) {
21284 if (v.match(/^\{/)) { // allow template editing.
21287 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21288 node.removeAttribute(n);
21291 cleanStyle : function(node, n,v)
21293 if (v.match(/expression/)) { //XSS?? should we even bother..
21294 node.removeAttribute(n);
21298 var parts = v.split(/;/);
21301 Roo.each(parts, function(p) {
21302 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21306 var l = p.split(':').shift().replace(/\s+/g,'');
21307 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21309 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
21313 // only allow 'c whitelisted system attributes'
21314 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
21322 if (clean.length) {
21323 node.setAttribute(n, clean.join(';'));
21325 node.removeAttribute(n);
21334 * @class Roo.htmleditor.FilterBlack
21335 * remove blacklisted elements.
21337 * Run a new Blacklisted Filter
21338 * @param {Object} config Configuration options
21341 Roo.htmleditor.FilterBlack = function(cfg)
21343 Roo.apply(this, cfg);
21344 this.walk(cfg.node);
21347 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
21349 tag : true, // all elements.
21351 replaceTag : function(n)
21353 n.parentNode.removeChild(n);
21357 * @class Roo.htmleditor.FilterComment
21360 * Run a new Comments Filter
21361 * @param {Object} config Configuration options
21363 Roo.htmleditor.FilterComment = function(cfg)
21365 this.walk(cfg.node);
21368 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
21371 replaceComment : function(n)
21373 n.parentNode.removeChild(n);
21376 * @class Roo.htmleditor.FilterKeepChildren
21377 * remove tags but keep children
21379 * Run a new Keep Children Filter
21380 * @param {Object} config Configuration options
21383 Roo.htmleditor.FilterKeepChildren = function(cfg)
21385 Roo.apply(this, cfg);
21386 if (this.tag === false) {
21387 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
21389 this.walk(cfg.node);
21392 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
21396 replaceTag : function(node)
21398 // walk children...
21400 var ar = Array.from(node.childNodes);
21402 for (var i = 0; i < ar.length; i++) {
21403 if (ar[i].nodeType == 1) {
21405 (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
21406 || // array and it matches
21407 (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
21409 this.replaceTag(ar[i]); // child is blacklisted as well...
21414 ar = Array.from(node.childNodes);
21415 for (var i = 0; i < ar.length; i++) {
21417 node.removeChild(ar[i]);
21418 // what if we need to walk these???
21419 node.parentNode.insertBefore(ar[i], node);
21420 if (this.tag !== false) {
21425 node.parentNode.removeChild(node);
21426 return false; // don't walk children
21431 * @class Roo.htmleditor.FilterParagraph
21432 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
21433 * like on 'push' to remove the <p> tags and replace them with line breaks.
21435 * Run a new Paragraph Filter
21436 * @param {Object} config Configuration options
21439 Roo.htmleditor.FilterParagraph = function(cfg)
21441 // no need to apply config.
21442 this.walk(cfg.node);
21445 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
21452 replaceTag : function(node)
21455 if (node.childNodes.length == 1 &&
21456 node.childNodes[0].nodeType == 3 &&
21457 node.childNodes[0].textContent.trim().length < 1
21459 // remove and replace with '<BR>';
21460 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
21461 return false; // no need to walk..
21463 var ar = Array.from(node.childNodes);
21464 for (var i = 0; i < ar.length; i++) {
21465 node.removeChild(ar[i]);
21466 // what if we need to walk these???
21467 node.parentNode.insertBefore(ar[i], node);
21469 // now what about this?
21473 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21474 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
21475 node.parentNode.removeChild(node);
21482 * @class Roo.htmleditor.FilterSpan
21483 * filter span's with no attributes out..
21485 * Run a new Span Filter
21486 * @param {Object} config Configuration options
21489 Roo.htmleditor.FilterSpan = function(cfg)
21491 // no need to apply config.
21492 this.walk(cfg.node);
21495 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
21501 replaceTag : function(node)
21503 if (node.attributes && node.attributes.length > 0) {
21504 return true; // walk if there are any.
21506 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
21512 * @class Roo.htmleditor.FilterTableWidth
21513 try and remove table width data - as that frequently messes up other stuff.
21515 * was cleanTableWidths.
21517 * Quite often pasting from word etc.. results in tables with column and widths.
21518 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21521 * Run a new Table Filter
21522 * @param {Object} config Configuration options
21525 Roo.htmleditor.FilterTableWidth = function(cfg)
21527 // no need to apply config.
21528 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
21529 this.walk(cfg.node);
21532 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
21537 replaceTag: function(node) {
21541 if (node.hasAttribute('width')) {
21542 node.removeAttribute('width');
21546 if (node.hasAttribute("style")) {
21549 var styles = node.getAttribute("style").split(";");
21551 Roo.each(styles, function(s) {
21552 if (!s.match(/:/)) {
21555 var kv = s.split(":");
21556 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21559 // what ever is left... we allow.
21562 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21563 if (!nstyle.length) {
21564 node.removeAttribute('style');
21568 return true; // continue doing children..
21571 * @class Roo.htmleditor.FilterWord
21572 * try and clean up all the mess that Word generates.
21574 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
21577 * Run a new Span Filter
21578 * @param {Object} config Configuration options
21581 Roo.htmleditor.FilterWord = function(cfg)
21583 // no need to apply config.
21584 this.replaceDocBullets(cfg.node);
21586 // this.walk(cfg.node);
21591 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
21597 * Clean up MS wordisms...
21599 replaceTag : function(node)
21602 // no idea what this does - span with text, replaceds with just text.
21604 node.nodeName == 'SPAN' &&
21605 !node.hasAttributes() &&
21606 node.childNodes.length == 1 &&
21607 node.firstChild.nodeName == "#text"
21609 var textNode = node.firstChild;
21610 node.removeChild(textNode);
21611 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
21612 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21614 node.parentNode.insertBefore(textNode, node);
21615 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
21616 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21619 node.parentNode.removeChild(node);
21620 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
21625 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21626 node.parentNode.removeChild(node);
21627 return false; // dont do chidlren
21629 //Roo.log(node.tagName);
21630 // remove - but keep children..
21631 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21632 //Roo.log('-- removed');
21633 while (node.childNodes.length) {
21634 var cn = node.childNodes[0];
21635 node.removeChild(cn);
21636 node.parentNode.insertBefore(cn, node);
21637 // move node to parent - and clean it..
21638 if (cn.nodeType == 1) {
21639 this.replaceTag(cn);
21643 node.parentNode.removeChild(node);
21644 /// no need to iterate chidlren = it's got none..
21645 //this.iterateChildren(node, this.cleanWord);
21646 return false; // no need to iterate children.
21649 if (node.className.length) {
21651 var cn = node.className.split(/\W+/);
21653 Roo.each(cn, function(cls) {
21654 if (cls.match(/Mso[a-zA-Z]+/)) {
21659 node.className = cna.length ? cna.join(' ') : '';
21661 node.removeAttribute("class");
21665 if (node.hasAttribute("lang")) {
21666 node.removeAttribute("lang");
21669 if (node.hasAttribute("style")) {
21671 var styles = node.getAttribute("style").split(";");
21673 Roo.each(styles, function(s) {
21674 if (!s.match(/:/)) {
21677 var kv = s.split(":");
21678 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21681 // what ever is left... we allow.
21684 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21685 if (!nstyle.length) {
21686 node.removeAttribute('style');
21689 return true; // do children
21695 styleToObject: function(node)
21697 var styles = (node.getAttribute("style") || '').split(";");
21699 Roo.each(styles, function(s) {
21700 if (!s.match(/:/)) {
21703 var kv = s.split(":");
21705 // what ever is left... we allow.
21706 ret[kv[0]] = kv[1];
21712 replaceDocBullets : function(doc)
21714 // this is a bit odd - but it appears some indents use ql-indent-1
21716 var listpara = doc.getElementsByClassName('ql-indent-1');
21717 while(listpara.length) {
21718 this.replaceDocBullet(listpara.item(0));
21721 var listpara = doc.getElementsByClassName('MsoListParagraph');
21722 while(listpara.length) {
21723 this.replaceDocBullet(listpara.item(0));
21727 replaceDocBullet : function(p)
21729 // gather all the siblings.
21731 parent = p.parentNode,
21732 doc = parent.ownerDocument,
21735 if (ns.nodeType != 1) {
21736 ns = ns.nextSibling;
21739 if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
21743 ns = ns.nextSibling;
21746 var ul = parent.ownerDocument.createElement('ul'); // what about number lists...
21747 parent.insertBefore(ul, p);
21749 var stack = [ ul ];
21750 var last_li = false;
21751 items.forEach(function(n) {
21752 //Roo.log("got innertHMLT=" + n.innerHTML);
21754 var spans = n.getElementsByTagName('span');
21755 if (!spans.length) {
21756 //Roo.log("No spans found");
21758 parent.removeChild(n);
21759 return; // skip it...
21765 for(var i = 0; i < spans.length; i++) {
21767 style = this.styleToObject(spans[i]);
21768 if (typeof(style['mso-list']) == 'undefined') {
21772 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
21775 //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
21776 style = this.styleToObject(n); // mo-list is from the parent node.
21777 if (typeof(style['mso-list']) == 'undefined') {
21778 //Roo.log("parent is missing level");
21779 parent.removeChild(n);
21783 var nlvl = (style['mso-list'].split(' ')[1].replace(/level/,'') *1) - 1;
21786 var nul = doc.createElement('ul'); // what about number lists...
21787 last_li.appendChild(nul);
21792 var nli = stack[nlvl].appendChild(doc.createElement('li'));
21794 nli.innerHTML = n.innerHTML;
21795 //Roo.log("innerHTML = " + n.innerHTML);
21796 parent.removeChild(n);
21798 // copy children of p into nli
21799 /*while(n.firstChild) {
21800 var fc = n.firstChild;
21802 nli.appendChild(fc);
21817 * @class Roo.htmleditor.FilterStyleToTag
21818 * part of the word stuff... - certain 'styles' should be converted to tags.
21820 * font-weight: bold -> bold
21821 * ?? super / subscrit etc..
21824 * Run a new style to tag filter.
21825 * @param {Object} config Configuration options
21827 Roo.htmleditor.FilterStyleToTag = function(cfg)
21831 B : [ 'fontWeight' , 'bold'],
21832 I : [ 'fontStyle' , 'italic'],
21833 //pre : [ 'font-style' , 'italic'],
21834 // h1.. h6 ?? font-size?
21835 SUP : [ 'verticalAlign' , 'super' ],
21836 SUB : [ 'verticalAlign' , 'sub' ]
21841 Roo.apply(this, cfg);
21844 this.walk(cfg.node);
21851 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
21853 tag: true, // all tags
21858 replaceTag : function(node)
21862 if (node.getAttribute("style") === null) {
21866 for (var k in this.tags) {
21867 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
21869 node.style.removeProperty(this.tags[k][0]);
21872 if (!inject.length) {
21875 var cn = Array.from(node.childNodes);
21877 Roo.each(inject, function(t) {
21878 var nc = node.ownerDocument.createElement(t);
21879 nn.appendChild(nc);
21882 for(var i = 0;i < cn.length;cn++) {
21883 node.removeChild(cn[i]);
21884 nn.appendChild(cn[i]);
21886 return true /// iterate thru
21890 * @class Roo.htmleditor.FilterLongBr
21891 * BR/BR/BR - keep a maximum of 2...
21893 * Run a new Long BR Filter
21894 * @param {Object} config Configuration options
21897 Roo.htmleditor.FilterLongBr = function(cfg)
21899 // no need to apply config.
21900 this.walk(cfg.node);
21903 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
21910 replaceTag : function(node)
21913 var ps = node.nextSibling;
21914 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21915 ps = ps.nextSibling;
21918 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
21919 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
21923 if (!ps || ps.nodeType != 1) {
21927 if (!ps || ps.tagName != 'BR') {
21936 if (!node.previousSibling) {
21939 var ps = node.previousSibling;
21941 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
21942 ps = ps.previousSibling;
21944 if (!ps || ps.nodeType != 1) {
21947 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
21948 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
21952 node.parentNode.removeChild(node); // remove me...
21954 return false; // no need to do children
21961 * @class Roo.htmleditor.FilterBlock
21962 * removes id / data-block and contenteditable that are associated with blocks
21963 * usage should be done on a cloned copy of the dom
21965 * Run a new Attribute Filter { node : xxxx }}
21966 * @param {Object} config Configuration options
21968 Roo.htmleditor.FilterBlock = function(cfg)
21970 Roo.apply(this, cfg);
21971 var qa = cfg.node.querySelectorAll;
21972 this.removeAttributes('data-block');
21973 this.removeAttributes('contenteditable');
21974 this.removeAttributes('id');
21978 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
21980 node: true, // all tags
21983 removeAttributes : function(attr)
21985 var ar = this.node.querySelectorAll('*[' + attr + ']');
21986 for (var i =0;i<ar.length;i++) {
21987 ar[i].removeAttribute(attr);
21996 * This is based loosely on tinymce
21997 * @class Roo.htmleditor.TidySerializer
21998 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
22000 * @method Serializer
22001 * @param {Object} settings Name/value settings object.
22005 Roo.htmleditor.TidySerializer = function(settings)
22007 Roo.apply(this, settings);
22009 this.writer = new Roo.htmleditor.TidyWriter(settings);
22014 Roo.htmleditor.TidySerializer.prototype = {
22017 * @param {boolean} inner do the inner of the node.
22024 * Serializes the specified node into a string.
22027 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
22028 * @method serialize
22029 * @param {DomElement} node Node instance to serialize.
22030 * @return {String} String with HTML based on DOM tree.
22032 serialize : function(node) {
22034 // = settings.validate;
22035 var writer = this.writer;
22039 3: function(node) {
22041 writer.text(node.nodeValue, node);
22044 8: function(node) {
22045 writer.comment(node.nodeValue);
22047 // Processing instruction
22048 7: function(node) {
22049 writer.pi(node.name, node.nodeValue);
22052 10: function(node) {
22053 writer.doctype(node.nodeValue);
22056 4: function(node) {
22057 writer.cdata(node.nodeValue);
22059 // Document fragment
22060 11: function(node) {
22061 node = node.firstChild;
22067 node = node.nextSibling
22072 1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
22073 return writer.getContent();
22076 walk: function(node)
22078 var attrName, attrValue, sortedAttrs, i, l, elementRule,
22079 handler = this.handlers[node.nodeType];
22086 var name = node.nodeName;
22087 var isEmpty = node.childNodes.length < 1;
22089 var writer = this.writer;
22090 var attrs = node.attributes;
22093 writer.start(node.nodeName, attrs, isEmpty, node);
22097 node = node.firstChild;
22104 node = node.nextSibling;
22110 // Serialize element and treat all non elements as fragments
22115 * This is based loosely on tinymce
22116 * @class Roo.htmleditor.TidyWriter
22117 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
22120 * - not tested much with 'PRE' formated elements.
22126 Roo.htmleditor.TidyWriter = function(settings)
22129 // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
22130 Roo.apply(this, settings);
22134 this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
22137 Roo.htmleditor.TidyWriter.prototype = {
22144 // part of state...
22148 last_inline : false,
22153 * Writes the a start element such as <p id="a">.
22156 * @param {String} name Name of the element.
22157 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
22158 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
22160 start: function(name, attrs, empty, node)
22162 var i, l, attr, value;
22164 // there are some situations where adding line break && indentation will not work. will not work.
22165 // <span / b / i ... formating?
22167 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22168 var in_pre = this.in_pre || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
22170 var is_short = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
22172 var add_lb = name == 'BR' ? false : in_inline;
22174 if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
22178 var indentstr = this.indentstr;
22180 // e_inline = elements that can be inline, but still allow \n before and after?
22181 // only 'BR' ??? any others?
22183 // ADD LINE BEFORE tage
22184 if (!this.in_pre) {
22187 if (name == 'BR') {
22189 } else if (this.lastElementEndsWS()) {
22192 // otherwise - no new line. (and dont indent.)
22203 this.html.push(indentstr + '<', name.toLowerCase());
22206 for (i = 0, l = attrs.length; i < l; i++) {
22208 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
22214 this.html[this.html.length] = '/>';
22216 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
22218 var e_inline = name == 'BR' ? false : this.in_inline;
22220 if (!e_inline && !this.in_pre) {
22227 this.html[this.html.length] = '>';
22229 // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
22231 if (!in_inline && !in_pre) {
22232 var cn = node.firstChild;
22234 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
22238 cn = cn.nextSibling;
22246 indentstr : in_pre ? '' : (this.indentstr + this.indent),
22248 in_inline : in_inline
22250 // add a line after if we are not in a
22252 if (!in_inline && !in_pre) {
22261 lastElementEndsWS : function()
22263 var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
22264 if (value === false) {
22267 return value.match(/\s+$/);
22272 * Writes the a end element such as </p>.
22275 * @param {String} name Name of the element.
22277 end: function(name) {
22280 var indentstr = '';
22281 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
22283 if (!this.in_pre && !in_inline) {
22285 indentstr = this.indentstr;
22287 this.html.push(indentstr + '</', name.toLowerCase(), '>');
22288 this.last_inline = in_inline;
22290 // pop the indent state..
22293 * Writes a text node.
22295 * In pre - we should not mess with the contents.
22299 * @param {String} text String to write out.
22300 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
22302 text: function(in_text, node)
22304 // if not in whitespace critical
22305 if (in_text.length < 1) {
22308 var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
22311 this.html[this.html.length] = text;
22315 if (this.in_inline) {
22316 text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
22318 text = text.replace(/\s+/,' '); // all white space to single white space
22321 // if next tag is '<BR>', then we can trim right..
22322 if (node.nextSibling &&
22323 node.nextSibling.nodeType == 1 &&
22324 node.nextSibling.nodeName == 'BR' )
22326 text = text.replace(/\s+$/g,'');
22328 // if previous tag was a BR, we can also trim..
22329 if (node.previousSibling &&
22330 node.previousSibling.nodeType == 1 &&
22331 node.previousSibling.nodeName == 'BR' )
22333 text = this.indentstr + text.replace(/^\s+/g,'');
22335 if (text.match(/\n/)) {
22336 text = text.replace(
22337 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22339 // remoeve the last whitespace / line break.
22340 text = text.replace(/\n\s+$/,'');
22342 // repace long lines
22346 this.html[this.html.length] = text;
22349 // see if previous element was a inline element.
22350 var indentstr = this.indentstr;
22352 text = text.replace(/\s+/g," "); // all whitespace into single white space.
22354 // should trim left?
22355 if (node.previousSibling &&
22356 node.previousSibling.nodeType == 1 &&
22357 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
22363 text = text.replace(/^\s+/,''); // trim left
22366 // should trim right?
22367 if (node.nextSibling &&
22368 node.nextSibling.nodeType == 1 &&
22369 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
22374 text = text.replace(/\s+$/,''); // trim right
22381 if (text.length < 1) {
22384 if (!text.match(/\n/)) {
22385 this.html.push(indentstr + text);
22389 text = this.indentstr + text.replace(
22390 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
22392 // remoeve the last whitespace / line break.
22393 text = text.replace(/\s+$/,'');
22395 this.html.push(text);
22397 // split and indent..
22402 * Writes a cdata node such as <![CDATA[data]]>.
22405 * @param {String} text String to write out inside the cdata.
22407 cdata: function(text) {
22408 this.html.push('<![CDATA[', text, ']]>');
22411 * Writes a comment node such as <!-- Comment -->.
22414 * @param {String} text String to write out inside the comment.
22416 comment: function(text) {
22417 this.html.push('<!--', text, '-->');
22420 * Writes a PI node such as <?xml attr="value" ?>.
22423 * @param {String} name Name of the pi.
22424 * @param {String} text String to write out inside the pi.
22426 pi: function(name, text) {
22427 text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
22428 this.indent != '' && this.html.push('\n');
22431 * Writes a doctype node such as <!DOCTYPE data>.
22434 * @param {String} text String to write out inside the doctype.
22436 doctype: function(text) {
22437 this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
22440 * Resets the internal buffer if one wants to reuse the writer.
22444 reset: function() {
22445 this.html.length = 0;
22454 * Returns the contents that got serialized.
22456 * @method getContent
22457 * @return {String} HTML contents that got written down.
22459 getContent: function() {
22460 return this.html.join('').replace(/\n$/, '');
22463 pushState : function(cfg)
22465 this.state.push(cfg);
22466 Roo.apply(this, cfg);
22469 popState : function()
22471 if (this.state.length < 1) {
22472 return; // nothing to push
22479 if (this.state.length > 0) {
22480 cfg = this.state[this.state.length-1];
22482 Roo.apply(this, cfg);
22485 addLine: function()
22487 if (this.html.length < 1) {
22492 var value = this.html[this.html.length - 1];
22493 if (value.length > 0 && '\n' !== value) {
22494 this.html.push('\n');
22499 //'pre script noscript style textarea video audio iframe object code'
22500 // shortended... 'area base basefont br col frame hr img input isindex link meta param embed source wbr track');
22504 Roo.htmleditor.TidyWriter.inline_elements = [
22505 'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
22506 'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
22508 Roo.htmleditor.TidyWriter.shortend_elements = [
22509 'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
22510 'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
22513 Roo.htmleditor.TidyWriter.whitespace_elements = [
22514 'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
22516 * This is based loosely on tinymce
22517 * @class Roo.htmleditor.TidyEntities
22519 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
22521 * Not 100% sure this is actually used or needed.
22524 Roo.htmleditor.TidyEntities = {
22527 * initialize data..
22529 init : function (){
22531 this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
22536 buildEntitiesLookup: function(items, radix) {
22537 var i, chr, entity, lookup = {};
22541 items = typeof(items) == 'string' ? items.split(',') : items;
22542 radix = radix || 10;
22543 // Build entities lookup table
22544 for (i = 0; i < items.length; i += 2) {
22545 chr = String.fromCharCode(parseInt(items[i], radix));
22546 // Only add non base entities
22547 if (!this.baseEntities[chr]) {
22548 entity = '&' + items[i + 1] + ';';
22549 lookup[chr] = entity;
22550 lookup[entity] = chr;
22589 // Needs to be escaped since the YUI compressor would otherwise break the code
22596 // Reverse lookup table for raw entities
22597 reverseEntities : {
22605 attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22606 textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
22607 rawCharsRegExp : /[<>&\"\']/g,
22608 entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
22609 namedEntities : false,
22610 namedEntitiesData : [
23111 * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
23113 * @method encodeRaw
23114 * @param {String} text Text to encode.
23115 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23116 * @return {String} Entity encoded text.
23118 encodeRaw: function(text, attr)
23121 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23122 return t.baseEntities[chr] || chr;
23126 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
23127 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
23128 * and is exposed as the DOMUtils.encode function.
23130 * @method encodeAllRaw
23131 * @param {String} text Text to encode.
23132 * @return {String} Entity encoded text.
23134 encodeAllRaw: function(text) {
23136 return ('' + text).replace(this.rawCharsRegExp, function(chr) {
23137 return t.baseEntities[chr] || chr;
23141 * Encodes the specified string using numeric entities. The core entities will be
23142 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
23144 * @method encodeNumeric
23145 * @param {String} text Text to encode.
23146 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23147 * @return {String} Entity encoded text.
23149 encodeNumeric: function(text, attr) {
23151 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23152 // Multi byte sequence convert it to a single entity
23153 if (chr.length > 1) {
23154 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
23156 return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
23160 * Encodes the specified string using named entities. The core entities will be encoded
23161 * as named ones but all non lower ascii characters will be encoded into named entities.
23163 * @method encodeNamed
23164 * @param {String} text Text to encode.
23165 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
23166 * @param {Object} entities Optional parameter with entities to use.
23167 * @return {String} Entity encoded text.
23169 encodeNamed: function(text, attr, entities) {
23171 entities = entities || this.namedEntities;
23172 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
23173 return t.baseEntities[chr] || entities[chr] || chr;
23177 * Returns an encode function based on the name(s) and it's optional entities.
23179 * @method getEncodeFunc
23180 * @param {String} name Comma separated list of encoders for example named,numeric.
23181 * @param {String} entities Optional parameter with entities to use instead of the built in set.
23182 * @return {function} Encode function to be used.
23184 getEncodeFunc: function(name, entities) {
23185 entities = this.buildEntitiesLookup(entities) || this.namedEntities;
23187 function encodeNamedAndNumeric(text, attr) {
23188 return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
23189 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
23193 function encodeCustomNamed(text, attr) {
23194 return t.encodeNamed(text, attr, entities);
23196 // Replace + with , to be compatible with previous TinyMCE versions
23197 name = this.makeMap(name.replace(/\+/g, ','));
23198 // Named and numeric encoder
23199 if (name.named && name.numeric) {
23200 return this.encodeNamedAndNumeric;
23206 return encodeCustomNamed;
23208 return this.encodeNamed;
23211 if (name.numeric) {
23212 return this.encodeNumeric;
23215 return this.encodeRaw;
23218 * Decodes the specified string, this will replace entities with raw UTF characters.
23221 * @param {String} text Text to entity decode.
23222 * @return {String} Entity decoded string.
23224 decode: function(text)
23227 return text.replace(this.entityRegExp, function(all, numeric) {
23229 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
23230 // Support upper UTF
23231 if (numeric > 65535) {
23233 return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
23235 return t.asciiMap[numeric] || String.fromCharCode(numeric);
23237 return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
23240 nativeDecode : function (text) {
23243 makeMap : function (items, delim, map) {
23245 items = items || [];
23246 delim = delim || ',';
23247 if (typeof items == "string") {
23248 items = items.split(delim);
23253 map[items[i]] = {};
23261 Roo.htmleditor.TidyEntities.init();
23263 * @class Roo.htmleditor.KeyEnter
23264 * Handle Enter press..
23265 * @cfg {Roo.HtmlEditorCore} core the editor.
23267 * Create a new Filter.
23268 * @param {Object} config Configuration options
23275 Roo.htmleditor.KeyEnter = function(cfg) {
23276 Roo.apply(this, cfg);
23277 // this does not actually call walk as it's really just a abstract class
23279 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
23282 //Roo.htmleditor.KeyEnter.i = 0;
23285 Roo.htmleditor.KeyEnter.prototype = {
23289 keypress : function(e)
23291 if (e.charCode != 13 && e.charCode != 10) {
23292 Roo.log([e.charCode,e]);
23295 e.preventDefault();
23296 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
23297 var doc = this.core.doc;
23301 var sel = this.core.getSelection();
23302 var range = sel.getRangeAt(0);
23303 var n = range.commonAncestorContainer;
23304 var pc = range.closest([ 'ol', 'ul']);
23305 var pli = range.closest('li');
23306 if (!pc || e.ctrlKey) {
23307 sel.insertNode('br', 'after');
23309 this.core.undoManager.addEvent();
23310 this.core.fireEditorEvent(e);
23314 // deal with <li> insetion
23315 if (pli.innerText.trim() == '' &&
23316 pli.previousSibling &&
23317 pli.previousSibling.nodeName == 'LI' &&
23318 pli.previousSibling.innerText.trim() == '') {
23319 pli.parentNode.removeChild(pli.previousSibling);
23320 sel.cursorAfter(pc);
23321 this.core.undoManager.addEvent();
23322 this.core.fireEditorEvent(e);
23326 var li = doc.createElement('LI');
23327 li.innerHTML = ' ';
23328 if (!pli || !pli.firstSibling) {
23329 pc.appendChild(li);
23331 pli.parentNode.insertBefore(li, pli.firstSibling);
23333 sel.cursorText (li.firstChild);
23335 this.core.undoManager.addEvent();
23336 this.core.fireEditorEvent(e);
23348 * @class Roo.htmleditor.Block
23349 * Base class for html editor blocks - do not use it directly .. extend it..
23350 * @cfg {DomElement} node The node to apply stuff to.
23351 * @cfg {String} friendly_name the name that appears in the context bar about this block
23352 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
23355 * Create a new Filter.
23356 * @param {Object} config Configuration options
23359 Roo.htmleditor.Block = function(cfg)
23361 // do nothing .. should not be called really.
23364 * factory method to get the block from an element (using cache if necessary)
23366 * @param {HtmlElement} the dom element
23368 Roo.htmleditor.Block.factory = function(node)
23370 var cc = Roo.htmleditor.Block.cache;
23371 var id = Roo.get(node).id;
23372 if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
23373 Roo.htmleditor.Block.cache[id].readElement(node);
23374 return Roo.htmleditor.Block.cache[id];
23376 var db = node.getAttribute('data-block');
23378 db = node.nodeName.toLowerCase().toUpperCaseFirst();
23380 var cls = Roo.htmleditor['Block' + db];
23381 if (typeof(cls) == 'undefined') {
23382 //Roo.log(node.getAttribute('data-block'));
23383 Roo.log("OOps missing block : " + 'Block' + db);
23386 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
23387 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
23391 * initalize all Elements from content that are 'blockable'
23393 * @param the body element
23395 Roo.htmleditor.Block.initAll = function(body, type)
23397 if (typeof(type) == 'undefined') {
23398 var ia = Roo.htmleditor.Block.initAll;
23404 Roo.each(Roo.get(body).query(type), function(e) {
23405 Roo.htmleditor.Block.factory(e);
23408 // question goes here... do we need to clear out this cache sometimes?
23409 // or show we make it relivant to the htmleditor.
23410 Roo.htmleditor.Block.cache = {};
23412 Roo.htmleditor.Block.prototype = {
23416 // used by context menu
23417 friendly_name : 'Based Block',
23419 // text for button to delete this element
23420 deleteTitle : false,
23424 * Update a node with values from this object
23425 * @param {DomElement} node
23427 updateElement : function(node)
23429 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
23432 * convert to plain HTML for calling insertAtCursor..
23434 toHTML : function()
23436 return Roo.DomHelper.markup(this.toObject());
23439 * used by readEleemnt to extract data from a node
23440 * may need improving as it's pretty basic
23442 * @param {DomElement} node
23443 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
23444 * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
23445 * @param {String} style the style property - eg. text-align
23447 getVal : function(node, tag, attr, style)
23450 if (tag !== true && n.tagName != tag.toUpperCase()) {
23451 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
23452 // but kiss for now.
23453 n = node.getElementsByTagName(tag).item(0);
23458 if (attr === false) {
23461 if (attr == 'html') {
23462 return n.innerHTML;
23464 if (attr == 'style') {
23465 return n.style[style];
23468 return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
23472 * create a DomHelper friendly object - for use with
23473 * Roo.DomHelper.markup / overwrite / etc..
23476 toObject : function()
23481 * Read a node that has a 'data-block' property - and extract the values from it.
23482 * @param {DomElement} node - the node
23484 readElement : function(node)
23495 * @class Roo.htmleditor.BlockFigure
23496 * Block that has an image and a figcaption
23497 * @cfg {String} image_src the url for the image
23498 * @cfg {String} align (left|right) alignment for the block default left
23499 * @cfg {String} caption the text to appear below (and in the alt tag)
23500 * @cfg {String} caption_display (block|none) display or not the caption
23501 * @cfg {String|number} image_width the width of the image number or %?
23502 * @cfg {String|number} image_height the height of the image number or %?
23505 * Create a new Filter.
23506 * @param {Object} config Configuration options
23509 Roo.htmleditor.BlockFigure = function(cfg)
23512 this.readElement(cfg.node);
23513 this.updateElement(cfg.node);
23515 Roo.apply(this, cfg);
23517 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
23524 caption_display : 'block',
23530 // margin: '2%', not used
23532 text_align: 'left', // (left|right) alignment for the text caption default left. - not used at present
23535 // used by context menu
23536 friendly_name : 'Image with caption',
23537 deleteTitle : "Delete Image and Caption",
23539 contextMenu : function(toolbar)
23542 var block = function() {
23543 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
23547 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
23549 var syncValue = toolbar.editorcore.syncValue;
23555 xtype : 'TextItem',
23557 xns : rooui.Toolbar //Boostrap?
23561 text: 'Change Image URL',
23564 click: function (btn, state)
23568 Roo.MessageBox.show({
23569 title : "Image Source URL",
23570 msg : "Enter the url for the image",
23571 buttons: Roo.MessageBox.OKCANCEL,
23572 fn: function(btn, val){
23579 toolbar.editorcore.onEditorEvent();
23583 //multiline: multiline,
23585 value : b.image_src
23589 xns : rooui.Toolbar
23594 text: 'Change Link URL',
23597 click: function (btn, state)
23601 Roo.MessageBox.show({
23602 title : "Link URL",
23603 msg : "Enter the url for the link - leave blank to have no link",
23604 buttons: Roo.MessageBox.OKCANCEL,
23605 fn: function(btn, val){
23612 toolbar.editorcore.onEditorEvent();
23616 //multiline: multiline,
23622 xns : rooui.Toolbar
23626 text: 'Show Video URL',
23629 click: function (btn, state)
23631 Roo.MessageBox.alert("Video URL",
23632 block().video_url == '' ? 'This image is not linked ot a video' :
23633 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
23636 xns : rooui.Toolbar
23641 xtype : 'TextItem',
23643 xns : rooui.Toolbar //Boostrap?
23646 xtype : 'ComboBox',
23647 allowBlank : false,
23648 displayField : 'val',
23651 triggerAction : 'all',
23653 valueField : 'val',
23657 select : function (combo, r, index)
23659 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23661 b.width = r.get('val');
23664 toolbar.editorcore.onEditorEvent();
23669 xtype : 'SimpleStore',
23680 xtype : 'TextItem',
23682 xns : rooui.Toolbar //Boostrap?
23685 xtype : 'ComboBox',
23686 allowBlank : false,
23687 displayField : 'val',
23690 triggerAction : 'all',
23692 valueField : 'val',
23696 select : function (combo, r, index)
23698 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23700 b.align = r.get('val');
23703 toolbar.editorcore.onEditorEvent();
23708 xtype : 'SimpleStore',
23722 text: 'Hide Caption',
23723 name : 'caption_display',
23725 enableToggle : true,
23726 setValue : function(v) {
23727 // this trigger toggle.
23729 this.setText(v ? "Hide Caption" : "Show Caption");
23730 this.setPressed(v != 'block');
23733 toggle: function (btn, state)
23736 b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
23737 this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
23740 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23741 toolbar.editorcore.onEditorEvent();
23744 xns : rooui.Toolbar
23750 * create a DomHelper friendly object - for use with
23751 * Roo.DomHelper.markup / overwrite / etc..
23753 toObject : function()
23755 var d = document.createElement('div');
23756 d.innerHTML = this.caption;
23758 var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0;
23760 var iw = this.align == 'center' ? this.width : '100%';
23763 contenteditable : 'false',
23764 src : this.image_src,
23765 alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
23768 maxWidth : iw + ' !important', // this is not getting rendered?
23774 '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
23776 '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' +
23781 if (this.href.length > 0) {
23785 contenteditable : 'true',
23793 if (this.video_url.length > 0) {
23798 allowfullscreen : true,
23799 width : 420, // these are for video tricks - that we replace the outer
23801 src : this.video_url,
23807 // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
23808 var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
23813 'data-block' : 'Figure',
23814 'data-width' : this.width,
23815 contenteditable : 'false',
23819 float : this.align ,
23820 maxWidth : this.align == 'center' ? '100% !important' : (this.width + ' !important'),
23821 width : this.align == 'center' ? '100%' : this.width,
23823 padding: this.align == 'center' ? '0' : '0 10px' ,
23824 textAlign : this.align // seems to work for email..
23829 align : this.align,
23835 'data-display' : this.caption_display,
23837 textAlign : 'left',
23839 lineHeight : '24px',
23840 display : this.caption_display,
23841 maxWidth : (this.align == 'center' ? this.width : '100%' ) + ' !important',
23843 width: this.align == 'center' ? this.width : '100%'
23847 cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '',
23852 marginTop : '16px',
23858 // we can not rely on yahoo syndication to use CSS elements - so have to use '<i>' to encase stuff.
23860 contenteditable : true,
23876 readElement : function(node)
23878 // this should not really come from the link...
23879 this.video_url = this.getVal(node, 'div', 'src');
23880 this.cls = this.getVal(node, 'div', 'class');
23881 this.href = this.getVal(node, 'a', 'href');
23884 this.image_src = this.getVal(node, 'img', 'src');
23886 this.align = this.getVal(node, 'figure', 'align');
23887 var figcaption = this.getVal(node, 'figcaption', false);
23888 if (figcaption !== '') {
23889 this.caption = this.getVal(figcaption, 'i', 'html');
23893 this.caption_display = this.getVal(node, 'figcaption', 'data-display');
23894 //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
23895 this.width = this.getVal(node, true, 'data-width');
23896 //this.margin = this.getVal(node, 'figure', 'style', 'margin');
23899 removeNode : function()
23916 * @class Roo.htmleditor.BlockTable
23917 * Block that manages a table
23920 * Create a new Filter.
23921 * @param {Object} config Configuration options
23924 Roo.htmleditor.BlockTable = function(cfg)
23927 this.readElement(cfg.node);
23928 this.updateElement(cfg.node);
23930 Roo.apply(this, cfg);
23933 for(var r = 0; r < this.no_row; r++) {
23935 for(var c = 0; c < this.no_col; c++) {
23936 this.rows[r][c] = this.emptyCell();
23943 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
23952 // used by context menu
23953 friendly_name : 'Table',
23954 deleteTitle : 'Delete Table',
23955 // context menu is drawn once..
23957 contextMenu : function(toolbar)
23960 var block = function() {
23961 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
23965 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
23967 var syncValue = toolbar.editorcore.syncValue;
23973 xtype : 'TextItem',
23975 xns : rooui.Toolbar //Boostrap?
23978 xtype : 'ComboBox',
23979 allowBlank : false,
23980 displayField : 'val',
23983 triggerAction : 'all',
23985 valueField : 'val',
23989 select : function (combo, r, index)
23991 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
23993 b.width = r.get('val');
23996 toolbar.editorcore.onEditorEvent();
24001 xtype : 'SimpleStore',
24013 xtype : 'TextItem',
24014 text : "Columns: ",
24015 xns : rooui.Toolbar //Boostrap?
24022 click : function (_self, e)
24024 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24025 block().removeColumn();
24027 toolbar.editorcore.onEditorEvent();
24030 xns : rooui.Toolbar
24036 click : function (_self, e)
24038 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24039 block().addColumn();
24041 toolbar.editorcore.onEditorEvent();
24044 xns : rooui.Toolbar
24048 xtype : 'TextItem',
24050 xns : rooui.Toolbar //Boostrap?
24057 click : function (_self, e)
24059 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24060 block().removeRow();
24062 toolbar.editorcore.onEditorEvent();
24065 xns : rooui.Toolbar
24071 click : function (_self, e)
24075 toolbar.editorcore.onEditorEvent();
24078 xns : rooui.Toolbar
24083 text: 'Reset Column Widths',
24086 click : function (_self, e)
24088 block().resetWidths();
24090 toolbar.editorcore.onEditorEvent();
24093 xns : rooui.Toolbar
24104 * create a DomHelper friendly object - for use with
24105 * Roo.DomHelper.markup / overwrite / etc..
24106 * ?? should it be called with option to hide all editing features?
24108 toObject : function()
24113 contenteditable : 'false', // this stops cell selection from picking the table.
24114 'data-block' : 'Table',
24117 border : 'solid 1px #000', // ??? hard coded?
24118 'border-collapse' : 'collapse'
24121 { tag : 'tbody' , cn : [] }
24125 // do we have a head = not really
24127 Roo.each(this.rows, function( row ) {
24132 border : 'solid 1px #000',
24138 ret.cn[0].cn.push(tr);
24139 // does the row have any properties? ?? height?
24141 Roo.each(row, function( cell ) {
24145 contenteditable : 'true',
24146 'data-block' : 'Td',
24150 if (cell.colspan > 1) {
24151 td.colspan = cell.colspan ;
24152 nc += cell.colspan;
24156 if (cell.rowspan > 1) {
24157 td.rowspan = cell.rowspan ;
24166 ncols = Math.max(nc, ncols);
24170 // add the header row..
24179 readElement : function(node)
24181 node = node ? node : this.node ;
24182 this.width = this.getVal(node, true, 'style', 'width') || '100%';
24186 var trs = Array.from(node.rows);
24187 trs.forEach(function(tr) {
24189 this.rows.push(row);
24193 Array.from(tr.cells).forEach(function(td) {
24196 colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
24197 rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
24198 style : td.hasAttribute('style') ? td.getAttribute('style') : '',
24199 html : td.innerHTML
24201 no_column += add.colspan;
24208 this.no_col = Math.max(this.no_col, no_column);
24215 normalizeRows: function()
24219 this.rows.forEach(function(row) {
24222 row = this.normalizeRow(row);
24224 row.forEach(function(c) {
24225 while (typeof(ret[rid][cid]) != 'undefined') {
24228 if (typeof(ret[rid]) == 'undefined') {
24234 if (c.rowspan < 2) {
24238 for(var i = 1 ;i < c.rowspan; i++) {
24239 if (typeof(ret[rid+i]) == 'undefined') {
24242 ret[rid+i][cid] = c;
24250 normalizeRow: function(row)
24253 row.forEach(function(c) {
24254 if (c.colspan < 2) {
24258 for(var i =0 ;i < c.colspan; i++) {
24266 deleteColumn : function(sel)
24268 if (!sel || sel.type != 'col') {
24271 if (this.no_col < 2) {
24275 this.rows.forEach(function(row) {
24276 var cols = this.normalizeRow(row);
24277 var col = cols[sel.col];
24278 if (col.colspan > 1) {
24288 removeColumn : function()
24290 this.deleteColumn({
24292 col : this.no_col-1
24294 this.updateElement();
24298 addColumn : function()
24301 this.rows.forEach(function(row) {
24302 row.push(this.emptyCell());
24305 this.updateElement();
24308 deleteRow : function(sel)
24310 if (!sel || sel.type != 'row') {
24314 if (this.no_row < 2) {
24318 var rows = this.normalizeRows();
24321 rows[sel.row].forEach(function(col) {
24322 if (col.rowspan > 1) {
24325 col.remove = 1; // flage it as removed.
24330 this.rows.forEach(function(row) {
24332 row.forEach(function(c) {
24333 if (typeof(c.remove) == 'undefined') {
24338 if (newrow.length > 0) {
24342 this.rows = newrows;
24347 this.updateElement();
24350 removeRow : function()
24354 row : this.no_row-1
24360 addRow : function()
24364 for (var i = 0; i < this.no_col; i++ ) {
24366 row.push(this.emptyCell());
24369 this.rows.push(row);
24370 this.updateElement();
24374 // the default cell object... at present...
24375 emptyCell : function() {
24376 return (new Roo.htmleditor.BlockTd({})).toObject();
24381 removeNode : function()
24388 resetWidths : function()
24390 Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
24391 var nn = Roo.htmleditor.Block.factory(n);
24393 nn.updateElement(n);
24406 * since selections really work on the table cell, then editing really should work from there
24408 * The original plan was to support merging etc... - but that may not be needed yet..
24410 * So this simple version will support:
24412 * adjust the width +/-
24413 * reset the width...
24422 * @class Roo.htmleditor.BlockTable
24423 * Block that manages a table
24426 * Create a new Filter.
24427 * @param {Object} config Configuration options
24430 Roo.htmleditor.BlockTd = function(cfg)
24433 this.readElement(cfg.node);
24434 this.updateElement(cfg.node);
24436 Roo.apply(this, cfg);
24441 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
24446 textAlign : 'left',
24453 // used by context menu
24454 friendly_name : 'Table Cell',
24455 deleteTitle : false, // use our customer delete
24457 // context menu is drawn once..
24459 contextMenu : function(toolbar)
24462 var cell = function() {
24463 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
24466 var table = function() {
24467 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
24471 var saveSel = function()
24473 lr = toolbar.editorcore.getSelection().getRangeAt(0);
24475 var restoreSel = function()
24479 toolbar.editorcore.focus();
24480 var cr = toolbar.editorcore.getSelection();
24481 cr.removeAllRanges();
24483 toolbar.editorcore.onEditorEvent();
24484 }).defer(10, this);
24490 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
24492 var syncValue = toolbar.editorcore.syncValue;
24499 text : 'Edit Table',
24501 click : function() {
24502 var t = toolbar.tb.selectedNode.closest('table');
24503 toolbar.editorcore.selectNode(t);
24504 toolbar.editorcore.onEditorEvent();
24513 xtype : 'TextItem',
24514 text : "Column Width: ",
24515 xns : rooui.Toolbar
24522 click : function (_self, e)
24524 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24525 cell().shrinkColumn();
24527 toolbar.editorcore.onEditorEvent();
24530 xns : rooui.Toolbar
24536 click : function (_self, e)
24538 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24539 cell().growColumn();
24541 toolbar.editorcore.onEditorEvent();
24544 xns : rooui.Toolbar
24548 xtype : 'TextItem',
24549 text : "Vertical Align: ",
24550 xns : rooui.Toolbar //Boostrap?
24553 xtype : 'ComboBox',
24554 allowBlank : false,
24555 displayField : 'val',
24558 triggerAction : 'all',
24560 valueField : 'val',
24564 select : function (combo, r, index)
24566 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24568 b.valign = r.get('val');
24571 toolbar.editorcore.onEditorEvent();
24576 xtype : 'SimpleStore',
24580 ['bottom'] // there are afew more...
24588 xtype : 'TextItem',
24589 text : "Merge Cells: ",
24590 xns : rooui.Toolbar
24599 click : function (_self, e)
24601 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24602 cell().mergeRight();
24603 //block().growColumn();
24605 toolbar.editorcore.onEditorEvent();
24608 xns : rooui.Toolbar
24615 click : function (_self, e)
24617 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24618 cell().mergeBelow();
24619 //block().growColumn();
24621 toolbar.editorcore.onEditorEvent();
24624 xns : rooui.Toolbar
24627 xtype : 'TextItem',
24629 xns : rooui.Toolbar
24637 click : function (_self, e)
24639 //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24642 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
24643 toolbar.editorcore.onEditorEvent();
24647 xns : rooui.Toolbar
24651 xns : rooui.Toolbar
24660 xns : rooui.Toolbar,
24669 click : function (_self, e)
24673 cell().deleteColumn();
24675 toolbar.editorcore.selectNode(t.node);
24676 toolbar.editorcore.onEditorEvent();
24685 click : function (_self, e)
24688 cell().deleteRow();
24691 toolbar.editorcore.selectNode(t.node);
24692 toolbar.editorcore.onEditorEvent();
24699 xtype : 'Separator',
24706 click : function (_self, e)
24709 var nn = t.node.nextSibling || t.node.previousSibling;
24710 t.node.parentNode.removeChild(t.node);
24712 toolbar.editorcore.selectNode(nn, true);
24714 toolbar.editorcore.onEditorEvent();
24724 // align... << fixme
24732 * create a DomHelper friendly object - for use with
24733 * Roo.DomHelper.markup / overwrite / etc..
24734 * ?? should it be called with option to hide all editing features?
24737 * create a DomHelper friendly object - for use with
24738 * Roo.DomHelper.markup / overwrite / etc..
24739 * ?? should it be called with option to hide all editing features?
24741 toObject : function()
24746 contenteditable : 'true', // this stops cell selection from picking the table.
24747 'data-block' : 'Td',
24748 valign : this.valign,
24750 'text-align' : this.textAlign,
24751 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
24752 'border-collapse' : 'collapse',
24753 padding : '6px', // 8 for desktop / 4 for mobile
24754 'vertical-align': this.valign
24758 if (this.width != '') {
24759 ret.width = this.width;
24760 ret.style.width = this.width;
24764 if (this.colspan > 1) {
24765 ret.colspan = this.colspan ;
24767 if (this.rowspan > 1) {
24768 ret.rowspan = this.rowspan ;
24777 readElement : function(node)
24779 node = node ? node : this.node ;
24780 this.width = node.style.width;
24781 this.colspan = Math.max(1,1*node.getAttribute('colspan'));
24782 this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
24783 this.html = node.innerHTML;
24788 // the default cell object... at present...
24789 emptyCell : function() {
24793 textAlign : 'left',
24794 html : " " // is this going to be editable now?
24799 removeNode : function()
24801 return this.node.closest('table');
24809 toTableArray : function()
24812 var tab = this.node.closest('tr').closest('table');
24813 Array.from(tab.rows).forEach(function(r, ri){
24817 this.colWidths = [];
24818 var all_auto = true;
24819 Array.from(tab.rows).forEach(function(r, ri){
24822 Array.from(r.cells).forEach(function(ce, ci){
24827 colspan : ce.colSpan,
24828 rowspan : ce.rowSpan
24830 if (ce.isEqualNode(this.node)) {
24833 // if we have been filled up by a row?
24834 if (typeof(ret[rn][cn]) != 'undefined') {
24835 while(typeof(ret[rn][cn]) != 'undefined') {
24841 if (typeof(this.colWidths[cn]) == 'undefined') {
24842 this.colWidths[cn] = ce.style.width;
24843 if (this.colWidths[cn] != '') {
24849 if (c.colspan < 2 && c.rowspan < 2 ) {
24854 for(var j = 0; j < c.rowspan; j++) {
24855 if (typeof(ret[rn+j]) == 'undefined') {
24856 continue; // we have a problem..
24859 for(var i = 0; i < c.colspan; i++) {
24860 ret[rn+j][cn+i] = c;
24869 // initalize widths.?
24870 // either all widths or no widths..
24872 this.colWidths[0] = false; // no widths flag.
24883 mergeRight: function()
24886 // get the contents of the next cell along..
24887 var tr = this.node.closest('tr');
24888 var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
24889 if (i >= tr.childNodes.length - 1) {
24890 return; // no cells on right to merge with.
24892 var table = this.toTableArray();
24894 if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
24895 return; // nothing right?
24897 var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
24898 // right cell - must be same rowspan and on the same row.
24899 if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
24900 return; // right hand side is not same rowspan.
24905 this.node.innerHTML += ' ' + rc.cell.innerHTML;
24906 tr.removeChild(rc.cell);
24907 this.colspan += rc.colspan;
24908 this.node.setAttribute('colspan', this.colspan);
24913 mergeBelow : function()
24915 var table = this.toTableArray();
24916 if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
24917 return; // no row below
24919 if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
24920 return; // nothing right?
24922 var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
24924 if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
24925 return; // right hand side is not same rowspan.
24927 this.node.innerHTML = this.node.innerHTML + rc.cell.innerHTML ;
24928 rc.cell.parentNode.removeChild(rc.cell);
24929 this.rowspan += rc.rowspan;
24930 this.node.setAttribute('rowspan', this.rowspan);
24935 if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
24938 var table = this.toTableArray();
24939 var cd = this.cellData;
24943 for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
24947 for(var c = cd.col; c < cd.col + cd.colspan; c++) {
24948 if (r == cd.row && c == cd.col) {
24949 this.node.removeAttribute('rowspan');
24950 this.node.removeAttribute('colspan');
24954 var ntd = this.node.cloneNode(); // which col/row should be 0..
24955 ntd.removeAttribute('id'); //
24956 //ntd.style.width = '';
24957 ntd.innerHTML = '';
24958 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1 };
24962 this.redrawAllCells(table);
24970 redrawAllCells: function(table)
24974 var tab = this.node.closest('tr').closest('table');
24975 var ctr = tab.rows[0].parentNode;
24976 Array.from(tab.rows).forEach(function(r, ri){
24978 Array.from(r.cells).forEach(function(ce, ci){
24979 ce.parentNode.removeChild(ce);
24981 r.parentNode.removeChild(r);
24983 for(var r = 0 ; r < table.length; r++) {
24984 var re = tab.rows[r];
24986 var re = tab.ownerDocument.createElement('tr');
24987 ctr.appendChild(re);
24988 for(var c = 0 ; c < table[r].length; c++) {
24989 if (table[r][c].cell === false) {
24993 re.appendChild(table[r][c].cell);
24995 table[r][c].cell = false;
25000 updateWidths : function(table)
25002 for(var r = 0 ; r < table.length; r++) {
25004 for(var c = 0 ; c < table[r].length; c++) {
25005 if (table[r][c].cell === false) {
25009 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
25010 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
25011 el.width = Math.floor(this.colWidths[c]) +'%';
25012 el.updateElement(el.node);
25014 table[r][c].cell = false; // done
25018 normalizeWidths : function(table)
25021 if (this.colWidths[0] === false) {
25022 var nw = 100.0 / this.colWidths.length;
25023 this.colWidths.forEach(function(w,i) {
25024 this.colWidths[i] = nw;
25029 var t = 0, missing = [];
25031 this.colWidths.forEach(function(w,i) {
25033 this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
25034 var add = this.colWidths[i];
25043 var nc = this.colWidths.length;
25044 if (missing.length) {
25045 var mult = (nc - missing.length) / (1.0 * nc);
25047 var ew = (100 -t) / (1.0 * missing.length);
25048 this.colWidths.forEach(function(w,i) {
25050 this.colWidths[i] = w * mult;
25054 this.colWidths[i] = ew;
25056 // have to make up numbers..
25059 // now we should have all the widths..
25064 shrinkColumn : function()
25066 var table = this.toTableArray();
25067 this.normalizeWidths(table);
25068 var col = this.cellData.col;
25069 var nw = this.colWidths[col] * 0.8;
25073 var otherAdd = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
25074 this.colWidths.forEach(function(w,i) {
25076 this.colWidths[i] = nw;
25079 this.colWidths[i] += otherAdd
25081 this.updateWidths(table);
25084 growColumn : function()
25086 var table = this.toTableArray();
25087 this.normalizeWidths(table);
25088 var col = this.cellData.col;
25089 var nw = this.colWidths[col] * 1.2;
25093 var otherSub = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
25094 this.colWidths.forEach(function(w,i) {
25096 this.colWidths[i] = nw;
25099 this.colWidths[i] -= otherSub
25101 this.updateWidths(table);
25104 deleteRow : function()
25106 // delete this rows 'tr'
25107 // if any of the cells in this row have a rowspan > 1 && row!= this row..
25108 // then reduce the rowspan.
25109 var table = this.toTableArray();
25110 // this.cellData.row;
25111 for (var i =0;i< table[this.cellData.row].length ; i++) {
25112 var c = table[this.cellData.row][i];
25113 if (c.row != this.cellData.row) {
25116 c.cell.setAttribute('rowspan', c.rowspan);
25119 if (c.rowspan > 1) {
25121 c.cell.setAttribute('rowspan', c.rowspan);
25124 table.splice(this.cellData.row,1);
25125 this.redrawAllCells(table);
25128 deleteColumn : function()
25130 var table = this.toTableArray();
25132 for (var i =0;i< table.length ; i++) {
25133 var c = table[i][this.cellData.col];
25134 if (c.col != this.cellData.col) {
25135 table[i][this.cellData.col].colspan--;
25136 } else if (c.colspan > 1) {
25138 c.cell.setAttribute('colspan', c.colspan);
25140 table[i].splice(this.cellData.col,1);
25143 this.redrawAllCells(table);
25151 //<script type="text/javascript">
25154 * Based Ext JS Library 1.1.1
25155 * Copyright(c) 2006-2007, Ext JS, LLC.
25161 * @class Roo.HtmlEditorCore
25162 * @extends Roo.Component
25163 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25165 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25168 Roo.HtmlEditorCore = function(config){
25171 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25176 * @event initialize
25177 * Fires when the editor is fully initialized (including the iframe)
25178 * @param {Roo.HtmlEditorCore} this
25183 * Fires when the editor is first receives the focus. Any insertion must wait
25184 * until after this event.
25185 * @param {Roo.HtmlEditorCore} this
25189 * @event beforesync
25190 * Fires before the textarea is updated with content from the editor iframe. Return false
25191 * to cancel the sync.
25192 * @param {Roo.HtmlEditorCore} this
25193 * @param {String} html
25197 * @event beforepush
25198 * Fires before the iframe editor is updated with content from the textarea. Return false
25199 * to cancel the push.
25200 * @param {Roo.HtmlEditorCore} this
25201 * @param {String} html
25206 * Fires when the textarea is updated with content from the editor iframe.
25207 * @param {Roo.HtmlEditorCore} this
25208 * @param {String} html
25213 * Fires when the iframe editor is updated with content from the textarea.
25214 * @param {Roo.HtmlEditorCore} this
25215 * @param {String} html
25220 * @event editorevent
25221 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25222 * @param {Roo.HtmlEditorCore} this
25229 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25231 // defaults : white / black...
25232 this.applyBlacklists();
25239 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
25243 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
25249 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
25254 * @cfg {Number} height (in pixels)
25258 * @cfg {Number} width (in pixels)
25262 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
25263 * if you are doing an email editor, this probably needs disabling, it's designed
25268 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
25270 enableBlocks : true,
25272 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25275 stylesheets: false,
25277 * @cfg {String} language default en - language of text (usefull for rtl languages)
25283 * @cfg {boolean} allowComments - default false - allow comments in HTML source
25284 * - by default they are stripped - if you are editing email you may need this.
25286 allowComments: false,
25290 // private properties
25291 validationEvent : false,
25293 initialized : false,
25295 sourceEditMode : false,
25296 onFocus : Roo.emptyFn,
25298 hideMode:'offsets',
25302 // blacklist + whitelisted elements..
25309 undoManager : false,
25311 * Protected method that will not generally be called directly. It
25312 * is called when the editor initializes the iframe with HTML contents. Override this method if you
25313 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25315 getDocMarkup : function(){
25319 // inherit styels from page...??
25320 if (this.stylesheets === false) {
25322 Roo.get(document.head).select('style').each(function(node) {
25323 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25326 Roo.get(document.head).select('link').each(function(node) {
25327 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25330 } else if (!this.stylesheets.length) {
25332 st = '<style type="text/css">' +
25333 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25336 for (var i in this.stylesheets) {
25337 if (typeof(this.stylesheets[i]) != 'string') {
25340 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25345 st += '<style type="text/css">' +
25346 'IMG { cursor: pointer } ' +
25349 st += '<meta name="google" content="notranslate">';
25351 var cls = 'notranslate roo-htmleditor-body';
25353 if(this.bodyCls.length){
25354 cls += ' ' + this.bodyCls;
25357 return '<html class="notranslate" translate="no"><head>' + st +
25358 //<style type="text/css">' +
25359 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25361 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
25365 onRender : function(ct, position)
25368 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25369 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25372 this.el.dom.style.border = '0 none';
25373 this.el.dom.setAttribute('tabIndex', -1);
25374 this.el.addClass('x-hidden hide');
25378 if(Roo.isIE){ // fix IE 1px bogus margin
25379 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25383 this.frameId = Roo.id();
25387 var iframe = this.owner.wrap.createChild({
25389 cls: 'form-control', // bootstrap..
25391 name: this.frameId,
25392 frameBorder : 'no',
25393 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
25398 this.iframe = iframe.dom;
25400 this.assignDocWin();
25402 this.doc.designMode = 'on';
25405 this.doc.write(this.getDocMarkup());
25409 var task = { // must defer to wait for browser to be ready
25411 //console.log("run task?" + this.doc.readyState);
25412 this.assignDocWin();
25413 if(this.doc.body || this.doc.readyState == 'complete'){
25415 this.doc.designMode="on";
25420 Roo.TaskMgr.stop(task);
25421 this.initEditor.defer(10, this);
25428 Roo.TaskMgr.start(task);
25433 onResize : function(w, h)
25435 Roo.log('resize: ' +w + ',' + h );
25436 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25440 if(typeof w == 'number'){
25442 this.iframe.style.width = w + 'px';
25444 if(typeof h == 'number'){
25446 this.iframe.style.height = h + 'px';
25448 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25455 * Toggles the editor between standard and source edit mode.
25456 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25458 toggleSourceEdit : function(sourceEditMode){
25460 this.sourceEditMode = sourceEditMode === true;
25462 if(this.sourceEditMode){
25464 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
25467 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
25468 //this.iframe.className = '';
25471 //this.setSize(this.owner.wrap.getSize());
25472 //this.fireEvent('editmodechange', this, this.sourceEditMode);
25479 * Protected method that will not generally be called directly. If you need/want
25480 * custom HTML cleanup, this is the method you should override.
25481 * @param {String} html The HTML to be cleaned
25482 * return {String} The cleaned HTML
25484 cleanHtml : function(html)
25486 html = String(html);
25487 if(html.length > 5){
25488 if(Roo.isSafari){ // strip safari nonsense
25489 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25492 if(html == ' '){
25499 * HTML Editor -> Textarea
25500 * Protected method that will not generally be called directly. Syncs the contents
25501 * of the editor iframe with the textarea.
25503 syncValue : function()
25505 //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
25506 if(this.initialized){
25508 if (this.undoManager) {
25509 this.undoManager.addEvent();
25513 var bd = (this.doc.body || this.doc.documentElement);
25516 var sel = this.win.getSelection();
25518 var div = document.createElement('div');
25519 div.innerHTML = bd.innerHTML;
25520 var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
25521 if (gtx.length > 0) {
25522 var rm = gtx.item(0).parentNode;
25523 rm.parentNode.removeChild(rm);
25527 if (this.enableBlocks) {
25528 new Roo.htmleditor.FilterBlock({ node : div });
25531 var tidy = new Roo.htmleditor.TidySerializer({
25534 var html = tidy.serialize(div);
25538 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25539 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25541 html = '<div style="'+m[0]+'">' + html + '</div>';
25544 html = this.cleanHtml(html);
25545 // fix up the special chars.. normaly like back quotes in word...
25546 // however we do not want to do this with chinese..
25547 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25549 var cc = match.charCodeAt();
25551 // Get the character value, handling surrogate pairs
25552 if (match.length == 2) {
25553 // It's a surrogate pair, calculate the Unicode code point
25554 var high = match.charCodeAt(0) - 0xD800;
25555 var low = match.charCodeAt(1) - 0xDC00;
25556 cc = (high * 0x400) + low + 0x10000;
25558 (cc >= 0x4E00 && cc < 0xA000 ) ||
25559 (cc >= 0x3400 && cc < 0x4E00 ) ||
25560 (cc >= 0xf900 && cc < 0xfb00 )
25565 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25566 return "&#" + cc + ";";
25573 if(this.owner.fireEvent('beforesync', this, html) !== false){
25574 this.el.dom.value = html;
25575 this.owner.fireEvent('sync', this, html);
25581 * TEXTAREA -> EDITABLE
25582 * Protected method that will not generally be called directly. Pushes the value of the textarea
25583 * into the iframe editor.
25585 pushValue : function()
25587 //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
25588 if(this.initialized){
25589 var v = this.el.dom.value.trim();
25592 if(this.owner.fireEvent('beforepush', this, v) !== false){
25593 var d = (this.doc.body || this.doc.documentElement);
25596 this.el.dom.value = d.innerHTML;
25597 this.owner.fireEvent('push', this, v);
25599 if (this.autoClean) {
25600 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
25601 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
25603 if (this.enableBlocks) {
25604 Roo.htmleditor.Block.initAll(this.doc.body);
25607 this.updateLanguage();
25609 var lc = this.doc.body.lastChild;
25610 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
25611 // add an extra line at the end.
25612 this.doc.body.appendChild(this.doc.createElement('br'));
25620 deferFocus : function(){
25621 this.focus.defer(10, this);
25625 focus : function(){
25626 if(this.win && !this.sourceEditMode){
25633 assignDocWin: function()
25635 var iframe = this.iframe;
25638 this.doc = iframe.contentWindow.document;
25639 this.win = iframe.contentWindow;
25641 // if (!Roo.get(this.frameId)) {
25644 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25645 // this.win = Roo.get(this.frameId).dom.contentWindow;
25647 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25651 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25652 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25657 initEditor : function(){
25658 //console.log("INIT EDITOR");
25659 this.assignDocWin();
25663 this.doc.designMode="on";
25665 this.doc.write(this.getDocMarkup());
25668 var dbody = (this.doc.body || this.doc.documentElement);
25669 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25670 // this copies styles from the containing element into thsi one..
25671 // not sure why we need all of this..
25672 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25674 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25675 //ss['background-attachment'] = 'fixed'; // w3c
25676 dbody.bgProperties = 'fixed'; // ie
25677 dbody.setAttribute("translate", "no");
25679 //Roo.DomHelper.applyStyles(dbody, ss);
25680 Roo.EventManager.on(this.doc, {
25682 'mouseup': this.onEditorEvent,
25683 'dblclick': this.onEditorEvent,
25684 'click': this.onEditorEvent,
25685 'keyup': this.onEditorEvent,
25690 Roo.EventManager.on(this.doc, {
25691 'paste': this.onPasteEvent,
25695 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25698 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25699 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25701 this.initialized = true;
25704 // initialize special key events - enter
25705 new Roo.htmleditor.KeyEnter({core : this});
25709 this.owner.fireEvent('initialize', this);
25712 // this is to prevent a href clicks resulting in a redirect?
25714 onPasteEvent : function(e,v)
25716 // I think we better assume paste is going to be a dirty load of rubish from word..
25718 // even pasting into a 'email version' of this widget will have to clean up that mess.
25719 var cd = (e.browserEvent.clipboardData || window.clipboardData);
25721 // check what type of paste - if it's an image, then handle it differently.
25722 if (cd.files && cd.files.length > 0) {
25724 var urlAPI = (window.createObjectURL && window) ||
25725 (window.URL && URL.revokeObjectURL && URL) ||
25726 (window.webkitURL && webkitURL);
25728 var url = urlAPI.createObjectURL( cd.files[0]);
25729 this.insertAtCursor('<img src=" + url + ">');
25732 if (cd.types.indexOf('text/html') < 0 ) {
25736 var html = cd.getData('text/html'); // clipboard event
25737 if (cd.types.indexOf('text/rtf') > -1) {
25738 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
25739 images = parser.doc ? parser.doc.getElementsByType('pict') : [];
25744 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
25745 .map(function(g) { return g.toDataURL(); })
25746 .filter(function(g) { return g != 'about:blank'; });
25749 html = this.cleanWordChars(html);
25751 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
25754 var sn = this.getParentElement();
25755 // check if d contains a table, and prevent nesting??
25756 //Roo.log(d.getElementsByTagName('table'));
25758 //Roo.log(sn.closest('table'));
25759 if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
25760 e.preventDefault();
25761 this.insertAtCursor("You can not nest tables");
25762 //Roo.log("prevent?"); // fixme -
25766 if (images.length > 0) {
25767 Roo.each(d.getElementsByTagName('img'), function(img, i) {
25768 img.setAttribute('src', images[i]);
25771 if (this.autoClean) {
25772 new Roo.htmleditor.FilterWord({ node : d });
25774 new Roo.htmleditor.FilterStyleToTag({ node : d });
25775 new Roo.htmleditor.FilterAttributes({
25777 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width'],
25778 attrib_clean : ['href', 'src' ]
25780 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
25781 // should be fonts..
25782 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
25783 new Roo.htmleditor.FilterParagraph({ node : d });
25784 new Roo.htmleditor.FilterSpan({ node : d });
25785 new Roo.htmleditor.FilterLongBr({ node : d });
25786 new Roo.htmleditor.FilterComment({ node : d });
25790 if (this.enableBlocks) {
25792 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
25793 if (img.closest('figure')) { // assume!! that it's aready
25796 var fig = new Roo.htmleditor.BlockFigure({
25797 image_src : img.src
25799 fig.updateElement(img); // replace it..
25805 this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
25806 if (this.enableBlocks) {
25807 Roo.htmleditor.Block.initAll(this.doc.body);
25811 e.preventDefault();
25813 // default behaveiour should be our local cleanup paste? (optional?)
25814 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
25815 //this.owner.fireEvent('paste', e, v);
25818 onDestroy : function(){
25824 //for (var i =0; i < this.toolbars.length;i++) {
25825 // // fixme - ask toolbars for heights?
25826 // this.toolbars[i].onDestroy();
25829 //this.wrap.dom.innerHTML = '';
25830 //this.wrap.remove();
25835 onFirstFocus : function(){
25837 this.assignDocWin();
25838 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
25840 this.activated = true;
25843 if(Roo.isGecko){ // prevent silly gecko errors
25845 var s = this.win.getSelection();
25846 if(!s.focusNode || s.focusNode.nodeType != 3){
25847 var r = s.getRangeAt(0);
25848 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25853 this.execCmd('useCSS', true);
25854 this.execCmd('styleWithCSS', false);
25857 this.owner.fireEvent('activate', this);
25861 adjustFont: function(btn){
25862 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25863 //if(Roo.isSafari){ // safari
25866 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25867 if(Roo.isSafari){ // safari
25868 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25869 v = (v < 10) ? 10 : v;
25870 v = (v > 48) ? 48 : v;
25871 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25876 v = Math.max(1, v+adjust);
25878 this.execCmd('FontSize', v );
25881 onEditorEvent : function(e)
25885 if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
25886 return; // we do not handle this.. (undo manager does..)
25888 // in theory this detects if the last element is not a br, then we try and do that.
25889 // its so clicking in space at bottom triggers adding a br and moving the cursor.
25891 e.target.nodeName == 'BODY' &&
25892 e.type == "mouseup" &&
25893 this.doc.body.lastChild
25895 var lc = this.doc.body.lastChild;
25896 // gtx-trans is google translate plugin adding crap.
25897 while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
25898 lc = lc.previousSibling;
25900 if (lc.nodeType == 1 && lc.nodeName != 'BR') {
25901 // if last element is <BR> - then dont do anything.
25903 var ns = this.doc.createElement('br');
25904 this.doc.body.appendChild(ns);
25905 range = this.doc.createRange();
25906 range.setStartAfter(ns);
25907 range.collapse(true);
25908 var sel = this.win.getSelection();
25909 sel.removeAllRanges();
25910 sel.addRange(range);
25916 this.fireEditorEvent(e);
25917 // this.updateToolbar();
25918 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25921 fireEditorEvent: function(e)
25923 this.owner.fireEvent('editorevent', this, e);
25926 insertTag : function(tg)
25928 // could be a bit smarter... -> wrap the current selected tRoo..
25929 if (tg.toLowerCase() == 'span' ||
25930 tg.toLowerCase() == 'code' ||
25931 tg.toLowerCase() == 'sup' ||
25932 tg.toLowerCase() == 'sub'
25935 range = this.createRange(this.getSelection());
25936 var wrappingNode = this.doc.createElement(tg.toLowerCase());
25937 wrappingNode.appendChild(range.extractContents());
25938 range.insertNode(wrappingNode);
25945 this.execCmd("formatblock", tg);
25946 this.undoManager.addEvent();
25949 insertText : function(txt)
25953 var range = this.createRange();
25954 range.deleteContents();
25955 //alert(Sender.getAttribute('label'));
25957 range.insertNode(this.doc.createTextNode(txt));
25958 this.undoManager.addEvent();
25964 * Executes a Midas editor command on the editor document and performs necessary focus and
25965 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25966 * @param {String} cmd The Midas command
25967 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25969 relayCmd : function(cmd, value)
25973 case 'justifyleft':
25974 case 'justifyright':
25975 case 'justifycenter':
25976 // if we are in a cell, then we will adjust the
25977 var n = this.getParentElement();
25978 var td = n.closest('td');
25980 var bl = Roo.htmleditor.Block.factory(td);
25981 bl.textAlign = cmd.replace('justify','');
25982 bl.updateElement();
25983 this.owner.fireEvent('editorevent', this);
25986 this.execCmd('styleWithCSS', true); //
25990 // if there is no selection, then we insert, and set the curson inside it..
25991 this.execCmd('styleWithCSS', false);
26001 this.execCmd(cmd, value);
26002 this.owner.fireEvent('editorevent', this);
26003 //this.updateToolbar();
26004 this.owner.deferFocus();
26008 * Executes a Midas editor command directly on the editor document.
26009 * For visual commands, you should use {@link #relayCmd} instead.
26010 * <b>This should only be called after the editor is initialized.</b>
26011 * @param {String} cmd The Midas command
26012 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26014 execCmd : function(cmd, value){
26015 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26022 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26024 * @param {String} text | dom node..
26026 insertAtCursor : function(text)
26029 if(!this.activated){
26033 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26037 // from jquery ui (MIT licenced)
26039 var win = this.win;
26041 if (win.getSelection && win.getSelection().getRangeAt) {
26043 // delete the existing?
26045 this.createRange(this.getSelection()).deleteContents();
26046 range = win.getSelection().getRangeAt(0);
26047 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26048 range.insertNode(node);
26049 range = range.cloneRange();
26050 range.collapse(false);
26052 win.getSelection().removeAllRanges();
26053 win.getSelection().addRange(range);
26057 } else if (win.document.selection && win.document.selection.createRange) {
26058 // no firefox support
26059 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26060 win.document.selection.createRange().pasteHTML(txt);
26063 // no firefox support
26064 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26065 this.execCmd('InsertHTML', txt);
26073 mozKeyPress : function(e){
26075 var c = e.getCharCode(), cmd;
26078 c = String.fromCharCode(c).toLowerCase();
26092 // this.cleanUpPaste.defer(100, this);
26098 this.relayCmd(cmd);
26099 //this.win.focus();
26100 //this.execCmd(cmd);
26101 //this.deferFocus();
26102 e.preventDefault();
26110 fixKeys : function(){ // load time branching for fastest keydown performance
26114 return function(e){
26115 var k = e.getKey(), r;
26118 r = this.doc.selection.createRange();
26121 r.pasteHTML('    ');
26126 /// this is handled by Roo.htmleditor.KeyEnter
26129 r = this.doc.selection.createRange();
26131 var target = r.parentElement();
26132 if(!target || target.tagName.toLowerCase() != 'li'){
26134 r.pasteHTML('<br/>');
26141 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26142 // this.cleanUpPaste.defer(100, this);
26148 }else if(Roo.isOpera){
26149 return function(e){
26150 var k = e.getKey();
26154 this.execCmd('InsertHTML','    ');
26158 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26159 // this.cleanUpPaste.defer(100, this);
26164 }else if(Roo.isSafari){
26165 return function(e){
26166 var k = e.getKey();
26170 this.execCmd('InsertText','\t');
26174 this.mozKeyPress(e);
26176 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26177 // this.cleanUpPaste.defer(100, this);
26185 getAllAncestors: function()
26187 var p = this.getSelectedNode();
26190 a.push(p); // push blank onto stack..
26191 p = this.getParentElement();
26195 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26199 a.push(this.doc.body);
26203 lastSelNode : false,
26206 getSelection : function()
26208 this.assignDocWin();
26209 return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
26212 * Select a dom node
26213 * @param {DomElement} node the node to select
26215 selectNode : function(node, collapse)
26217 var nodeRange = node.ownerDocument.createRange();
26219 nodeRange.selectNode(node);
26221 nodeRange.selectNodeContents(node);
26223 if (collapse === true) {
26224 nodeRange.collapse(true);
26227 var s = this.win.getSelection();
26228 s.removeAllRanges();
26229 s.addRange(nodeRange);
26232 getSelectedNode: function()
26234 // this may only work on Gecko!!!
26236 // should we cache this!!!!
26240 var range = this.createRange(this.getSelection()).cloneRange();
26243 var parent = range.parentElement();
26245 var testRange = range.duplicate();
26246 testRange.moveToElementText(parent);
26247 if (testRange.inRange(range)) {
26250 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26253 parent = parent.parentElement;
26258 // is ancestor a text element.
26259 var ac = range.commonAncestorContainer;
26260 if (ac.nodeType == 3) {
26261 ac = ac.parentNode;
26264 var ar = ac.childNodes;
26267 var other_nodes = [];
26268 var has_other_nodes = false;
26269 for (var i=0;i<ar.length;i++) {
26270 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26273 // fullly contained node.
26275 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26280 // probably selected..
26281 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26282 other_nodes.push(ar[i]);
26286 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26291 has_other_nodes = true;
26293 if (!nodes.length && other_nodes.length) {
26294 nodes= other_nodes;
26296 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26304 createRange: function(sel)
26306 // this has strange effects when using with
26307 // top toolbar - not sure if it's a great idea.
26308 //this.editor.contentWindow.focus();
26309 if (typeof sel != "undefined") {
26311 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26313 return this.doc.createRange();
26316 return this.doc.createRange();
26319 getParentElement: function()
26322 this.assignDocWin();
26323 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26325 var range = this.createRange(sel);
26328 var p = range.commonAncestorContainer;
26329 while (p.nodeType == 3) { // text node
26340 * Range intersection.. the hard stuff...
26344 * [ -- selected range --- ]
26348 * if end is before start or hits it. fail.
26349 * if start is after end or hits it fail.
26351 * if either hits (but other is outside. - then it's not
26357 // @see http://www.thismuchiknow.co.uk/?p=64.
26358 rangeIntersectsNode : function(range, node)
26360 var nodeRange = node.ownerDocument.createRange();
26362 nodeRange.selectNode(node);
26364 nodeRange.selectNodeContents(node);
26367 var rangeStartRange = range.cloneRange();
26368 rangeStartRange.collapse(true);
26370 var rangeEndRange = range.cloneRange();
26371 rangeEndRange.collapse(false);
26373 var nodeStartRange = nodeRange.cloneRange();
26374 nodeStartRange.collapse(true);
26376 var nodeEndRange = nodeRange.cloneRange();
26377 nodeEndRange.collapse(false);
26379 return rangeStartRange.compareBoundaryPoints(
26380 Range.START_TO_START, nodeEndRange) == -1 &&
26381 rangeEndRange.compareBoundaryPoints(
26382 Range.START_TO_START, nodeStartRange) == 1;
26386 rangeCompareNode : function(range, node)
26388 var nodeRange = node.ownerDocument.createRange();
26390 nodeRange.selectNode(node);
26392 nodeRange.selectNodeContents(node);
26396 range.collapse(true);
26398 nodeRange.collapse(true);
26400 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26401 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26403 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26405 var nodeIsBefore = ss == 1;
26406 var nodeIsAfter = ee == -1;
26408 if (nodeIsBefore && nodeIsAfter) {
26411 if (!nodeIsBefore && nodeIsAfter) {
26412 return 1; //right trailed.
26415 if (nodeIsBefore && !nodeIsAfter) {
26416 return 2; // left trailed.
26422 cleanWordChars : function(input) {// change the chars to hex code
26425 [ 8211, "–" ],
26426 [ 8212, "—" ],
26434 var output = input;
26435 Roo.each(swapCodes, function(sw) {
26436 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26438 output = output.replace(swapper, sw[1]);
26448 cleanUpChild : function (node)
26451 new Roo.htmleditor.FilterComment({node : node});
26452 new Roo.htmleditor.FilterAttributes({
26454 attrib_black : this.ablack,
26455 attrib_clean : this.aclean,
26456 style_white : this.cwhite,
26457 style_black : this.cblack
26459 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
26460 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
26466 * Clean up MS wordisms...
26467 * @deprecated - use filter directly
26469 cleanWord : function(node)
26471 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
26478 * @deprecated - use filters
26480 cleanTableWidths : function(node)
26482 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
26489 applyBlacklists : function()
26491 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
26492 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
26494 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
26495 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
26496 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
26500 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26501 if (b.indexOf(tag) > -1) {
26504 this.white.push(tag);
26508 Roo.each(w, function(tag) {
26509 if (b.indexOf(tag) > -1) {
26512 if (this.white.indexOf(tag) > -1) {
26515 this.white.push(tag);
26520 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26521 if (w.indexOf(tag) > -1) {
26524 this.black.push(tag);
26528 Roo.each(b, function(tag) {
26529 if (w.indexOf(tag) > -1) {
26532 if (this.black.indexOf(tag) > -1) {
26535 this.black.push(tag);
26540 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
26541 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
26545 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26546 if (b.indexOf(tag) > -1) {
26549 this.cwhite.push(tag);
26553 Roo.each(w, function(tag) {
26554 if (b.indexOf(tag) > -1) {
26557 if (this.cwhite.indexOf(tag) > -1) {
26560 this.cwhite.push(tag);
26565 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26566 if (w.indexOf(tag) > -1) {
26569 this.cblack.push(tag);
26573 Roo.each(b, function(tag) {
26574 if (w.indexOf(tag) > -1) {
26577 if (this.cblack.indexOf(tag) > -1) {
26580 this.cblack.push(tag);
26585 setStylesheets : function(stylesheets)
26587 if(typeof(stylesheets) == 'string'){
26588 Roo.get(this.iframe.contentDocument.head).createChild({
26590 rel : 'stylesheet',
26599 Roo.each(stylesheets, function(s) {
26604 Roo.get(_this.iframe.contentDocument.head).createChild({
26606 rel : 'stylesheet',
26616 updateLanguage : function()
26618 if (!this.iframe || !this.iframe.contentDocument) {
26621 Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
26625 removeStylesheets : function()
26629 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26634 setStyle : function(style)
26636 Roo.get(this.iframe.contentDocument.head).createChild({
26645 // hide stuff that is not compatible
26659 * @event specialkey
26663 * @cfg {String} fieldClass @hide
26666 * @cfg {String} focusClass @hide
26669 * @cfg {String} autoCreate @hide
26672 * @cfg {String} inputType @hide
26675 * @cfg {String} invalidClass @hide
26678 * @cfg {String} invalidText @hide
26681 * @cfg {String} msgFx @hide
26684 * @cfg {String} validateOnBlur @hide
26688 Roo.HtmlEditorCore.white = [
26689 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
26691 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
26692 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
26693 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
26694 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
26695 'TABLE', 'UL', 'XMP',
26697 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
26700 'DIR', 'MENU', 'OL', 'UL', 'DL',
26706 Roo.HtmlEditorCore.black = [
26707 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26709 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
26710 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
26711 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
26712 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
26713 //'FONT' // CLEAN LATER..
26714 'COLGROUP', 'COL' // messy tables.
26718 Roo.HtmlEditorCore.clean = [ // ?? needed???
26719 'SCRIPT', 'STYLE', 'TITLE', 'XML'
26721 Roo.HtmlEditorCore.tag_remove = [
26726 Roo.HtmlEditorCore.ablack = [
26730 Roo.HtmlEditorCore.aclean = [
26731 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26735 Roo.HtmlEditorCore.pwhite= [
26736 'http', 'https', 'mailto'
26739 // white listed style attributes.
26740 Roo.HtmlEditorCore.cwhite= [
26741 // 'text-align', /// default is to allow most things..
26747 // black listed style attributes.
26748 Roo.HtmlEditorCore.cblack= [
26749 // 'font-size' -- this can be set by the project
26755 //<script type="text/javascript">
26758 * Ext JS Library 1.1.1
26759 * Copyright(c) 2006-2007, Ext JS, LLC.
26765 Roo.form.HtmlEditor = function(config){
26769 Roo.form.HtmlEditor.superclass.constructor.call(this, config);
26771 if (!this.toolbars) {
26772 this.toolbars = [];
26774 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26780 * @class Roo.form.HtmlEditor
26781 * @extends Roo.form.Field
26782 * Provides a lightweight HTML Editor component.
26784 * This has been tested on Fireforx / Chrome.. IE may not be so great..
26786 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
26787 * supported by this editor.</b><br/><br/>
26788 * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
26789 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
26791 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
26793 * @cfg {Boolean} clearUp
26797 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26802 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26807 * @cfg {Number} height (in pixels)
26811 * @cfg {Number} width (in pixels)
26816 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea rootURL + '/roojs1/css/undoreset.css', .
26819 stylesheets: false,
26823 * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
26828 * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
26834 * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
26839 * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
26844 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26846 allowComments: false,
26848 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
26850 enableBlocks : true,
26853 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
26854 * if you are doing an email editor, this probably needs disabling, it's designed
26858 * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
26862 * @cfg {String} language default en - language of text (usefull for rtl languages)
26871 // private properties
26872 validationEvent : false,
26874 initialized : false,
26877 onFocus : Roo.emptyFn,
26879 hideMode:'offsets',
26881 actionMode : 'container', // defaults to hiding it...
26883 defaultAutoCreate : { // modified by initCompnoent..
26885 style:"width:500px;height:300px;",
26886 autocomplete: "new-password"
26890 initComponent : function(){
26893 * @event initialize
26894 * Fires when the editor is fully initialized (including the iframe)
26895 * @param {HtmlEditor} this
26900 * Fires when the editor is first receives the focus. Any insertion must wait
26901 * until after this event.
26902 * @param {HtmlEditor} this
26906 * @event beforesync
26907 * Fires before the textarea is updated with content from the editor iframe. Return false
26908 * to cancel the sync.
26909 * @param {HtmlEditor} this
26910 * @param {String} html
26914 * @event beforepush
26915 * Fires before the iframe editor is updated with content from the textarea. Return false
26916 * to cancel the push.
26917 * @param {HtmlEditor} this
26918 * @param {String} html
26923 * Fires when the textarea is updated with content from the editor iframe.
26924 * @param {HtmlEditor} this
26925 * @param {String} html
26930 * Fires when the iframe editor is updated with content from the textarea.
26931 * @param {HtmlEditor} this
26932 * @param {String} html
26936 * @event editmodechange
26937 * Fires when the editor switches edit modes
26938 * @param {HtmlEditor} this
26939 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26941 editmodechange: true,
26943 * @event editorevent
26944 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26945 * @param {HtmlEditor} this
26949 * @event firstfocus
26950 * Fires when on first focus - needed by toolbars..
26951 * @param {HtmlEditor} this
26956 * Auto save the htmlEditor value as a file into Events
26957 * @param {HtmlEditor} this
26961 * @event savedpreview
26962 * preview the saved version of htmlEditor
26963 * @param {HtmlEditor} this
26965 savedpreview: true,
26968 * @event stylesheetsclick
26969 * Fires when press the Sytlesheets button
26970 * @param {Roo.HtmlEditorCore} this
26972 stylesheetsclick: true,
26975 * Fires when press user pastes into the editor
26976 * @param {Roo.HtmlEditorCore} this
26980 this.defaultAutoCreate = {
26982 style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
26983 autocomplete: "new-password"
26988 * Protected method that will not generally be called directly. It
26989 * is called when the editor creates its toolbar. Override this method if you need to
26990 * add custom toolbar buttons.
26991 * @param {HtmlEditor} editor
26993 createToolbar : function(editor){
26994 Roo.log("create toolbars");
26995 if (!editor.toolbars || !editor.toolbars.length) {
26996 editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
26999 for (var i =0 ; i < editor.toolbars.length;i++) {
27000 editor.toolbars[i] = Roo.factory(
27001 typeof(editor.toolbars[i]) == 'string' ?
27002 { xtype: editor.toolbars[i]} : editor.toolbars[i],
27003 Roo.form.HtmlEditor);
27004 editor.toolbars[i].init(editor);
27010 * get the Context selected node
27011 * @returns {DomElement|boolean} selected node if active or false if none
27014 getSelectedNode : function()
27016 if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
27019 return this.toolbars[1].tb.selectedNode;
27023 onRender : function(ct, position)
27026 Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27028 this.wrap = this.el.wrap({
27029 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27032 this.editorcore.onRender(ct, position);
27034 if (this.resizable) {
27035 this.resizeEl = new Roo.Resizable(this.wrap, {
27039 minHeight : this.height,
27040 height: this.height,
27041 handles : this.resizable,
27044 resize : function(r, w, h) {
27045 _t.onResize(w,h); // -something
27051 this.createToolbar(this);
27055 this.setSize(this.wrap.getSize());
27057 if (this.resizeEl) {
27058 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27059 // should trigger onReize..
27062 this.keyNav = new Roo.KeyNav(this.el, {
27064 "tab" : function(e){
27065 e.preventDefault();
27067 var value = this.getValue();
27069 var start = this.el.dom.selectionStart;
27070 var end = this.el.dom.selectionEnd;
27074 this.setValue(value.substring(0, start) + "\t" + value.substring(end));
27075 this.el.dom.setSelectionRange(end + 1, end + 1);
27079 var f = value.substring(0, start).split("\t");
27081 if(f.pop().length != 0){
27085 this.setValue(f.join("\t") + value.substring(end));
27086 this.el.dom.setSelectionRange(start - 1, start - 1);
27090 "home" : function(e){
27091 e.preventDefault();
27093 var curr = this.el.dom.selectionStart;
27094 var lines = this.getValue().split("\n");
27101 this.el.dom.setSelectionRange(0, 0);
27107 for (var i = 0; i < lines.length;i++) {
27108 pos += lines[i].length;
27118 pos -= lines[i].length;
27124 this.el.dom.setSelectionRange(pos, pos);
27128 this.el.dom.selectionStart = pos;
27129 this.el.dom.selectionEnd = curr;
27132 "end" : function(e){
27133 e.preventDefault();
27135 var curr = this.el.dom.selectionStart;
27136 var lines = this.getValue().split("\n");
27143 this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
27149 for (var i = 0; i < lines.length;i++) {
27151 pos += lines[i].length;
27165 this.el.dom.setSelectionRange(pos, pos);
27169 this.el.dom.selectionStart = curr;
27170 this.el.dom.selectionEnd = pos;
27175 doRelay : function(foo, bar, hname){
27176 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
27182 // if(this.autosave && this.w){
27183 // this.autoSaveFn = setInterval(this.autosave, 1000);
27188 onResize : function(w, h)
27190 Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27195 if(typeof w == 'number'){
27196 var aw = w - this.wrap.getFrameWidth('lr');
27197 this.el.setWidth(this.adjustWidth('textarea', aw));
27200 if(typeof h == 'number'){
27202 for (var i =0; i < this.toolbars.length;i++) {
27203 // fixme - ask toolbars for heights?
27204 tbh += this.toolbars[i].tb.el.getHeight();
27205 if (this.toolbars[i].footer) {
27206 tbh += this.toolbars[i].footer.el.getHeight();
27213 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27214 ah -= 5; // knock a few pixes off for look..
27216 this.el.setHeight(this.adjustWidth('textarea', ah));
27220 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27221 this.editorcore.onResize(ew,eh);
27226 * Toggles the editor between standard and source edit mode.
27227 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27229 toggleSourceEdit : function(sourceEditMode)
27231 this.editorcore.toggleSourceEdit(sourceEditMode);
27233 if(this.editorcore.sourceEditMode){
27234 Roo.log('editor - showing textarea');
27237 // Roo.log(this.syncValue());
27238 this.editorcore.syncValue();
27239 this.el.removeClass('x-hidden');
27240 this.el.dom.removeAttribute('tabIndex');
27242 this.el.dom.scrollTop = 0;
27245 for (var i = 0; i < this.toolbars.length; i++) {
27246 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27247 this.toolbars[i].tb.hide();
27248 this.toolbars[i].footer.hide();
27253 Roo.log('editor - hiding textarea');
27255 // Roo.log(this.pushValue());
27256 this.editorcore.pushValue();
27258 this.el.addClass('x-hidden');
27259 this.el.dom.setAttribute('tabIndex', -1);
27261 for (var i = 0; i < this.toolbars.length; i++) {
27262 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
27263 this.toolbars[i].tb.show();
27264 this.toolbars[i].footer.show();
27268 //this.deferFocus();
27271 this.setSize(this.wrap.getSize());
27272 this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
27274 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27277 // private (for BoxComponent)
27278 adjustSize : Roo.BoxComponent.prototype.adjustSize,
27280 // private (for BoxComponent)
27281 getResizeEl : function(){
27285 // private (for BoxComponent)
27286 getPositionEl : function(){
27291 initEvents : function(){
27292 this.originalValue = this.getValue();
27296 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27299 markInvalid : Roo.emptyFn,
27301 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27304 clearInvalid : Roo.emptyFn,
27306 setValue : function(v){
27307 Roo.form.HtmlEditor.superclass.setValue.call(this, v);
27308 this.editorcore.pushValue();
27312 * update the language in the body - really done by core
27313 * @param {String} language - eg. en / ar / zh-CN etc..
27315 updateLanguage : function(lang)
27317 this.language = lang;
27318 this.editorcore.language = lang;
27319 this.editorcore.updateLanguage();
27323 deferFocus : function(){
27324 this.focus.defer(10, this);
27328 focus : function(){
27329 this.editorcore.focus();
27335 onDestroy : function(){
27341 for (var i =0; i < this.toolbars.length;i++) {
27342 // fixme - ask toolbars for heights?
27343 this.toolbars[i].onDestroy();
27346 this.wrap.dom.innerHTML = '';
27347 this.wrap.remove();
27352 onFirstFocus : function(){
27353 //Roo.log("onFirstFocus");
27354 this.editorcore.onFirstFocus();
27355 for (var i =0; i < this.toolbars.length;i++) {
27356 this.toolbars[i].onFirstFocus();
27362 syncValue : function()
27364 this.editorcore.syncValue();
27367 pushValue : function()
27369 this.editorcore.pushValue();
27372 setStylesheets : function(stylesheets)
27374 this.editorcore.setStylesheets(stylesheets);
27377 removeStylesheets : function()
27379 this.editorcore.removeStylesheets();
27383 // hide stuff that is not compatible
27397 * @event specialkey
27401 * @cfg {String} fieldClass @hide
27404 * @cfg {String} focusClass @hide
27407 * @cfg {String} autoCreate @hide
27410 * @cfg {String} inputType @hide
27413 * @cfg {String} invalidClass @hide
27416 * @cfg {String} invalidText @hide
27419 * @cfg {String} msgFx @hide
27422 * @cfg {String} validateOnBlur @hide
27428 * Ext JS Library 1.1.1
27429 * Copyright(c) 2006-2007, Ext JS, LLC.
27435 * @class Roo.form.HtmlEditor.ToolbarStandard
27440 new Roo.form.HtmlEditor({
27443 new Roo.form.HtmlEditorToolbar1({
27444 disable : { fonts: 1 , format: 1, ..., ... , ...],
27450 * @cfg {Object} disable List of elements to disable..
27451 * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
27455 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27458 Roo.form.HtmlEditor.ToolbarStandard = function(config)
27461 Roo.apply(this, config);
27463 // default disabled, based on 'good practice'..
27464 this.disable = this.disable || {};
27465 Roo.applyIf(this.disable, {
27468 specialElements : true
27472 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27473 // dont call parent... till later.
27476 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
27483 editorcore : false,
27485 * @cfg {Object} disable List of toolbar elements to disable
27492 * @cfg {String} createLinkText The default text for the create link prompt
27494 createLinkText : 'Please enter the URL for the link:',
27496 * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
27498 defaultLinkValue : 'http:/'+'/',
27502 * @cfg {Array} fontFamilies An array of available font families
27520 // "á" , ?? a acute?
27525 "°" // , // degrees
27527 // "é" , // e ecute
27528 // "ú" , // u ecute?
27531 specialElements : [
27533 text: "Insert Table",
27536 ihtml : '<table><tr><td>Cell</td></tr></table>'
27540 text: "Insert Image",
27543 ihtml : '<img src="about:blank"/>'
27552 "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password",
27553 "input:submit", "input:button", "select", "textarea", "label" ],
27556 ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"],
27558 ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
27567 * @cfg {String} defaultFont default font to use.
27569 defaultFont: 'tahoma',
27571 fontSelect : false,
27574 formatCombo : false,
27576 init : function(editor)
27578 this.editor = editor;
27579 this.editorcore = editor.editorcore ? editor.editorcore : editor;
27580 var editorcore = this.editorcore;
27584 var fid = editorcore.frameId;
27586 function btn(id, toggle, handler){
27587 var xid = fid + '-'+ id ;
27591 cls : 'x-btn-icon x-edit-'+id,
27592 enableToggle:toggle !== false,
27593 scope: _t, // was editor...
27594 handler:handler||_t.relayBtnCmd,
27595 clickEvent:'mousedown',
27596 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27603 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27605 // stop form submits
27606 tb.el.on('click', function(e){
27607 e.preventDefault(); // what does this do?
27610 if(!this.disable.font) { // && !Roo.isSafari){
27611 /* why no safari for fonts
27612 editor.fontSelect = tb.el.createChild({
27615 cls:'x-font-select',
27616 html: this.createFontOptions()
27619 editor.fontSelect.on('change', function(){
27620 var font = editor.fontSelect.dom.value;
27621 editor.relayCmd('fontname', font);
27622 editor.deferFocus();
27626 editor.fontSelect.dom,
27632 if(!this.disable.formats){
27633 this.formatCombo = new Roo.form.ComboBox({
27634 store: new Roo.data.SimpleStore({
27637 data : this.formats // from states.js
27641 //autoCreate : {tag: "div", size: "20"},
27642 displayField:'tag',
27646 triggerAction: 'all',
27647 emptyText:'Add tag',
27648 selectOnFocus:true,
27651 'select': function(c, r, i) {
27652 editorcore.insertTag(r.get('tag'));
27658 tb.addField(this.formatCombo);
27662 if(!this.disable.format){
27667 btn('strikethrough')
27670 if(!this.disable.fontSize){
27675 btn('increasefontsize', false, editorcore.adjustFont),
27676 btn('decreasefontsize', false, editorcore.adjustFont)
27681 if(!this.disable.colors){
27684 id:editorcore.frameId +'-forecolor',
27685 cls:'x-btn-icon x-edit-forecolor',
27686 clickEvent:'mousedown',
27687 tooltip: this.buttonTips['forecolor'] || undefined,
27689 menu : new Roo.menu.ColorMenu({
27690 allowReselect: true,
27691 focus: Roo.emptyFn,
27694 selectHandler: function(cp, color){
27695 editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
27696 editor.deferFocus();
27699 clickEvent:'mousedown'
27702 id:editorcore.frameId +'backcolor',
27703 cls:'x-btn-icon x-edit-backcolor',
27704 clickEvent:'mousedown',
27705 tooltip: this.buttonTips['backcolor'] || undefined,
27707 menu : new Roo.menu.ColorMenu({
27708 focus: Roo.emptyFn,
27711 allowReselect: true,
27712 selectHandler: function(cp, color){
27714 editorcore.execCmd('useCSS', false);
27715 editorcore.execCmd('hilitecolor', color);
27716 editorcore.execCmd('useCSS', true);
27717 editor.deferFocus();
27719 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor',
27720 Roo.isSafari || Roo.isIE ? '#'+color : color);
27721 editor.deferFocus();
27725 clickEvent:'mousedown'
27730 // now add all the items...
27733 if(!this.disable.alignments){
27736 btn('justifyleft'),
27737 btn('justifycenter'),
27738 btn('justifyright')
27742 //if(!Roo.isSafari){
27743 if(!this.disable.links){
27746 btn('createlink', false, this.createLink) /// MOVE TO HERE?!!?!?!?!
27750 if(!this.disable.lists){
27753 btn('insertorderedlist'),
27754 btn('insertunorderedlist')
27757 if(!this.disable.sourceEdit){
27760 btn('sourceedit', true, function(btn){
27761 this.toggleSourceEdit(btn.pressed);
27768 // special menu.. - needs to be tidied up..
27769 if (!this.disable.special) {
27772 cls: 'x-edit-none',
27778 for (var i =0; i < this.specialChars.length; i++) {
27779 smenu.menu.items.push({
27781 html: this.specialChars[i],
27782 handler: function(a,b) {
27783 editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
27784 //editor.insertAtCursor(a.html);
27798 if (!this.disable.cleanStyles) {
27800 cls: 'x-btn-icon x-btn-clear',
27806 for (var i =0; i < this.cleanStyles.length; i++) {
27807 cmenu.menu.items.push({
27808 actiontype : this.cleanStyles[i],
27809 html: 'Remove ' + this.cleanStyles[i],
27810 handler: function(a,b) {
27813 var c = Roo.get(editorcore.doc.body);
27814 c.select('[style]').each(function(s) {
27815 s.dom.style.removeProperty(a.actiontype);
27817 editorcore.syncValue();
27822 cmenu.menu.items.push({
27823 actiontype : 'tablewidths',
27824 html: 'Remove Table Widths',
27825 handler: function(a,b) {
27826 editorcore.cleanTableWidths();
27827 editorcore.syncValue();
27831 cmenu.menu.items.push({
27832 actiontype : 'word',
27833 html: 'Remove MS Word Formating',
27834 handler: function(a,b) {
27835 editorcore.cleanWord();
27836 editorcore.syncValue();
27841 cmenu.menu.items.push({
27842 actiontype : 'all',
27843 html: 'Remove All Styles',
27844 handler: function(a,b) {
27846 var c = Roo.get(editorcore.doc.body);
27847 c.select('[style]').each(function(s) {
27848 s.dom.removeAttribute('style');
27850 editorcore.syncValue();
27855 cmenu.menu.items.push({
27856 actiontype : 'all',
27857 html: 'Remove All CSS Classes',
27858 handler: function(a,b) {
27860 var c = Roo.get(editorcore.doc.body);
27861 c.select('[class]').each(function(s) {
27862 s.dom.removeAttribute('class');
27864 editorcore.cleanWord();
27865 editorcore.syncValue();
27870 cmenu.menu.items.push({
27871 actiontype : 'tidy',
27872 html: 'Tidy HTML Source',
27873 handler: function(a,b) {
27874 new Roo.htmleditor.Tidy(editorcore.doc.body);
27875 editorcore.syncValue();
27884 if (!this.disable.specialElements) {
27887 cls: 'x-edit-none',
27892 for (var i =0; i < this.specialElements.length; i++) {
27893 semenu.menu.items.push(
27895 handler: function(a,b) {
27896 editor.insertAtCursor(this.ihtml);
27898 }, this.specialElements[i])
27910 for(var i =0; i< this.btns.length;i++) {
27911 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
27912 b.cls = 'x-edit-none';
27914 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
27915 b.cls += ' x-init-enable';
27918 b.scope = editorcore;
27926 // disable everything...
27928 this.tb.items.each(function(item){
27931 item.id != editorcore.frameId+ '-sourceedit' &&
27932 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
27938 this.rendered = true;
27940 // the all the btns;
27941 editor.on('editorevent', this.updateToolbar, this);
27942 // other toolbars need to implement this..
27943 //editor.on('editmodechange', this.updateToolbar, this);
27947 relayBtnCmd : function(btn) {
27948 this.editorcore.relayCmd(btn.cmd);
27950 // private used internally
27951 createLink : function(){
27952 //Roo.log("create link?");
27953 var ec = this.editorcore;
27954 var ar = ec.getAllAncestors();
27956 for(var i = 0;i< ar.length;i++) {
27957 if (ar[i] && ar[i].nodeName == 'A') {
27965 Roo.MessageBox.show({
27966 title : "Add / Edit Link URL",
27967 msg : "Enter the url for the link",
27968 buttons: Roo.MessageBox.OKCANCEL,
27969 fn: function(btn, url){
27973 if(url && url != 'http:/'+'/'){
27975 n.setAttribute('href', url);
27977 ec.relayCmd('createlink', url);
27983 //multiline: multiline,
27985 value : n ? n.getAttribute('href') : ''
27989 }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
27995 * Protected method that will not generally be called directly. It triggers
27996 * a toolbar update by reading the markup state of the current selection in the editor.
27998 updateToolbar: function(){
28000 if(!this.editorcore.activated){
28001 this.editor.onFirstFocus();
28005 var btns = this.tb.items.map,
28006 doc = this.editorcore.doc,
28007 frameId = this.editorcore.frameId;
28009 if(!this.disable.font && !Roo.isSafari){
28011 var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
28012 if(name != this.fontSelect.dom.value){
28013 this.fontSelect.dom.value = name;
28017 if(!this.disable.format){
28018 btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
28019 btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
28020 btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
28021 btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
28023 if(!this.disable.alignments){
28024 btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
28025 btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
28026 btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
28028 if(!Roo.isSafari && !this.disable.lists){
28029 btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
28030 btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
28033 var ans = this.editorcore.getAllAncestors();
28034 if (this.formatCombo) {
28037 var store = this.formatCombo.store;
28038 this.formatCombo.setValue("");
28039 for (var i =0; i < ans.length;i++) {
28040 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28042 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28050 // hides menus... - so this cant be on a menu...
28051 Roo.menu.MenuMgr.hideAll();
28053 //this.editorsyncValue();
28057 createFontOptions : function(){
28058 var buf = [], fs = this.fontFamilies, ff, lc;
28062 for(var i = 0, len = fs.length; i< len; i++){
28064 lc = ff.toLowerCase();
28066 '<option value="',lc,'" style="font-family:',ff,';"',
28067 (this.defaultFont == lc ? ' selected="true">' : '>'),
28072 return buf.join('');
28075 toggleSourceEdit : function(sourceEditMode){
28077 Roo.log("toolbar toogle");
28078 if(sourceEditMode === undefined){
28079 sourceEditMode = !this.sourceEditMode;
28081 this.sourceEditMode = sourceEditMode === true;
28082 var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
28083 // just toggle the button?
28084 if(btn.pressed !== this.sourceEditMode){
28085 btn.toggle(this.sourceEditMode);
28089 if(sourceEditMode){
28090 Roo.log("disabling buttons");
28091 this.tb.items.each(function(item){
28092 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
28098 Roo.log("enabling buttons");
28099 if(this.editorcore.initialized){
28100 this.tb.items.each(function(item){
28103 // initialize 'blocks'
28104 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
28105 Roo.htmleditor.Block.factory(e).updateElement(e);
28111 Roo.log("calling toggole on editor");
28112 // tell the editor that it's been pressed..
28113 this.editor.toggleSourceEdit(sourceEditMode);
28117 * Object collection of toolbar tooltips for the buttons in the editor. The key
28118 * is the command id associated with that button and the value is a valid QuickTips object.
28123 title: 'Bold (Ctrl+B)',
28124 text: 'Make the selected text bold.',
28125 cls: 'x-html-editor-tip'
28128 title: 'Italic (Ctrl+I)',
28129 text: 'Make the selected text italic.',
28130 cls: 'x-html-editor-tip'
28138 title: 'Bold (Ctrl+B)',
28139 text: 'Make the selected text bold.',
28140 cls: 'x-html-editor-tip'
28143 title: 'Italic (Ctrl+I)',
28144 text: 'Make the selected text italic.',
28145 cls: 'x-html-editor-tip'
28148 title: 'Underline (Ctrl+U)',
28149 text: 'Underline the selected text.',
28150 cls: 'x-html-editor-tip'
28153 title: 'Strikethrough',
28154 text: 'Strikethrough the selected text.',
28155 cls: 'x-html-editor-tip'
28157 increasefontsize : {
28158 title: 'Grow Text',
28159 text: 'Increase the font size.',
28160 cls: 'x-html-editor-tip'
28162 decreasefontsize : {
28163 title: 'Shrink Text',
28164 text: 'Decrease the font size.',
28165 cls: 'x-html-editor-tip'
28168 title: 'Text Highlight Color',
28169 text: 'Change the background color of the selected text.',
28170 cls: 'x-html-editor-tip'
28173 title: 'Font Color',
28174 text: 'Change the color of the selected text.',
28175 cls: 'x-html-editor-tip'
28178 title: 'Align Text Left',
28179 text: 'Align text to the left.',
28180 cls: 'x-html-editor-tip'
28183 title: 'Center Text',
28184 text: 'Center text in the editor.',
28185 cls: 'x-html-editor-tip'
28188 title: 'Align Text Right',
28189 text: 'Align text to the right.',
28190 cls: 'x-html-editor-tip'
28192 insertunorderedlist : {
28193 title: 'Bullet List',
28194 text: 'Start a bulleted list.',
28195 cls: 'x-html-editor-tip'
28197 insertorderedlist : {
28198 title: 'Numbered List',
28199 text: 'Start a numbered list.',
28200 cls: 'x-html-editor-tip'
28203 title: 'Hyperlink',
28204 text: 'Make the selected text a hyperlink.',
28205 cls: 'x-html-editor-tip'
28208 title: 'Source Edit',
28209 text: 'Switch to source editing mode.',
28210 cls: 'x-html-editor-tip'
28214 onDestroy : function(){
28217 this.tb.items.each(function(item){
28219 item.menu.removeAll();
28221 item.menu.el.destroy();
28229 onFirstFocus: function() {
28230 this.tb.items.each(function(item){
28239 // <script type="text/javascript">
28242 * Ext JS Library 1.1.1
28243 * Copyright(c) 2006-2007, Ext JS, LLC.
28250 * @class Roo.form.HtmlEditor.ToolbarContext
28255 new Roo.form.HtmlEditor({
28258 { xtype: 'ToolbarStandard', styles : {} }
28259 { xtype: 'ToolbarContext', disable : {} }
28265 * @config : {Object} disable List of elements to disable.. (not done yet.)
28266 * @config : {Object} styles Map of styles available.
28270 Roo.form.HtmlEditor.ToolbarContext = function(config)
28273 Roo.apply(this, config);
28274 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28275 // dont call parent... till later.
28276 this.styles = this.styles || {};
28281 Roo.form.HtmlEditor.ToolbarContext.types = {
28296 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28322 opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
28393 name : 'selectoptions',
28399 // should we really allow this??
28400 // should this just be
28417 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
28418 Roo.form.HtmlEditor.ToolbarContext.stores = false;
28420 Roo.form.HtmlEditor.ToolbarContext.options = {
28422 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
28423 [ 'Courier New', 'Courier New'],
28424 [ 'Tahoma', 'Tahoma'],
28425 [ 'Times New Roman,serif', 'Times'],
28426 [ 'Verdana','Verdana' ]
28430 // fixme - these need to be configurable..
28433 //Roo.form.HtmlEditor.ToolbarContext.types
28436 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype, {
28443 editorcore : false,
28445 * @cfg {Object} disable List of toolbar elements to disable
28450 * @cfg {Object} styles List of styles
28451 * eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] }
28453 * These must be defined in the page, so they get rendered correctly..
28464 init : function(editor)
28466 this.editor = editor;
28467 this.editorcore = editor.editorcore ? editor.editorcore : editor;
28468 var editorcore = this.editorcore;
28470 var fid = editorcore.frameId;
28472 function btn(id, toggle, handler){
28473 var xid = fid + '-'+ id ;
28477 cls : 'x-btn-icon x-edit-'+id,
28478 enableToggle:toggle !== false,
28479 scope: editorcore, // was editor...
28480 handler:handler||editorcore.relayBtnCmd,
28481 clickEvent:'mousedown',
28482 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28486 // create a new element.
28487 var wdiv = editor.wrap.createChild({
28489 }, editor.wrap.dom.firstChild.nextSibling, true);
28491 // can we do this more than once??
28493 // stop form submits
28496 // disable everything...
28497 var ty= Roo.form.HtmlEditor.ToolbarContext.types;
28498 this.toolbars = {};
28499 // block toolbars are built in updateToolbar when needed.
28500 for (var i in ty) {
28502 this.toolbars[i] = this.buildToolbar(ty[i],i);
28504 this.tb = this.toolbars.BODY;
28506 this.buildFooter();
28507 this.footer.show();
28508 editor.on('hide', function( ) { this.footer.hide() }, this);
28509 editor.on('show', function( ) { this.footer.show() }, this);
28512 this.rendered = true;
28514 // the all the btns;
28515 editor.on('editorevent', this.updateToolbar, this);
28516 // other toolbars need to implement this..
28517 //editor.on('editmodechange', this.updateToolbar, this);
28523 * Protected method that will not generally be called directly. It triggers
28524 * a toolbar update by reading the markup state of the current selection in the editor.
28526 * Note you can force an update by calling on('editorevent', scope, false)
28528 updateToolbar: function(editor ,ev, sel)
28532 ev.stopEvent(); // se if we can stop this looping with mutiple events.
28536 // capture mouse up - this is handy for selecting images..
28537 // perhaps should go somewhere else...
28538 if(!this.editorcore.activated){
28539 this.editor.onFirstFocus();
28542 //Roo.log(ev ? ev.target : 'NOTARGET');
28545 // http://developer.yahoo.com/yui/docs/simple-editor.js.html
28546 // selectNode - might want to handle IE?
28551 (ev.type == 'mouseup' || ev.type == 'click' ) &&
28552 ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
28553 // they have click on an image...
28554 // let's see if we can change the selection...
28557 // this triggers looping?
28558 //this.editorcore.selectNode(sel);
28562 // this forces an id..
28563 Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
28564 e.classList.remove('roo-ed-selection');
28566 //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
28567 //Roo.get(node).addClass('roo-ed-selection');
28569 //var updateFooter = sel ? false : true;
28572 var ans = this.editorcore.getAllAncestors();
28575 var ty = Roo.form.HtmlEditor.ToolbarContext.types;
28578 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
28579 sel = sel ? sel : this.editorcore.doc.body;
28580 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
28584 var tn = sel.tagName.toUpperCase();
28585 var lastSel = this.tb.selectedNode;
28586 this.tb.selectedNode = sel;
28587 var left_label = tn;
28589 // ok see if we are editing a block?
28592 // you are not actually selecting the block.
28593 if (sel && sel.hasAttribute('data-block')) {
28595 } else if (sel && sel.closest('[data-block]')) {
28597 db = sel.closest('[data-block]');
28598 //var cepar = sel.closest('[contenteditable=true]');
28599 //if (db && cepar && cepar.tagName != 'BODY') {
28600 // db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
28606 //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
28607 if (db && this.editorcore.enableBlocks) {
28608 block = Roo.htmleditor.Block.factory(db);
28613 db.classList.length > 0 ? db.className + ' ' : ''
28614 ) + 'roo-ed-selection';
28616 // since we removed it earlier... its not there..
28617 tn = 'BLOCK.' + db.getAttribute('data-block');
28619 //this.editorcore.selectNode(db);
28620 if (typeof(this.toolbars[tn]) == 'undefined') {
28621 this.toolbars[tn] = this.buildToolbar( false ,tn ,block.friendly_name, block);
28623 this.toolbars[tn].selectedNode = db;
28624 left_label = block.friendly_name;
28625 ans = this.editorcore.getAllAncestors();
28633 if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
28634 return; // no change?
28640 ///console.log("show: " + tn);
28641 this.tb = typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
28645 this.tb.items.first().el.innerHTML = left_label + ': ';
28648 // update attributes
28649 if (block && this.tb.fields) {
28651 this.tb.fields.each(function(e) {
28652 e.setValue(block[e.name]);
28656 } else if (this.tb.fields && this.tb.selectedNode) {
28657 this.tb.fields.each( function(e) {
28659 e.setValue(this.tb.selectedNode.style[e.stylename]);
28662 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
28664 this.updateToolbarStyles(this.tb.selectedNode);
28669 Roo.menu.MenuMgr.hideAll();
28674 // update the footer
28676 this.updateFooter(ans);
28680 updateToolbarStyles : function(sel)
28682 var hasStyles = false;
28683 for(var i in this.styles) {
28689 if (hasStyles && this.tb.hasStyles) {
28690 var st = this.tb.fields.item(0);
28692 st.store.removeAll();
28693 var cn = sel.className.split(/\s+/);
28696 if (this.styles['*']) {
28698 Roo.each(this.styles['*'], function(v) {
28699 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
28702 if (this.styles[tn]) {
28703 Roo.each(this.styles[tn], function(v) {
28704 avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );
28708 st.store.loadData(avs);
28715 updateFooter : function(ans)
28718 if (ans === false) {
28719 this.footDisp.dom.innerHTML = '';
28723 this.footerEls = ans.reverse();
28724 Roo.each(this.footerEls, function(a,i) {
28725 if (!a) { return; }
28726 html += html.length ? ' > ' : '';
28728 html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
28733 var sz = this.footDisp.up('td').getSize();
28734 this.footDisp.dom.style.width = (sz.width -10) + 'px';
28735 this.footDisp.dom.style.marginLeft = '5px';
28737 this.footDisp.dom.style.overflow = 'hidden';
28739 this.footDisp.dom.innerHTML = html;
28746 onDestroy : function(){
28749 this.tb.items.each(function(item){
28751 item.menu.removeAll();
28753 item.menu.el.destroy();
28761 onFirstFocus: function() {
28762 // need to do this for all the toolbars..
28763 this.tb.items.each(function(item){
28767 buildToolbar: function(tlist, nm, friendly_name, block)
28769 var editor = this.editor;
28770 var editorcore = this.editorcore;
28771 // create a new element.
28772 var wdiv = editor.wrap.createChild({
28774 }, editor.wrap.dom.firstChild.nextSibling, true);
28777 var tb = new Roo.Toolbar(wdiv);
28778 ///this.tb = tb; // << this sets the active toolbar..
28779 if (tlist === false && block) {
28780 tlist = block.contextMenu(this);
28783 tb.hasStyles = false;
28786 tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ": ");
28788 var styles = Array.from(this.styles);
28792 if (styles && styles.length) {
28793 tb.hasStyles = true;
28794 // this needs a multi-select checkbox...
28795 tb.addField( new Roo.form.ComboBox({
28796 store: new Roo.data.SimpleStore({
28798 fields: ['val', 'selected'],
28801 name : '-roo-edit-className',
28802 attrname : 'className',
28803 displayField: 'val',
28807 triggerAction: 'all',
28808 emptyText:'Select Style',
28809 selectOnFocus:true,
28812 'select': function(c, r, i) {
28813 // initial support only for on class per el..
28814 tb.selectedNode.className = r ? r.get('val') : '';
28815 editorcore.syncValue();
28822 var tbc = Roo.form.HtmlEditor.ToolbarContext;
28825 for (var i = 0; i < tlist.length; i++) {
28827 // newer versions will use xtype cfg to create menus.
28828 if (typeof(tlist[i].xtype) != 'undefined') {
28830 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
28836 var item = tlist[i];
28837 tb.add(item.title + ": ");
28840 //optname == used so you can configure the options available..
28841 var opts = item.opts ? item.opts : false;
28842 if (item.optname) { // use the b
28843 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
28848 // opts == pulldown..
28849 tb.addField( new Roo.form.ComboBox({
28850 store: typeof(tbc.stores[i]) != 'undefined' ? Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
28852 fields: ['val', 'display'],
28855 name : '-roo-edit-' + tlist[i].name,
28857 attrname : tlist[i].name,
28858 stylename : item.style ? item.style : false,
28860 displayField: item.displayField ? item.displayField : 'val',
28861 valueField : 'val',
28863 mode: typeof(tbc.stores[tlist[i].name]) != 'undefined' ? 'remote' : 'local',
28865 triggerAction: 'all',
28866 emptyText:'Select',
28867 selectOnFocus:true,
28868 width: item.width ? item.width : 130,
28870 'select': function(c, r, i) {
28874 tb.selectedNode.style[c.stylename] = r.get('val');
28875 editorcore.syncValue();
28879 tb.selectedNode.removeAttribute(c.attrname);
28880 editorcore.syncValue();
28883 tb.selectedNode.setAttribute(c.attrname, r.get('val'));
28884 editorcore.syncValue();
28893 tb.addField( new Roo.form.TextField({
28896 //allowBlank:false,
28902 tb.addField( new Roo.form.TextField({
28903 name: '-roo-edit-' + tlist[i].name,
28904 attrname : tlist[i].name,
28910 'change' : function(f, nv, ov) {
28913 tb.selectedNode.setAttribute(f.attrname, nv);
28914 editorcore.syncValue();
28922 var show_delete = !block || block.deleteTitle !== false;
28924 show_delete = false;
28928 text: 'Stylesheets',
28931 click : function ()
28933 _this.editor.fireEvent('stylesheetsclick', _this.editor);
28942 text: block && block.deleteTitle ? block.deleteTitle : 'Remove Block or Formating', // remove the tag, and puts the children outside...
28945 click : function ()
28947 var sn = tb.selectedNode;
28949 sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
28955 var stn = sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
28956 if (sn.hasAttribute('data-block')) {
28957 stn = sn.nextSibling || sn.previousSibling || sn.parentNode;
28958 sn.parentNode.removeChild(sn);
28960 } else if (sn && sn.tagName != 'BODY') {
28961 // remove and keep parents.
28962 a = new Roo.htmleditor.FilterKeepChildren({tag : false});
28967 var range = editorcore.createRange();
28969 range.setStart(stn,0);
28970 range.setEnd(stn,0);
28971 var selection = editorcore.getSelection();
28972 selection.removeAllRanges();
28973 selection.addRange(range);
28976 //_this.updateToolbar(null, null, pn);
28977 _this.updateToolbar(null, null, null);
28978 _this.updateFooter(false);
28989 tb.el.on('click', function(e){
28990 e.preventDefault(); // what does this do?
28992 tb.el.setVisibilityMode( Roo.Element.DISPLAY);
28995 // dont need to disable them... as they will get hidden
29000 buildFooter : function()
29003 var fel = this.editor.wrap.createChild();
29004 this.footer = new Roo.Toolbar(fel);
29005 // toolbar has scrolly on left / right?
29006 var footDisp= new Roo.Toolbar.Fill();
29012 handler : function() {
29013 _t.footDisp.scrollTo('left',0,true)
29017 this.footer.add( footDisp );
29022 handler : function() {
29024 _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
29028 var fel = Roo.get(footDisp.el);
29029 fel.addClass('x-editor-context');
29030 this.footDispWrap = fel;
29031 this.footDispWrap.overflow = 'hidden';
29033 this.footDisp = fel.createChild();
29034 this.footDispWrap.on('click', this.onContextClick, this)
29038 // when the footer contect changes
29039 onContextClick : function (ev,dom)
29041 ev.preventDefault();
29042 var cn = dom.className;
29044 if (!cn.match(/x-ed-loc-/)) {
29047 var n = cn.split('-').pop();
29048 var ans = this.footerEls;
29051 this.editorcore.selectNode(sel);
29054 this.updateToolbar(null, null, sel);
29071 * Ext JS Library 1.1.1
29072 * Copyright(c) 2006-2007, Ext JS, LLC.
29074 * Originally Released Under LGPL - original licence link has changed is not relivant.
29077 * <script type="text/javascript">
29081 * @class Roo.form.BasicForm
29082 * @extends Roo.util.Observable
29083 * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
29085 * @param {String/HTMLElement/Roo.Element} el The form element or its id
29086 * @param {Object} config Configuration options
29088 Roo.form.BasicForm = function(el, config){
29089 this.allItems = [];
29090 this.childForms = [];
29091 Roo.apply(this, config);
29093 * The Roo.form.Field items in this form.
29094 * @type MixedCollection
29098 this.items = new Roo.util.MixedCollection(false, function(o){
29099 return o.id || (o.id = Roo.id());
29103 * @event beforeaction
29104 * Fires before any action is performed. Return false to cancel the action.
29105 * @param {Form} this
29106 * @param {Action} action The action to be performed
29108 beforeaction: true,
29110 * @event actionfailed
29111 * Fires when an action fails.
29112 * @param {Form} this
29113 * @param {Action} action The action that failed
29115 actionfailed : true,
29117 * @event actioncomplete
29118 * Fires when an action is completed.
29119 * @param {Form} this
29120 * @param {Action} action The action that completed
29122 actioncomplete : true
29127 Roo.form.BasicForm.superclass.constructor.call(this);
29129 Roo.form.BasicForm.popover.apply();
29132 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
29134 * @cfg {String} method
29135 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
29138 * @cfg {DataReader} reader
29139 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
29140 * This is optional as there is built-in support for processing JSON.
29143 * @cfg {DataReader} errorReader
29144 * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
29145 * This is completely optional as there is built-in support for processing JSON.
29148 * @cfg {String} url
29149 * The URL to use for form actions if one isn't supplied in the action options.
29152 * @cfg {Boolean} fileUpload
29153 * Set to true if this form is a file upload.
29157 * @cfg {Object} baseParams
29158 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
29163 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
29168 activeAction : null,
29171 * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
29172 * or setValues() data instead of when the form was first created.
29174 trackResetOnLoad : false,
29178 * childForms - used for multi-tab forms
29181 childForms : false,
29184 * allItems - full list of fields.
29190 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
29191 * element by passing it or its id or mask the form itself by passing in true.
29194 waitMsgTarget : false,
29199 disableMask : false,
29202 * @cfg {Boolean} errorMask (true|false) default false
29207 * @cfg {Number} maskOffset Default 100
29212 initEl : function(el){
29213 this.el = Roo.get(el);
29214 this.id = this.el.id || Roo.id();
29215 this.el.on('submit', this.onSubmit, this);
29216 this.el.addClass('x-form');
29220 onSubmit : function(e){
29225 * Returns true if client-side validation on the form is successful.
29228 isValid : function(){
29230 var target = false;
29231 this.items.each(function(f){
29238 if(!target && f.el.isVisible(true)){
29243 if(this.errorMask && !valid){
29244 Roo.form.BasicForm.popover.mask(this, target);
29250 * Returns array of invalid form fields.
29254 invalidFields : function()
29257 this.items.each(function(f){
29270 * DEPRICATED Returns true if any fields in this form have changed since their original load.
29273 isDirty : function(){
29275 this.items.each(function(f){
29285 * Returns true if any fields in this form have changed since their original load. (New version)
29289 hasChanged : function()
29292 this.items.each(function(f){
29293 if(f.hasChanged()){
29302 * Resets all hasChanged to 'false' -
29303 * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
29304 * So hasChanged storage is only to be used for this purpose
29307 resetHasChanged : function()
29309 this.items.each(function(f){
29310 f.resetHasChanged();
29317 * Performs a predefined action (submit or load) or custom actions you define on this form.
29318 * @param {String} actionName The name of the action type
29319 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
29320 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
29321 * accept other config options):
29323 Property Type Description
29324 ---------------- --------------- ----------------------------------------------------------------------------------
29325 url String The url for the action (defaults to the form's url)
29326 method String The form method to use (defaults to the form's method, or POST if not defined)
29327 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
29328 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
29329 validate the form on the client (defaults to false)
29331 * @return {BasicForm} this
29333 doAction : function(action, options){
29334 if(typeof action == 'string'){
29335 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
29337 if(this.fireEvent('beforeaction', this, action) !== false){
29338 this.beforeAction(action);
29339 action.run.defer(100, action);
29345 * Shortcut to do a submit action.
29346 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29347 * @return {BasicForm} this
29349 submit : function(options){
29350 this.doAction('submit', options);
29355 * Shortcut to do a load action.
29356 * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
29357 * @return {BasicForm} this
29359 load : function(options){
29360 this.doAction('load', options);
29365 * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
29366 * @param {Record} record The record to edit
29367 * @return {BasicForm} this
29369 updateRecord : function(record){
29370 record.beginEdit();
29371 var fs = record.fields;
29372 fs.each(function(f){
29373 var field = this.findField(f.name);
29375 record.set(f.name, field.getValue());
29383 * Loads an Roo.data.Record into this form.
29384 * @param {Record} record The record to load
29385 * @return {BasicForm} this
29387 loadRecord : function(record){
29388 this.setValues(record.data);
29393 beforeAction : function(action){
29394 var o = action.options;
29396 if(!this.disableMask) {
29397 if(this.waitMsgTarget === true){
29398 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
29399 }else if(this.waitMsgTarget){
29400 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
29401 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
29403 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
29411 afterAction : function(action, success){
29412 this.activeAction = null;
29413 var o = action.options;
29415 if(!this.disableMask) {
29416 if(this.waitMsgTarget === true){
29418 }else if(this.waitMsgTarget){
29419 this.waitMsgTarget.unmask();
29421 Roo.MessageBox.updateProgress(1);
29422 Roo.MessageBox.hide();
29430 Roo.callback(o.success, o.scope, [this, action]);
29431 this.fireEvent('actioncomplete', this, action);
29435 // failure condition..
29436 // we have a scenario where updates need confirming.
29437 // eg. if a locking scenario exists..
29438 // we look for { errors : { needs_confirm : true }} in the response.
29440 (typeof(action.result) != 'undefined') &&
29441 (typeof(action.result.errors) != 'undefined') &&
29442 (typeof(action.result.errors.needs_confirm) != 'undefined')
29445 Roo.MessageBox.confirm(
29446 "Change requires confirmation",
29447 action.result.errorMsg,
29452 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
29462 Roo.callback(o.failure, o.scope, [this, action]);
29463 // show an error message if no failed handler is set..
29464 if (!this.hasListener('actionfailed')) {
29465 Roo.MessageBox.alert("Error",
29466 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
29467 action.result.errorMsg :
29468 "Saving Failed, please check your entries or try again"
29472 this.fireEvent('actionfailed', this, action);
29478 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
29479 * @param {String} id The value to search for
29482 findField : function(id){
29483 var field = this.items.get(id);
29485 this.items.each(function(f){
29486 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
29492 return field || null;
29496 * Add a secondary form to this one,
29497 * Used to provide tabbed forms. One form is primary, with hidden values
29498 * which mirror the elements from the other forms.
29500 * @param {Roo.form.Form} form to add.
29503 addForm : function(form)
29506 if (this.childForms.indexOf(form) > -1) {
29510 this.childForms.push(form);
29512 Roo.each(form.allItems, function (fe) {
29514 n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
29515 if (this.findField(n)) { // already added..
29518 var add = new Roo.form.Hidden({
29521 add.render(this.el);
29528 * Mark fields in this form invalid in bulk.
29529 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
29530 * @return {BasicForm} this
29532 markInvalid : function(errors){
29533 if(errors instanceof Array){
29534 for(var i = 0, len = errors.length; i < len; i++){
29535 var fieldError = errors[i];
29536 var f = this.findField(fieldError.id);
29538 f.markInvalid(fieldError.msg);
29544 if(typeof errors[id] != 'function' && (field = this.findField(id))){
29545 field.markInvalid(errors[id]);
29549 Roo.each(this.childForms || [], function (f) {
29550 f.markInvalid(errors);
29557 * Set values for fields in this form in bulk.
29558 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
29559 * @return {BasicForm} this
29561 setValues : function(values){
29562 if(values instanceof Array){ // array of objects
29563 for(var i = 0, len = values.length; i < len; i++){
29565 var f = this.findField(v.id);
29567 f.setValue(v.value);
29568 if(this.trackResetOnLoad){
29569 f.originalValue = f.getValue();
29573 }else{ // object hash
29576 if(typeof values[id] != 'function' && (field = this.findField(id))){
29578 if (field.setFromData &&
29579 field.valueField &&
29580 field.displayField &&
29581 // combos' with local stores can
29582 // be queried via setValue()
29583 // to set their value..
29584 (field.store && !field.store.isLocal)
29588 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
29589 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
29590 field.setFromData(sd);
29593 field.setValue(values[id]);
29597 if(this.trackResetOnLoad){
29598 field.originalValue = field.getValue();
29603 this.resetHasChanged();
29606 Roo.each(this.childForms || [], function (f) {
29607 f.setValues(values);
29608 f.resetHasChanged();
29615 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
29616 * they are returned as an array.
29617 * @param {Boolean} asString
29620 getValues : function(asString)
29622 if (this.childForms) {
29623 // copy values from the child forms
29624 Roo.each(this.childForms, function (f) {
29625 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
29630 if (typeof(FormData) != 'undefined' && asString !== true) {
29631 // this relies on a 'recent' version of chrome apparently...
29633 var fd = (new FormData(this.el.dom)).entries();
29635 var ent = fd.next();
29636 while (!ent.done) {
29637 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
29648 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
29649 if(asString === true){
29652 return Roo.urlDecode(fs);
29656 * Returns the fields in this form as an object with key/value pairs.
29657 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
29658 * Normally this will not return readOnly data
29659 * @param {Boolean} with_readonly return readonly field data.
29662 getFieldValues : function(with_readonly)
29664 if (this.childForms) {
29665 // copy values from the child forms
29666 // should this call getFieldValues - probably not as we do not currently copy
29667 // hidden fields when we generate..
29668 Roo.each(this.childForms, function (f) {
29669 this.setValues(f.getFieldValues());
29674 this.items.each(function(f){
29676 if (f.readOnly && with_readonly !== true) {
29677 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
29678 // if a subform contains a copy of them.
29679 // if you have subforms with the same editable data, you will need to copy the data back
29683 if (!f.getName()) {
29686 var v = f.getValue();
29687 if (f.inputType =='radio') {
29688 if (typeof(ret[f.getName()]) == 'undefined') {
29689 ret[f.getName()] = ''; // empty..
29692 if (!f.el.dom.checked) {
29696 v = f.el.dom.value;
29700 // not sure if this supported any more..
29701 if ((typeof(v) == 'object') && f.getRawValue) {
29702 v = f.getRawValue() ; // dates..
29704 // combo boxes where name != hiddenName...
29705 if (f.name != f.getName()) {
29706 ret[f.name] = f.getRawValue();
29708 ret[f.getName()] = v;
29715 * Clears all invalid messages in this form.
29716 * @return {BasicForm} this
29718 clearInvalid : function(){
29719 this.items.each(function(f){
29723 Roo.each(this.childForms || [], function (f) {
29732 * Resets this form.
29733 * @return {BasicForm} this
29735 reset : function(){
29736 this.items.each(function(f){
29740 Roo.each(this.childForms || [], function (f) {
29743 this.resetHasChanged();
29749 * Add Roo.form components to this form.
29750 * @param {Field} field1
29751 * @param {Field} field2 (optional)
29752 * @param {Field} etc (optional)
29753 * @return {BasicForm} this
29756 this.items.addAll(Array.prototype.slice.call(arguments, 0));
29762 * Removes a field from the items collection (does NOT remove its markup).
29763 * @param {Field} field
29764 * @return {BasicForm} this
29766 remove : function(field){
29767 this.items.remove(field);
29772 * Looks at the fields in this form, checks them for an id attribute,
29773 * and calls applyTo on the existing dom element with that id.
29774 * @return {BasicForm} this
29776 render : function(){
29777 this.items.each(function(f){
29778 if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
29786 * Calls {@link Ext#apply} for all fields in this form with the passed object.
29787 * @param {Object} values
29788 * @return {BasicForm} this
29790 applyToFields : function(o){
29791 this.items.each(function(f){
29798 * Calls {@link Ext#applyIf} for all field in this form with the passed object.
29799 * @param {Object} values
29800 * @return {BasicForm} this
29802 applyIfToFields : function(o){
29803 this.items.each(function(f){
29811 Roo.BasicForm = Roo.form.BasicForm;
29813 Roo.apply(Roo.form.BasicForm, {
29827 intervalID : false,
29833 if(this.isApplied){
29838 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
29839 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
29840 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
29841 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
29844 this.maskEl.top.enableDisplayMode("block");
29845 this.maskEl.left.enableDisplayMode("block");
29846 this.maskEl.bottom.enableDisplayMode("block");
29847 this.maskEl.right.enableDisplayMode("block");
29849 Roo.get(document.body).on('click', function(){
29853 Roo.get(document.body).on('touchstart', function(){
29857 this.isApplied = true
29860 mask : function(form, target)
29864 this.target = target;
29866 if(!this.form.errorMask || !target.el){
29870 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
29872 var ot = this.target.el.calcOffsetsTo(scrollable);
29874 var scrollTo = ot[1] - this.form.maskOffset;
29876 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
29878 scrollable.scrollTo('top', scrollTo);
29880 var el = this.target.wrap || this.target.el;
29882 var box = el.getBox();
29884 this.maskEl.top.setStyle('position', 'absolute');
29885 this.maskEl.top.setStyle('z-index', 10000);
29886 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
29887 this.maskEl.top.setLeft(0);
29888 this.maskEl.top.setTop(0);
29889 this.maskEl.top.show();
29891 this.maskEl.left.setStyle('position', 'absolute');
29892 this.maskEl.left.setStyle('z-index', 10000);
29893 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
29894 this.maskEl.left.setLeft(0);
29895 this.maskEl.left.setTop(box.y - this.padding);
29896 this.maskEl.left.show();
29898 this.maskEl.bottom.setStyle('position', 'absolute');
29899 this.maskEl.bottom.setStyle('z-index', 10000);
29900 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
29901 this.maskEl.bottom.setLeft(0);
29902 this.maskEl.bottom.setTop(box.bottom + this.padding);
29903 this.maskEl.bottom.show();
29905 this.maskEl.right.setStyle('position', 'absolute');
29906 this.maskEl.right.setStyle('z-index', 10000);
29907 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
29908 this.maskEl.right.setLeft(box.right + this.padding);
29909 this.maskEl.right.setTop(box.y - this.padding);
29910 this.maskEl.right.show();
29912 this.intervalID = window.setInterval(function() {
29913 Roo.form.BasicForm.popover.unmask();
29916 window.onwheel = function(){ return false;};
29918 (function(){ this.isMasked = true; }).defer(500, this);
29922 unmask : function()
29924 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
29928 this.maskEl.top.setStyle('position', 'absolute');
29929 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
29930 this.maskEl.top.hide();
29932 this.maskEl.left.setStyle('position', 'absolute');
29933 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
29934 this.maskEl.left.hide();
29936 this.maskEl.bottom.setStyle('position', 'absolute');
29937 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
29938 this.maskEl.bottom.hide();
29940 this.maskEl.right.setStyle('position', 'absolute');
29941 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
29942 this.maskEl.right.hide();
29944 window.onwheel = function(){ return true;};
29946 if(this.intervalID){
29947 window.clearInterval(this.intervalID);
29948 this.intervalID = false;
29951 this.isMasked = false;
29959 * Ext JS Library 1.1.1
29960 * Copyright(c) 2006-2007, Ext JS, LLC.
29962 * Originally Released Under LGPL - original licence link has changed is not relivant.
29965 * <script type="text/javascript">
29969 * @class Roo.form.Form
29970 * @extends Roo.form.BasicForm
29971 * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
29972 * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
29974 * @param {Object} config Configuration options
29976 Roo.form.Form = function(config){
29978 if (config.items) {
29979 xitems = config.items;
29980 delete config.items;
29984 Roo.form.Form.superclass.constructor.call(this, null, config);
29985 this.url = this.url || this.action;
29987 this.root = new Roo.form.Layout(Roo.applyIf({
29991 this.active = this.root;
29993 * Array of all the buttons that have been added to this form via {@link addButton}
29997 this.allItems = [];
30000 * @event clientvalidation
30001 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
30002 * @param {Form} this
30003 * @param {Boolean} valid true if the form has passed client-side validation
30005 clientvalidation: true,
30008 * Fires when the form is rendered
30009 * @param {Roo.form.Form} form
30014 if (this.progressUrl) {
30015 // push a hidden field onto the list of fields..
30019 name : 'UPLOAD_IDENTIFIER'
30024 Roo.each(xitems, this.addxtype, this);
30028 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
30030 * @cfg {Roo.Button} buttons[] buttons at bottom of form
30034 * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
30037 * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
30040 * @cfg {String} (left|center|right) buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
30042 buttonAlign:'center',
30045 * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
30050 * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
30051 * This property cascades to child containers if not set.
30056 * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
30057 * fires a looping event with that state. This is required to bind buttons to the valid
30058 * state using the config value formBind:true on the button.
30060 monitorValid : false,
30063 * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
30068 * @cfg {String} progressUrl - Url to return progress data
30071 progressUrl : false,
30073 * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
30074 * sending a formdata with extra parameters - eg uploaded elements.
30080 * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
30081 * fields are added and the column is closed. If no fields are passed the column remains open
30082 * until end() is called.
30083 * @param {Object} config The config to pass to the column
30084 * @param {Field} field1 (optional)
30085 * @param {Field} field2 (optional)
30086 * @param {Field} etc (optional)
30087 * @return Column The column container object
30089 column : function(c){
30090 var col = new Roo.form.Column(c);
30092 if(arguments.length > 1){ // duplicate code required because of Opera
30093 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
30100 * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
30101 * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
30102 * until end() is called.
30103 * @param {Object} config The config to pass to the fieldset
30104 * @param {Field} field1 (optional)
30105 * @param {Field} field2 (optional)
30106 * @param {Field} etc (optional)
30107 * @return FieldSet The fieldset container object
30109 fieldset : function(c){
30110 var fs = new Roo.form.FieldSet(c);
30112 if(arguments.length > 1){ // duplicate code required because of Opera
30113 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
30120 * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
30121 * fields are added and the container is closed. If no fields are passed the container remains open
30122 * until end() is called.
30123 * @param {Object} config The config to pass to the Layout
30124 * @param {Field} field1 (optional)
30125 * @param {Field} field2 (optional)
30126 * @param {Field} etc (optional)
30127 * @return Layout The container object
30129 container : function(c){
30130 var l = new Roo.form.Layout(c);
30132 if(arguments.length > 1){ // duplicate code required because of Opera
30133 this.add.apply(this, Array.prototype.slice.call(arguments, 1));
30140 * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
30141 * @param {Object} container A Roo.form.Layout or subclass of Layout
30142 * @return {Form} this
30144 start : function(c){
30145 // cascade label info
30146 Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
30147 this.active.stack.push(c);
30148 c.ownerCt = this.active;
30154 * Closes the current open container
30155 * @return {Form} this
30158 if(this.active == this.root){
30161 this.active = this.active.ownerCt;
30166 * Add Roo.form components to the current open container (e.g. column, fieldset, etc.). Fields added via this method
30167 * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
30168 * as the label of the field.
30169 * @param {Field} field1
30170 * @param {Field} field2 (optional)
30171 * @param {Field} etc. (optional)
30172 * @return {Form} this
30175 this.active.stack.push.apply(this.active.stack, arguments);
30176 this.allItems.push.apply(this.allItems,arguments);
30178 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
30179 if(a[i].isFormField){
30184 Roo.form.Form.superclass.add.apply(this, r);
30194 * Find any element that has been added to a form, using it's ID or name
30195 * This can include framesets, columns etc. along with regular fields..
30196 * @param {String} id - id or name to find.
30198 * @return {Element} e - or false if nothing found.
30200 findbyId : function(id)
30206 Roo.each(this.allItems, function(f){
30207 if (f.id == id || f.name == id ){
30218 * Render this form into the passed container. This should only be called once!
30219 * @param {String/HTMLElement/Element} container The element this component should be rendered into
30220 * @return {Form} this
30222 render : function(ct)
30228 var o = this.autoCreate || {
30230 method : this.method || 'POST',
30231 id : this.id || Roo.id()
30233 this.initEl(ct.createChild(o));
30235 this.root.render(this.el);
30239 this.items.each(function(f){
30240 f.render('x-form-el-'+f.id);
30243 if(this.buttons.length > 0){
30244 // tables are required to maintain order and for correct IE layout
30245 var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
30246 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
30247 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30249 var tr = tb.getElementsByTagName('tr')[0];
30250 for(var i = 0, len = this.buttons.length; i < len; i++) {
30251 var b = this.buttons[i];
30252 var td = document.createElement('td');
30253 td.className = 'x-form-btn-td';
30254 b.render(tr.appendChild(td));
30257 if(this.monitorValid){ // initialize after render
30258 this.startMonitoring();
30260 this.fireEvent('rendered', this);
30265 * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
30266 * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30267 * object or a valid Roo.DomHelper element config
30268 * @param {Function} handler The function called when the button is clicked
30269 * @param {Object} scope (optional) The scope of the handler function
30270 * @return {Roo.Button}
30272 addButton : function(config, handler, scope){
30276 minWidth: this.minButtonWidth,
30279 if(typeof config == "string"){
30282 Roo.apply(bc, config);
30284 var btn = new Roo.Button(null, bc);
30285 this.buttons.push(btn);
30290 * Adds a series of form elements (using the xtype property as the factory method.
30291 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
30292 * @param {Object} config
30295 addxtype : function()
30297 var ar = Array.prototype.slice.call(arguments, 0);
30299 for(var i = 0; i < ar.length; i++) {
30301 continue; // skip -- if this happends something invalid got sent, we
30302 // should ignore it, as basically that interface element will not show up
30303 // and that should be pretty obvious!!
30306 if (Roo.form[ar[i].xtype]) {
30308 var fe = Roo.factory(ar[i], Roo.form);
30314 fe.store.form = this;
30319 this.allItems.push(fe);
30320 if (fe.items && fe.addxtype) {
30321 fe.addxtype.apply(fe, fe.items);
30331 // console.log('adding ' + ar[i].xtype);
30333 if (ar[i].xtype == 'Button') {
30334 //console.log('adding button');
30335 //console.log(ar[i]);
30336 this.addButton(ar[i]);
30337 this.allItems.push(fe);
30341 if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
30342 alert('end is not supported on xtype any more, use items');
30344 // //console.log('adding end');
30352 * Starts monitoring of the valid state of this form. Usually this is done by passing the config
30353 * option "monitorValid"
30355 startMonitoring : function(){
30358 Roo.TaskMgr.start({
30359 run : this.bindHandler,
30360 interval : this.monitorPoll || 200,
30367 * Stops monitoring of the valid state of this form
30369 stopMonitoring : function(){
30370 this.bound = false;
30374 bindHandler : function(){
30376 return false; // stops binding
30379 this.items.each(function(f){
30380 if(!f.isValid(true)){
30385 for(var i = 0, len = this.buttons.length; i < len; i++){
30386 var btn = this.buttons[i];
30387 if(btn.formBind === true && btn.disabled === valid){
30388 btn.setDisabled(!valid);
30391 this.fireEvent('clientvalidation', this, valid);
30405 Roo.Form = Roo.form.Form;
30408 * Ext JS Library 1.1.1
30409 * Copyright(c) 2006-2007, Ext JS, LLC.
30411 * Originally Released Under LGPL - original licence link has changed is not relivant.
30414 * <script type="text/javascript">
30417 // as we use this in bootstrap.
30418 Roo.namespace('Roo.form');
30420 * @class Roo.form.Action
30421 * Internal Class used to handle form actions
30423 * @param {Roo.form.BasicForm} el The form element or its id
30424 * @param {Object} config Configuration options
30429 // define the action interface
30430 Roo.form.Action = function(form, options){
30432 this.options = options || {};
30435 * Client Validation Failed
30438 Roo.form.Action.CLIENT_INVALID = 'client';
30440 * Server Validation Failed
30443 Roo.form.Action.SERVER_INVALID = 'server';
30445 * Connect to Server Failed
30448 Roo.form.Action.CONNECT_FAILURE = 'connect';
30450 * Reading Data from Server Failed
30453 Roo.form.Action.LOAD_FAILURE = 'load';
30455 Roo.form.Action.prototype = {
30457 failureType : undefined,
30458 response : undefined,
30459 result : undefined,
30461 // interface method
30462 run : function(options){
30466 // interface method
30467 success : function(response){
30471 // interface method
30472 handleResponse : function(response){
30476 // default connection failure
30477 failure : function(response){
30479 this.response = response;
30480 this.failureType = Roo.form.Action.CONNECT_FAILURE;
30481 this.form.afterAction(this, false);
30484 processResponse : function(response){
30485 this.response = response;
30486 if(!response.responseText){
30489 this.result = this.handleResponse(response);
30490 return this.result;
30493 // utility functions used internally
30494 getUrl : function(appendParams){
30495 var url = this.options.url || this.form.url || this.form.el.dom.action;
30497 var p = this.getParams();
30499 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
30505 getMethod : function(){
30506 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
30509 getParams : function(){
30510 var bp = this.form.baseParams;
30511 var p = this.options.params;
30513 if(typeof p == "object"){
30514 p = Roo.urlEncode(Roo.applyIf(p, bp));
30515 }else if(typeof p == 'string' && bp){
30516 p += '&' + Roo.urlEncode(bp);
30519 p = Roo.urlEncode(bp);
30524 createCallback : function(){
30526 success: this.success,
30527 failure: this.failure,
30529 timeout: (this.form.timeout*1000),
30530 upload: this.form.fileUpload ? this.success : undefined
30535 Roo.form.Action.Submit = function(form, options){
30536 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
30539 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
30542 haveProgress : false,
30543 uploadComplete : false,
30545 // uploadProgress indicator.
30546 uploadProgress : function()
30548 if (!this.form.progressUrl) {
30552 if (!this.haveProgress) {
30553 Roo.MessageBox.progress("Uploading", "Uploading");
30555 if (this.uploadComplete) {
30556 Roo.MessageBox.hide();
30560 this.haveProgress = true;
30562 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
30564 var c = new Roo.data.Connection();
30566 url : this.form.progressUrl,
30571 success : function(req){
30572 //console.log(data);
30576 rdata = Roo.decode(req.responseText)
30578 Roo.log("Invalid data from server..");
30582 if (!rdata || !rdata.success) {
30584 Roo.MessageBox.alert(Roo.encode(rdata));
30587 var data = rdata.data;
30589 if (this.uploadComplete) {
30590 Roo.MessageBox.hide();
30595 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
30596 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
30599 this.uploadProgress.defer(2000,this);
30602 failure: function(data) {
30603 Roo.log('progress url failed ');
30614 // run get Values on the form, so it syncs any secondary forms.
30615 this.form.getValues();
30617 var o = this.options;
30618 var method = this.getMethod();
30619 var isPost = method == 'POST';
30620 if(o.clientValidation === false || this.form.isValid()){
30622 if (this.form.progressUrl) {
30623 this.form.findField('UPLOAD_IDENTIFIER').setValue(
30624 (new Date() * 1) + '' + Math.random());
30629 Roo.Ajax.request(Roo.apply(this.createCallback(), {
30630 form:this.form.el.dom,
30631 url:this.getUrl(!isPost),
30633 params:isPost ? this.getParams() : null,
30634 isUpload: this.form.fileUpload,
30635 formData : this.form.formData
30638 this.uploadProgress();
30640 }else if (o.clientValidation !== false){ // client validation failed
30641 this.failureType = Roo.form.Action.CLIENT_INVALID;
30642 this.form.afterAction(this, false);
30646 success : function(response)
30648 this.uploadComplete= true;
30649 if (this.haveProgress) {
30650 Roo.MessageBox.hide();
30654 var result = this.processResponse(response);
30655 if(result === true || result.success){
30656 this.form.afterAction(this, true);
30660 this.form.markInvalid(result.errors);
30661 this.failureType = Roo.form.Action.SERVER_INVALID;
30663 this.form.afterAction(this, false);
30665 failure : function(response)
30667 this.uploadComplete= true;
30668 if (this.haveProgress) {
30669 Roo.MessageBox.hide();
30672 this.response = response;
30673 this.failureType = Roo.form.Action.CONNECT_FAILURE;
30674 this.form.afterAction(this, false);
30677 handleResponse : function(response){
30678 if(this.form.errorReader){
30679 var rs = this.form.errorReader.read(response);
30682 for(var i = 0, len = rs.records.length; i < len; i++) {
30683 var r = rs.records[i];
30684 errors[i] = r.data;
30687 if(errors.length < 1){
30691 success : rs.success,
30697 ret = Roo.decode(response.responseText);
30701 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
30711 Roo.form.Action.Load = function(form, options){
30712 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
30713 this.reader = this.form.reader;
30716 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
30721 Roo.Ajax.request(Roo.apply(
30722 this.createCallback(), {
30723 method:this.getMethod(),
30724 url:this.getUrl(false),
30725 params:this.getParams()
30729 success : function(response){
30731 var result = this.processResponse(response);
30732 if(result === true || !result.success || !result.data){
30733 this.failureType = Roo.form.Action.LOAD_FAILURE;
30734 this.form.afterAction(this, false);
30737 this.form.clearInvalid();
30738 this.form.setValues(result.data);
30739 this.form.afterAction(this, true);
30742 handleResponse : function(response){
30743 if(this.form.reader){
30744 var rs = this.form.reader.read(response);
30745 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
30747 success : rs.success,
30751 return Roo.decode(response.responseText);
30755 Roo.form.Action.ACTION_TYPES = {
30756 'load' : Roo.form.Action.Load,
30757 'submit' : Roo.form.Action.Submit
30760 * Ext JS Library 1.1.1
30761 * Copyright(c) 2006-2007, Ext JS, LLC.
30763 * Originally Released Under LGPL - original licence link has changed is not relivant.
30766 * <script type="text/javascript">
30770 * @class Roo.form.Layout
30771 * @extends Roo.Component
30772 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30773 * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
30775 * @param {Object} config Configuration options
30777 Roo.form.Layout = function(config){
30779 if (config.items) {
30780 xitems = config.items;
30781 delete config.items;
30783 Roo.form.Layout.superclass.constructor.call(this, config);
30785 Roo.each(xitems, this.addxtype, this);
30789 Roo.extend(Roo.form.Layout, Roo.Component, {
30791 * @cfg {String/Object} autoCreate
30792 * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
30795 * @cfg {String/Object/Function} style
30796 * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
30797 * a function which returns such a specification.
30800 * @cfg {String} labelAlign
30801 * Valid values are "left," "top" and "right" (defaults to "left")
30804 * @cfg {Number} labelWidth
30805 * Fixed width in pixels of all field labels (defaults to undefined)
30808 * @cfg {Boolean} clear
30809 * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
30813 * @cfg {String} labelSeparator
30814 * The separator to use after field labels (defaults to ':')
30816 labelSeparator : ':',
30818 * @cfg {Boolean} hideLabels
30819 * True to suppress the display of field labels in this layout (defaults to false)
30821 hideLabels : false,
30824 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
30829 onRender : function(ct, position){
30830 if(this.el){ // from markup
30831 this.el = Roo.get(this.el);
30832 }else { // generate
30833 var cfg = this.getAutoCreate();
30834 this.el = ct.createChild(cfg, position);
30837 this.el.applyStyles(this.style);
30839 if(this.labelAlign){
30840 this.el.addClass('x-form-label-'+this.labelAlign);
30842 if(this.hideLabels){
30843 this.labelStyle = "display:none";
30844 this.elementStyle = "padding-left:0;";
30846 if(typeof this.labelWidth == 'number'){
30847 this.labelStyle = "width:"+this.labelWidth+"px;";
30848 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
30850 if(this.labelAlign == 'top'){
30851 this.labelStyle = "width:auto;";
30852 this.elementStyle = "padding-left:0;";
30855 var stack = this.stack;
30856 var slen = stack.length;
30858 if(!this.fieldTpl){
30859 var t = new Roo.Template(
30860 '<div class="x-form-item {5}">',
30861 '<label for="{0}" style="{2}">{1}{4}</label>',
30862 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30864 '</div><div class="x-form-clear-left"></div>'
30866 t.disableFormats = true;
30868 Roo.form.Layout.prototype.fieldTpl = t;
30870 for(var i = 0; i < slen; i++) {
30871 if(stack[i].isFormField){
30872 this.renderField(stack[i]);
30874 this.renderComponent(stack[i]);
30879 this.el.createChild({cls:'x-form-clear'});
30884 renderField : function(f){
30885 f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
30888 f.labelStyle||this.labelStyle||'', //2
30889 this.elementStyle||'', //3
30890 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
30891 f.itemCls||this.itemCls||'' //5
30892 ], true).getPrevSibling());
30896 renderComponent : function(c){
30897 c.render(c.isLayout ? this.el : this.el.createChild());
30900 * Adds a object form elements (using the xtype property as the factory method.)
30901 * Valid xtypes are: TextField, TextArea .... Button, Layout, FieldSet, Column
30902 * @param {Object} config
30904 addxtype : function(o)
30906 // create the lement.
30907 o.form = this.form;
30908 var fe = Roo.factory(o, Roo.form);
30909 this.form.allItems.push(fe);
30910 this.stack.push(fe);
30912 if (fe.isFormField) {
30913 this.form.items.add(fe);
30921 * @class Roo.form.Column
30922 * @extends Roo.form.Layout
30923 * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30924 * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
30926 * @param {Object} config Configuration options
30928 Roo.form.Column = function(config){
30929 Roo.form.Column.superclass.constructor.call(this, config);
30932 Roo.extend(Roo.form.Column, Roo.form.Layout, {
30934 * @cfg {Number/String} width
30935 * The fixed width of the column in pixels or CSS value (defaults to "auto")
30938 * @cfg {String/Object} autoCreate
30939 * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
30943 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
30946 onRender : function(ct, position){
30947 Roo.form.Column.superclass.onRender.call(this, ct, position);
30949 this.el.setWidth(this.width);
30956 * @class Roo.form.Row
30957 * @extends Roo.form.Layout
30958 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
30959 * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
30961 * @param {Object} config Configuration options
30965 Roo.form.Row = function(config){
30966 Roo.form.Row.superclass.constructor.call(this, config);
30969 Roo.extend(Roo.form.Row, Roo.form.Layout, {
30971 * @cfg {Number/String} width
30972 * The fixed width of the column in pixels or CSS value (defaults to "auto")
30975 * @cfg {Number/String} height
30976 * The fixed height of the column in pixels or CSS value (defaults to "auto")
30978 defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
30982 onRender : function(ct, position){
30983 //console.log('row render');
30985 var t = new Roo.Template(
30986 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
30987 '<label for="{0}" style="{2}">{1}{4}</label>',
30988 '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
30992 t.disableFormats = true;
30994 Roo.form.Layout.prototype.rowTpl = t;
30996 this.fieldTpl = this.rowTpl;
30998 //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
30999 var labelWidth = 100;
31001 if ((this.labelAlign != 'top')) {
31002 if (typeof this.labelWidth == 'number') {
31003 labelWidth = this.labelWidth
31005 this.padWidth = 20 + labelWidth;
31009 Roo.form.Column.superclass.onRender.call(this, ct, position);
31011 this.el.setWidth(this.width);
31014 this.el.setHeight(this.height);
31019 renderField : function(f){
31020 f.fieldEl = this.fieldTpl.append(this.el, [
31021 f.id, f.fieldLabel,
31022 f.labelStyle||this.labelStyle||'',
31023 this.elementStyle||'',
31024 typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
31025 f.itemCls||this.itemCls||'',
31026 f.width ? f.width + this.padWidth : 160 + this.padWidth
31033 * @class Roo.form.FieldSet
31034 * @extends Roo.form.Layout
31035 * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
31036 * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
31038 * @param {Object} config Configuration options
31040 Roo.form.FieldSet = function(config){
31041 Roo.form.FieldSet.superclass.constructor.call(this, config);
31044 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
31046 * @cfg {String} legend
31047 * The text to display as the legend for the FieldSet (defaults to '')
31050 * @cfg {String/Object} autoCreate
31051 * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
31055 defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
31058 onRender : function(ct, position){
31059 Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
31061 this.setLegend(this.legend);
31066 setLegend : function(text){
31068 this.el.child('legend').update(text);
31073 * Ext JS Library 1.1.1
31074 * Copyright(c) 2006-2007, Ext JS, LLC.
31076 * Originally Released Under LGPL - original licence link has changed is not relivant.
31079 * <script type="text/javascript">
31082 * @class Roo.form.VTypes
31083 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
31086 Roo.form.VTypes = function(){
31087 // closure these in so they are only created once.
31088 var alpha = /^[a-zA-Z_]+$/;
31089 var alphanum = /^[a-zA-Z0-9_]+$/;
31090 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
31091 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
31093 // All these messages and functions are configurable
31096 * The function used to validate email addresses
31097 * @param {String} value The email address
31099 'email' : function(v){
31100 return email.test(v);
31103 * The error text to display when the email validation function returns false
31106 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
31108 * The keystroke filter mask to be applied on email input
31111 'emailMask' : /[a-z0-9_\.\-@]/i,
31114 * The function used to validate URLs
31115 * @param {String} value The URL
31117 'url' : function(v){
31118 return url.test(v);
31121 * The error text to display when the url validation function returns false
31124 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
31127 * The function used to validate alpha values
31128 * @param {String} value The value
31130 'alpha' : function(v){
31131 return alpha.test(v);
31134 * The error text to display when the alpha validation function returns false
31137 'alphaText' : 'This field should only contain letters and _',
31139 * The keystroke filter mask to be applied on alpha input
31142 'alphaMask' : /[a-z_]/i,
31145 * The function used to validate alphanumeric values
31146 * @param {String} value The value
31148 'alphanum' : function(v){
31149 return alphanum.test(v);
31152 * The error text to display when the alphanumeric validation function returns false
31155 'alphanumText' : 'This field should only contain letters, numbers and _',
31157 * The keystroke filter mask to be applied on alphanumeric input
31160 'alphanumMask' : /[a-z0-9_]/i
31162 }();//<script type="text/javascript">
31165 * @class Roo.form.FCKeditor
31166 * @extends Roo.form.TextArea
31167 * Wrapper around the FCKEditor http://www.fckeditor.net
31169 * Creates a new FCKeditor
31170 * @param {Object} config Configuration options
31172 Roo.form.FCKeditor = function(config){
31173 Roo.form.FCKeditor.superclass.constructor.call(this, config);
31176 * @event editorinit
31177 * Fired when the editor is initialized - you can add extra handlers here..
31178 * @param {FCKeditor} this
31179 * @param {Object} the FCK object.
31186 Roo.form.FCKeditor.editors = { };
31187 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
31189 //defaultAutoCreate : {
31190 // tag : "textarea",style : "width:100px;height:60px;" ,autocomplete : "off"
31194 * @cfg {Object} fck options - see fck manual for details.
31199 * @cfg {Object} fck toolbar set (Basic or Default)
31201 toolbarSet : 'Basic',
31203 * @cfg {Object} fck BasePath
31205 basePath : '/fckeditor/',
31213 onRender : function(ct, position)
31216 this.defaultAutoCreate = {
31218 style:"width:300px;height:60px;",
31219 autocomplete: "new-password"
31222 Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
31225 this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
31226 if(this.preventScrollbars){
31227 this.el.setStyle("overflow", "hidden");
31229 this.el.setHeight(this.growMin);
31232 //console.log('onrender' + this.getId() );
31233 Roo.form.FCKeditor.editors[this.getId()] = this;
31236 this.replaceTextarea() ;
31240 getEditor : function() {
31241 return this.fckEditor;
31244 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
31245 * @param {Mixed} value The value to set
31249 setValue : function(value)
31251 //console.log('setValue: ' + value);
31253 if(typeof(value) == 'undefined') { // not sure why this is happending...
31256 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31258 //if(!this.el || !this.getEditor()) {
31259 // this.value = value;
31260 //this.setValue.defer(100,this,[value]);
31264 if(!this.getEditor()) {
31268 this.getEditor().SetData(value);
31275 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
31276 * @return {Mixed} value The field value
31278 getValue : function()
31281 if (this.frame && this.frame.dom.style.display == 'none') {
31282 return Roo.form.FCKeditor.superclass.getValue.call(this);
31285 if(!this.el || !this.getEditor()) {
31287 // this.getValue.defer(100,this);
31292 var value=this.getEditor().GetData();
31293 Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
31294 return Roo.form.FCKeditor.superclass.getValue.call(this);
31300 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
31301 * @return {Mixed} value The field value
31303 getRawValue : function()
31305 if (this.frame && this.frame.dom.style.display == 'none') {
31306 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31309 if(!this.el || !this.getEditor()) {
31310 //this.getRawValue.defer(100,this);
31317 var value=this.getEditor().GetData();
31318 Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
31319 return Roo.form.FCKeditor.superclass.getRawValue.call(this);
31323 setSize : function(w,h) {
31327 //if (this.frame && this.frame.dom.style.display == 'none') {
31328 // Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31331 //if(!this.el || !this.getEditor()) {
31332 // this.setSize.defer(100,this, [w,h]);
31338 Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
31340 this.frame.dom.setAttribute('width', w);
31341 this.frame.dom.setAttribute('height', h);
31342 this.frame.setSize(w,h);
31346 toggleSourceEdit : function(value) {
31350 this.el.dom.style.display = value ? '' : 'none';
31351 this.frame.dom.style.display = value ? 'none' : '';
31356 focus: function(tag)
31358 if (this.frame.dom.style.display == 'none') {
31359 return Roo.form.FCKeditor.superclass.focus.call(this);
31361 if(!this.el || !this.getEditor()) {
31362 this.focus.defer(100,this, [tag]);
31369 var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
31370 this.getEditor().Focus();
31372 if (!this.getEditor().Selection.GetSelection()) {
31373 this.focus.defer(100,this, [tag]);
31378 var r = this.getEditor().EditorDocument.createRange();
31379 r.setStart(tgs[0],0);
31380 r.setEnd(tgs[0],0);
31381 this.getEditor().Selection.GetSelection().removeAllRanges();
31382 this.getEditor().Selection.GetSelection().addRange(r);
31383 this.getEditor().Focus();
31390 replaceTextarea : function()
31392 if ( document.getElementById( this.getId() + '___Frame' ) ) {
31395 //if ( !this.checkBrowser || this._isCompatibleBrowser() )
31397 // We must check the elements firstly using the Id and then the name.
31398 var oTextarea = document.getElementById( this.getId() );
31400 var colElementsByName = document.getElementsByName( this.getId() ) ;
31402 oTextarea.style.display = 'none' ;
31404 if ( oTextarea.tabIndex ) {
31405 this.TabIndex = oTextarea.tabIndex ;
31408 this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
31409 this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
31410 this.frame = Roo.get(this.getId() + '___Frame')
31413 _getConfigHtml : function()
31417 for ( var o in this.fckconfig ) {
31418 sConfig += sConfig.length > 0 ? '&' : '';
31419 sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
31422 return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
31426 _getIFrameHtml : function()
31428 var sFile = 'fckeditor.html' ;
31429 /* no idea what this is about..
31432 if ( (/fcksource=true/i).test( window.top.location.search ) )
31433 sFile = 'fckeditor.original.html' ;
31438 var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
31439 sLink += this.toolbarSet ? ( '&Toolbar=' + this.toolbarSet) : '';
31442 var html = '<iframe id="' + this.getId() +
31443 '___Frame" src="' + sLink +
31444 '" width="' + this.width +
31445 '" height="' + this.height + '"' +
31446 (this.tabIndex ? ' tabindex="' + this.tabIndex + '"' :'' ) +
31447 ' frameborder="0" scrolling="no"></iframe>' ;
31452 _insertHtmlBefore : function( html, element )
31454 if ( element.insertAdjacentHTML ) {
31456 element.insertAdjacentHTML( 'beforeBegin', html ) ;
31458 var oRange = document.createRange() ;
31459 oRange.setStartBefore( element ) ;
31460 var oFragment = oRange.createContextualFragment( html );
31461 element.parentNode.insertBefore( oFragment, element ) ;
31474 //Roo.reg('fckeditor', Roo.form.FCKeditor);
31476 function FCKeditor_OnComplete(editorInstance){
31477 var f = Roo.form.FCKeditor.editors[editorInstance.Name];
31478 f.fckEditor = editorInstance;
31479 //console.log("loaded");
31480 f.fireEvent('editorinit', f, editorInstance);
31500 //<script type="text/javascript">
31502 * @class Roo.form.GridField
31503 * @extends Roo.form.Field
31504 * Embed a grid (or editable grid into a form)
31507 * This embeds a grid in a form, the value of the field should be the json encoded array of rows
31509 * xgrid.store = Roo.data.Store
31510 * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
31511 * xgrid.store.reader = Roo.data.JsonReader
31515 * Creates a new GridField
31516 * @param {Object} config Configuration options
31518 Roo.form.GridField = function(config){
31519 Roo.form.GridField.superclass.constructor.call(this, config);
31523 Roo.extend(Roo.form.GridField, Roo.form.Field, {
31525 * @cfg {Number} width - used to restrict width of grid..
31529 * @cfg {Number} height - used to restrict height of grid..
31533 * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
31539 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31540 * {tag: "input", type: "checkbox", autocomplete: "off"})
31542 // defaultAutoCreate : { tag: 'div' },
31543 defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
31545 * @cfg {String} addTitle Text to include for adding a title.
31549 onResize : function(){
31550 Roo.form.Field.superclass.onResize.apply(this, arguments);
31553 initEvents : function(){
31554 // Roo.form.Checkbox.superclass.initEvents.call(this);
31555 // has no events...
31560 getResizeEl : function(){
31564 getPositionEl : function(){
31569 onRender : function(ct, position){
31571 this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
31572 var style = this.style;
31575 Roo.form.GridField.superclass.onRender.call(this, ct, position);
31576 this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
31577 this.viewEl = this.wrap.createChild({ tag: 'div' });
31579 this.viewEl.applyStyles(style);
31582 this.viewEl.setWidth(this.width);
31585 this.viewEl.setHeight(this.height);
31587 //if(this.inputValue !== undefined){
31588 //this.setValue(this.value);
31591 this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
31594 this.grid.render();
31595 this.grid.getDataSource().on('remove', this.refreshValue, this);
31596 this.grid.getDataSource().on('update', this.refreshValue, this);
31597 this.grid.on('afteredit', this.refreshValue, this);
31603 * Sets the value of the item.
31604 * @param {String} either an object or a string..
31606 setValue : function(v){
31608 v = v || []; // empty set..
31609 // this does not seem smart - it really only affects memoryproxy grids..
31610 if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
31611 var ds = this.grid.getDataSource();
31612 // assumes a json reader..
31614 data[ds.reader.meta.root ] = typeof(v) == 'string' ? Roo.decode(v) : v;
31615 ds.loadData( data);
31617 // clear selection so it does not get stale.
31618 if (this.grid.sm) {
31619 this.grid.sm.clearSelections();
31622 Roo.form.GridField.superclass.setValue.call(this, v);
31623 this.refreshValue();
31624 // should load data in the grid really....
31628 refreshValue: function() {
31630 this.grid.getDataSource().each(function(r) {
31633 this.el.dom.value = Roo.encode(val);
31641 * Ext JS Library 1.1.1
31642 * Copyright(c) 2006-2007, Ext JS, LLC.
31644 * Originally Released Under LGPL - original licence link has changed is not relivant.
31647 * <script type="text/javascript">
31650 * @class Roo.form.DisplayField
31651 * @extends Roo.form.Field
31652 * A generic Field to display non-editable data.
31653 * @cfg {Boolean} closable (true|false) default false
31655 * Creates a new Display Field item.
31656 * @param {Object} config Configuration options
31658 Roo.form.DisplayField = function(config){
31659 Roo.form.DisplayField.superclass.constructor.call(this, config);
31664 * Fires after the click the close btn
31665 * @param {Roo.form.DisplayField} this
31671 Roo.extend(Roo.form.DisplayField, Roo.form.TextField, {
31672 inputType: 'hidden',
31678 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31680 focusClass : undefined,
31682 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31684 fieldClass: 'x-form-field',
31687 * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
31689 valueRenderer: undefined,
31693 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31694 * {tag: "input", type: "checkbox", autocomplete: "off"})
31697 // defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
31701 onResize : function(){
31702 Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
31706 initEvents : function(){
31707 // Roo.form.Checkbox.superclass.initEvents.call(this);
31708 // has no events...
31711 this.closeEl.on('click', this.onClose, this);
31717 getResizeEl : function(){
31721 getPositionEl : function(){
31726 onRender : function(ct, position){
31728 Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
31729 //if(this.inputValue !== undefined){
31730 this.wrap = this.el.wrap();
31732 this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
31735 this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
31738 if (this.bodyStyle) {
31739 this.viewEl.applyStyles(this.bodyStyle);
31741 //this.viewEl.setStyle('padding', '2px');
31743 this.setValue(this.value);
31748 initValue : Roo.emptyFn,
31753 onClick : function(){
31758 * Sets the checked state of the checkbox.
31759 * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
31761 setValue : function(v){
31763 var html = this.valueRenderer ? this.valueRenderer(v) : String.format('{0}', v);
31764 // this might be called before we have a dom element..
31765 if (!this.viewEl) {
31768 this.viewEl.dom.innerHTML = html;
31769 Roo.form.DisplayField.superclass.setValue.call(this, v);
31773 onClose : function(e)
31775 e.preventDefault();
31777 this.fireEvent('close', this);
31786 * @class Roo.form.DayPicker
31787 * @extends Roo.form.Field
31788 * A Day picker show [M] [T] [W] ....
31790 * Creates a new Day Picker
31791 * @param {Object} config Configuration options
31793 Roo.form.DayPicker= function(config){
31794 Roo.form.DayPicker.superclass.constructor.call(this, config);
31798 Roo.extend(Roo.form.DayPicker, Roo.form.Field, {
31800 * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
31802 focusClass : undefined,
31804 * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
31806 fieldClass: "x-form-field",
31809 * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
31810 * {tag: "input", type: "checkbox", autocomplete: "off"})
31812 defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
31815 actionMode : 'viewEl',
31819 inputType : 'hidden',
31822 inputElement: false, // real input element?
31823 basedOn: false, // ????
31825 isFormField: true, // not sure where this is needed!!!!
31827 onResize : function(){
31828 Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
31829 if(!this.boxLabel){
31830 this.el.alignTo(this.wrap, 'c-c');
31834 initEvents : function(){
31835 Roo.form.Checkbox.superclass.initEvents.call(this);
31836 this.el.on("click", this.onClick, this);
31837 this.el.on("change", this.onClick, this);
31841 getResizeEl : function(){
31845 getPositionEl : function(){
31851 onRender : function(ct, position){
31852 Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
31854 this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
31856 var r1 = '<table><tr>';
31857 var r2 = '<tr class="x-form-daypick-icons">';
31858 for (var i=0; i < 7; i++) {
31859 r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
31860 r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL +'"></td>';
31863 var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
31864 viewEl.select('img').on('click', this.onClick, this);
31865 this.viewEl = viewEl;
31868 // this will not work on Chrome!!!
31869 this.el.on('DOMAttrModified', this.setFromHidden, this); //ff
31870 this.el.on('propertychange', this.setFromHidden, this); //ie
31878 initValue : Roo.emptyFn,
31881 * Returns the checked state of the checkbox.
31882 * @return {Boolean} True if checked, else false
31884 getValue : function(){
31885 return this.el.dom.value;
31890 onClick : function(e){
31891 //this.setChecked(!this.checked);
31892 Roo.get(e.target).toggleClass('x-menu-item-checked');
31893 this.refreshValue();
31894 //if(this.el.dom.checked != this.checked){
31895 // this.setValue(this.el.dom.checked);
31900 refreshValue : function()
31903 this.viewEl.select('img',true).each(function(e,i,n) {
31904 val += e.is(".x-menu-item-checked") ? String(n) : '';
31906 this.setValue(val, true);
31910 * Sets the checked state of the checkbox.
31911 * On is always based on a string comparison between inputValue and the param.
31912 * @param {Boolean/String} value - the value to set
31913 * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
31915 setValue : function(v,suppressEvent){
31916 if (!this.el.dom) {
31919 var old = this.el.dom.value ;
31920 this.el.dom.value = v;
31921 if (suppressEvent) {
31925 // update display..
31926 this.viewEl.select('img',true).each(function(e,i,n) {
31928 var on = e.is(".x-menu-item-checked");
31929 var newv = v.indexOf(String(n)) > -1;
31931 e.toggleClass('x-menu-item-checked');
31937 this.fireEvent('change', this, v, old);
31942 // handle setting of hidden value by some other method!!?!?
31943 setFromHidden: function()
31948 //console.log("SET FROM HIDDEN");
31949 //alert('setFrom hidden');
31950 this.setValue(this.el.dom.value);
31953 onDestroy : function()
31956 Roo.get(this.viewEl).remove();
31959 Roo.form.DayPicker.superclass.onDestroy.call(this);
31963 * RooJS Library 1.1.1
31964 * Copyright(c) 2008-2011 Alan Knowles
31971 * @class Roo.form.ComboCheck
31972 * @extends Roo.form.ComboBox
31973 * A combobox for multiple select items.
31975 * FIXME - could do with a reset button..
31978 * Create a new ComboCheck
31979 * @param {Object} config Configuration options
31981 Roo.form.ComboCheck = function(config){
31982 Roo.form.ComboCheck.superclass.constructor.call(this, config);
31983 // should verify some data...
31985 // hiddenName = required..
31986 // displayField = required
31987 // valudField == required
31988 var req= [ 'hiddenName', 'displayField', 'valueField' ];
31990 Roo.each(req, function(e) {
31991 if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
31992 throw "Roo.form.ComboCheck : missing value for: " + e;
31999 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
32004 selectedClass: 'x-menu-item-checked',
32007 onRender : function(ct, position){
32013 var cls = 'x-combo-list';
32016 this.tpl = new Roo.Template({
32017 html : '<div class="'+cls+'-item x-menu-check-item">' +
32018 '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' +
32019 '<span>{' + this.displayField + '}</span>' +
32026 Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
32027 this.view.singleSelect = false;
32028 this.view.multiSelect = true;
32029 this.view.toggleSelect = true;
32030 this.pageTb.add(new Roo.Toolbar.Fill(), {
32033 handler: function()
32040 onViewOver : function(e, t){
32046 onViewClick : function(doFocus,index){
32050 select: function () {
32051 //Roo.log("SELECT CALLED");
32054 selectByValue : function(xv, scrollIntoView){
32055 var ar = this.getValueArray();
32058 Roo.each(ar, function(v) {
32059 if(v === undefined || v === null){
32062 var r = this.findRecord(this.valueField, v);
32064 sels.push(this.store.indexOf(r))
32068 this.view.select(sels);
32074 onSelect : function(record, index){
32075 // Roo.log("onselect Called");
32076 // this is only called by the clear button now..
32077 this.view.clearSelections();
32078 this.setValue('[]');
32079 if (this.value != this.valueBefore) {
32080 this.fireEvent('change', this, this.value, this.valueBefore);
32081 this.valueBefore = this.value;
32084 getValueArray : function()
32089 //Roo.log(this.value);
32090 if (typeof(this.value) == 'undefined') {
32093 var ar = Roo.decode(this.value);
32094 return ar instanceof Array ? ar : []; //?? valid?
32097 Roo.log(e + "\nRoo.form.ComboCheck:getValueArray invalid data:" + this.getValue());
32102 expand : function ()
32105 Roo.form.ComboCheck.superclass.expand.call(this);
32106 this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
32107 //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
32112 collapse : function(){
32113 Roo.form.ComboCheck.superclass.collapse.call(this);
32114 var sl = this.view.getSelectedIndexes();
32115 var st = this.store;
32119 Roo.each(sl, function(i) {
32121 nv.push(r.get(this.valueField));
32123 this.setValue(Roo.encode(nv));
32124 if (this.value != this.valueBefore) {
32126 this.fireEvent('change', this, this.value, this.valueBefore);
32127 this.valueBefore = this.value;
32132 setValue : function(v){
32136 var vals = this.getValueArray();
32138 Roo.each(vals, function(k) {
32139 var r = this.findRecord(this.valueField, k);
32141 tv.push(r.data[this.displayField]);
32142 }else if(this.valueNotFoundText !== undefined){
32143 tv.push( this.valueNotFoundText );
32148 Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
32149 this.hiddenField.value = v;
32155 * Ext JS Library 1.1.1
32156 * Copyright(c) 2006-2007, Ext JS, LLC.
32158 * Originally Released Under LGPL - original licence link has changed is not relivant.
32161 * <script type="text/javascript">
32165 * @class Roo.form.Signature
32166 * @extends Roo.form.Field
32170 * @param {Object} config Configuration options
32173 Roo.form.Signature = function(config){
32174 Roo.form.Signature.superclass.constructor.call(this, config);
32176 this.addEvents({// not in used??
32179 * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
32180 * @param {Roo.form.Signature} combo This combo box
32185 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
32186 * @param {Roo.form.ComboBox} combo This combo box
32187 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
32193 Roo.extend(Roo.form.Signature, Roo.form.Field, {
32195 * @cfg {Object} labels Label to use when rendering a form.
32199 * confirm : "Confirm"
32204 confirm : "Confirm"
32207 * @cfg {Number} width The signature panel width (defaults to 300)
32211 * @cfg {Number} height The signature panel height (defaults to 100)
32215 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
32217 allowBlank : false,
32220 // {Object} signPanel The signature SVG panel element (defaults to {})
32222 // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
32223 isMouseDown : false,
32224 // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
32225 isConfirmed : false,
32226 // {String} signatureTmp SVG mapping string (defaults to empty string)
32230 defaultAutoCreate : { // modified by initCompnoent..
32236 onRender : function(ct, position){
32238 Roo.form.Signature.superclass.onRender.call(this, ct, position);
32240 this.wrap = this.el.wrap({
32241 cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
32244 this.createToolbar(this);
32245 this.signPanel = this.wrap.createChild({
32247 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
32251 this.svgID = Roo.id();
32252 this.svgEl = this.signPanel.createChild({
32253 xmlns : 'http://www.w3.org/2000/svg',
32255 id : this.svgID + "-svg",
32257 height: this.height,
32258 viewBox: '0 0 '+this.width+' '+this.height,
32262 id: this.svgID + "-svg-r",
32264 height: this.height,
32269 id: this.svgID + "-svg-l",
32271 y1: (this.height*0.8), // start set the line in 80% of height
32272 x2: this.width, // end
32273 y2: (this.height*0.8), // end set the line in 80% of height
32275 'stroke-width': "1",
32276 'stroke-dasharray': "3",
32277 'shape-rendering': "crispEdges",
32278 'pointer-events': "none"
32282 id: this.svgID + "-svg-p",
32284 'stroke-width': "3",
32286 'pointer-events': 'none'
32291 this.svgBox = this.svgEl.dom.getScreenCTM();
32293 createSVG : function(){
32294 var svg = this.signPanel;
32295 var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
32298 r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
32299 r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
32300 r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
32301 r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
32302 r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
32303 r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
32304 r.addEventListener('touchend', function(e) { return t.up(e); }, false);
32307 isTouchEvent : function(e){
32308 return e.type.match(/^touch/);
32310 getCoords : function (e) {
32311 var pt = this.svgEl.dom.createSVGPoint();
32314 if (this.isTouchEvent(e)) {
32315 pt.x = e.targetTouches[0].clientX;
32316 pt.y = e.targetTouches[0].clientY;
32318 var a = this.svgEl.dom.getScreenCTM();
32319 var b = a.inverse();
32320 var mx = pt.matrixTransform(b);
32321 return mx.x + ',' + mx.y;
32323 //mouse event headler
32324 down : function (e) {
32325 this.signatureTmp += 'M' + this.getCoords(e) + ' ';
32326 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
32328 this.isMouseDown = true;
32330 e.preventDefault();
32332 move : function (e) {
32333 if (this.isMouseDown) {
32334 this.signatureTmp += 'L' + this.getCoords(e) + ' ';
32335 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
32338 e.preventDefault();
32340 up : function (e) {
32341 this.isMouseDown = false;
32342 var sp = this.signatureTmp.split(' ');
32345 if(!sp[sp.length-2].match(/^L/)){
32349 this.signatureTmp = sp.join(" ");
32352 if(this.getValue() != this.signatureTmp){
32353 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32354 this.isConfirmed = false;
32356 e.preventDefault();
32360 * Protected method that will not generally be called directly. It
32361 * is called when the editor creates its toolbar. Override this method if you need to
32362 * add custom toolbar buttons.
32363 * @param {HtmlEditor} editor
32365 createToolbar : function(editor){
32366 function btn(id, toggle, handler){
32367 var xid = fid + '-'+ id ;
32371 cls : 'x-btn-icon x-edit-'+id,
32372 enableToggle:toggle !== false,
32373 scope: editor, // was editor...
32374 handler:handler||editor.relayBtnCmd,
32375 clickEvent:'mousedown',
32376 tooltip: etb.buttonTips[id] || undefined, ///tips ???
32382 var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
32386 cls : ' x-signature-btn x-signature-'+id,
32387 scope: editor, // was editor...
32388 handler: this.reset,
32389 clickEvent:'mousedown',
32390 text: this.labels.clear
32397 cls : ' x-signature-btn x-signature-'+id,
32398 scope: editor, // was editor...
32399 handler: this.confirmHandler,
32400 clickEvent:'mousedown',
32401 text: this.labels.confirm
32408 * when user is clicked confirm then show this image.....
32410 * @return {String} Image Data URI
32412 getImageDataURI : function(){
32413 var svg = this.svgEl.dom.parentNode.innerHTML;
32414 var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
32419 * @return {Boolean} this.isConfirmed
32421 getConfirmed : function(){
32422 return this.isConfirmed;
32426 * @return {Number} this.width
32428 getWidth : function(){
32433 * @return {Number} this.height
32435 getHeight : function(){
32436 return this.height;
32439 getSignature : function(){
32440 return this.signatureTmp;
32443 reset : function(){
32444 this.signatureTmp = '';
32445 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32446 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
32447 this.isConfirmed = false;
32448 Roo.form.Signature.superclass.reset.call(this);
32450 setSignature : function(s){
32451 this.signatureTmp = s;
32452 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
32453 this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
32455 this.isConfirmed = false;
32456 Roo.form.Signature.superclass.reset.call(this);
32459 // Roo.log(this.signPanel.dom.contentWindow.up())
32462 setConfirmed : function(){
32466 // Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
32469 confirmHandler : function(){
32470 if(!this.getSignature()){
32474 this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
32475 this.setValue(this.getSignature());
32476 this.isConfirmed = true;
32478 this.fireEvent('confirm', this);
32481 // Subclasses should provide the validation implementation by overriding this
32482 validateValue : function(value){
32483 if(this.allowBlank){
32487 if(this.isConfirmed){
32494 * Ext JS Library 1.1.1
32495 * Copyright(c) 2006-2007, Ext JS, LLC.
32497 * Originally Released Under LGPL - original licence link has changed is not relivant.
32500 * <script type="text/javascript">
32505 * @class Roo.form.ComboBox
32506 * @extends Roo.form.TriggerField
32507 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
32509 * Create a new ComboBox.
32510 * @param {Object} config Configuration options
32512 Roo.form.Select = function(config){
32513 Roo.form.Select.superclass.constructor.call(this, config);
32517 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
32519 * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
32522 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
32523 * rendering into an Roo.Editor, defaults to false)
32526 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
32527 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
32530 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
32533 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
32534 * the dropdown list (defaults to undefined, with no header element)
32538 * @cfg {String/Roo.Template} tpl The template to use to render the output
32542 defaultAutoCreate : {tag: "select" },
32544 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
32546 listWidth: undefined,
32548 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
32549 * mode = 'remote' or 'text' if mode = 'local')
32551 displayField: undefined,
32553 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
32554 * mode = 'remote' or 'value' if mode = 'local').
32555 * Note: use of a valueField requires the user make a selection
32556 * in order for a value to be mapped.
32558 valueField: undefined,
32562 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
32563 * field's data value (defaults to the underlying DOM element's name)
32565 hiddenName: undefined,
32567 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
32571 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
32573 selectedClass: 'x-combo-selected',
32575 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
32576 * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
32577 * which displays a downward arrow icon).
32579 triggerClass : 'x-form-arrow-trigger',
32581 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32585 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
32586 * anchor positions (defaults to 'tl-bl')
32588 listAlign: 'tl-bl?',
32590 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
32594 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
32595 * query specified by the allQuery config option (defaults to 'query')
32597 triggerAction: 'query',
32599 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
32600 * (defaults to 4, does not apply if editable = false)
32604 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
32605 * delay (typeAheadDelay) if it matches a known value (defaults to false)
32609 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
32610 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
32614 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
32615 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
32619 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
32620 * when editable = true (defaults to false)
32622 selectOnFocus:false,
32624 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
32626 queryParam: 'query',
32628 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
32629 * when mode = 'remote' (defaults to 'Loading...')
32631 loadingText: 'Loading...',
32633 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
32637 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
32641 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
32642 * traditional select (defaults to true)
32646 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
32650 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
32654 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
32655 * listWidth has a higher value)
32659 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
32660 * allow the user to set arbitrary text into the field (defaults to false)
32662 forceSelection:false,
32664 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
32665 * if typeAhead = true (defaults to 250)
32667 typeAheadDelay : 250,
32669 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
32670 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
32672 valueNotFoundText : undefined,
32675 * @cfg {String} defaultValue The value displayed after loading the store.
32680 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
32682 blockFocus : false,
32685 * @cfg {Boolean} disableClear Disable showing of clear button.
32687 disableClear : false,
32689 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
32691 alwaysQuery : false,
32697 // element that contains real text value.. (when hidden is used..)
32700 onRender : function(ct, position){
32701 Roo.form.Field.prototype.onRender.call(this, ct, position);
32704 this.store.on('beforeload', this.onBeforeLoad, this);
32705 this.store.on('load', this.onLoad, this);
32706 this.store.on('loadexception', this.onLoadException, this);
32707 this.store.load({});
32715 initEvents : function(){
32716 //Roo.form.ComboBox.superclass.initEvents.call(this);
32720 onDestroy : function(){
32723 this.store.un('beforeload', this.onBeforeLoad, this);
32724 this.store.un('load', this.onLoad, this);
32725 this.store.un('loadexception', this.onLoadException, this);
32727 //Roo.form.ComboBox.superclass.onDestroy.call(this);
32731 fireKey : function(e){
32732 if(e.isNavKeyPress() && !this.list.isVisible()){
32733 this.fireEvent("specialkey", this, e);
32738 onResize: function(w, h){
32746 * Allow or prevent the user from directly editing the field text. If false is passed,
32747 * the user will only be able to select from the items defined in the dropdown list. This method
32748 * is the runtime equivalent of setting the 'editable' config option at config time.
32749 * @param {Boolean} value True to allow the user to directly edit the field text
32751 setEditable : function(value){
32756 onBeforeLoad : function(){
32758 Roo.log("Select before load");
32761 this.innerList.update(this.loadingText ?
32762 '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
32763 //this.restrictHeight();
32764 this.selectedIndex = -1;
32768 onLoad : function(){
32771 var dom = this.el.dom;
32772 dom.innerHTML = '';
32773 var od = dom.ownerDocument;
32775 if (this.emptyText) {
32776 var op = od.createElement('option');
32777 op.setAttribute('value', '');
32778 op.innerHTML = String.format('{0}', this.emptyText);
32779 dom.appendChild(op);
32781 if(this.store.getCount() > 0){
32783 var vf = this.valueField;
32784 var df = this.displayField;
32785 this.store.data.each(function(r) {
32786 // which colmsn to use... testing - cdoe / title..
32787 var op = od.createElement('option');
32788 op.setAttribute('value', r.data[vf]);
32789 op.innerHTML = String.format('{0}', r.data[df]);
32790 dom.appendChild(op);
32792 if (typeof(this.defaultValue != 'undefined')) {
32793 this.setValue(this.defaultValue);
32798 //this.onEmptyResults();
32803 onLoadException : function()
32805 dom.innerHTML = '';
32807 Roo.log("Select on load exception");
32811 Roo.log(this.store.reader.jsonData);
32812 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
32813 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
32819 onTypeAhead : function(){
32824 onSelect : function(record, index){
32825 Roo.log('on select?');
32827 if(this.fireEvent('beforeselect', this, record, index) !== false){
32828 this.setFromData(index > -1 ? record.data : false);
32830 this.fireEvent('select', this, record, index);
32835 * Returns the currently selected field value or empty string if no value is set.
32836 * @return {String} value The selected value
32838 getValue : function(){
32839 var dom = this.el.dom;
32840 this.value = dom.options[dom.selectedIndex].value;
32846 * Clears any text/value currently set in the field
32848 clearValue : function(){
32850 this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
32855 * Sets the specified value into the field. If the value finds a match, the corresponding record text
32856 * will be displayed in the field. If the value does not match the data value of an existing item,
32857 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
32858 * Otherwise the field will be blank (although the value will still be set).
32859 * @param {String} value The value to match
32861 setValue : function(v){
32862 var d = this.el.dom;
32863 for (var i =0; i < d.options.length;i++) {
32864 if (v == d.options[i].value) {
32865 d.selectedIndex = i;
32873 * @property {Object} the last set data for the element
32878 * Sets the value of the field based on a object which is related to the record format for the store.
32879 * @param {Object} value the value to set as. or false on reset?
32881 setFromData : function(o){
32882 Roo.log('setfrom data?');
32888 reset : function(){
32892 findRecord : function(prop, value){
32897 if(this.store.getCount() > 0){
32898 this.store.each(function(r){
32899 if(r.data[prop] == value){
32909 getName: function()
32911 // returns hidden if it's set..
32912 if (!this.rendered) {return ''};
32913 return !this.hiddenName && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
32921 onEmptyResults : function(){
32922 Roo.log('empty results');
32927 * Returns true if the dropdown list is expanded, else false.
32929 isExpanded : function(){
32934 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
32935 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32936 * @param {String} value The data value of the item to select
32937 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32938 * selected item if it is not currently in view (defaults to true)
32939 * @return {Boolean} True if the value matched an item in the list, else false
32941 selectByValue : function(v, scrollIntoView){
32942 Roo.log('select By Value');
32945 if(v !== undefined && v !== null){
32946 var r = this.findRecord(this.valueField || this.displayField, v);
32948 this.select(this.store.indexOf(r), scrollIntoView);
32956 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
32957 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
32958 * @param {Number} index The zero-based index of the list item to select
32959 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
32960 * selected item if it is not currently in view (defaults to true)
32962 select : function(index, scrollIntoView){
32963 Roo.log('select ');
32966 this.selectedIndex = index;
32967 this.view.select(index);
32968 if(scrollIntoView !== false){
32969 var el = this.view.getNode(index);
32971 this.innerList.scrollChildIntoView(el, false);
32979 validateBlur : function(){
32986 initQuery : function(){
32987 this.doQuery(this.getRawValue());
32991 doForce : function(){
32992 if(this.el.dom.value.length > 0){
32993 this.el.dom.value =
32994 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
33000 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
33001 * query allowing the query action to be canceled if needed.
33002 * @param {String} query The SQL query to execute
33003 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
33004 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
33005 * saved in the current store (defaults to false)
33007 doQuery : function(q, forceAll){
33009 Roo.log('doQuery?');
33010 if(q === undefined || q === null){
33015 forceAll: forceAll,
33019 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
33023 forceAll = qe.forceAll;
33024 if(forceAll === true || (q.length >= this.minChars)){
33025 if(this.lastQuery != q || this.alwaysQuery){
33026 this.lastQuery = q;
33027 if(this.mode == 'local'){
33028 this.selectedIndex = -1;
33030 this.store.clearFilter();
33032 this.store.filter(this.displayField, q);
33036 this.store.baseParams[this.queryParam] = q;
33038 params: this.getParams(q)
33043 this.selectedIndex = -1;
33050 getParams : function(q){
33052 //p[this.queryParam] = q;
33055 p.limit = this.pageSize;
33061 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
33063 collapse : function(){
33068 collapseIf : function(e){
33073 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
33075 expand : function(){
33083 * @cfg {Boolean} grow
33087 * @cfg {Number} growMin
33091 * @cfg {Number} growMax
33099 setWidth : function()
33103 getResizeEl : function(){
33106 });//<script type="text/javasscript">
33110 * @class Roo.DDView
33111 * A DnD enabled version of Roo.View.
33112 * @param {Element/String} container The Element in which to create the View.
33113 * @param {String} tpl The template string used to create the markup for each element of the View
33114 * @param {Object} config The configuration properties. These include all the config options of
33115 * {@link Roo.View} plus some specific to this class.<br>
33117 * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
33118 * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
33120 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
33121 .x-view-drag-insert-above {
33122 border-top:1px dotted #3366cc;
33124 .x-view-drag-insert-below {
33125 border-bottom:1px dotted #3366cc;
33131 Roo.DDView = function(container, tpl, config) {
33132 Roo.DDView.superclass.constructor.apply(this, arguments);
33133 this.getEl().setStyle("outline", "0px none");
33134 this.getEl().unselectable();
33135 if (this.dragGroup) {
33136 this.setDraggable(this.dragGroup.split(","));
33138 if (this.dropGroup) {
33139 this.setDroppable(this.dropGroup.split(","));
33141 if (this.deletable) {
33142 this.setDeletable();
33144 this.isDirtyFlag = false;
33150 Roo.extend(Roo.DDView, Roo.View, {
33151 /** @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
33152 /** @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
33153 /** @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
33154 /** @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
33158 reset: Roo.emptyFn,
33160 clearInvalid: Roo.form.Field.prototype.clearInvalid,
33162 validate: function() {
33166 destroy: function() {
33167 this.purgeListeners();
33168 this.getEl.removeAllListeners();
33169 this.getEl().remove();
33170 if (this.dragZone) {
33171 if (this.dragZone.destroy) {
33172 this.dragZone.destroy();
33175 if (this.dropZone) {
33176 if (this.dropZone.destroy) {
33177 this.dropZone.destroy();
33182 /** Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
33183 getName: function() {
33187 /** Loads the View from a JSON string representing the Records to put into the Store. */
33188 setValue: function(v) {
33190 throw "DDView.setValue(). DDView must be constructed with a valid Store";
33193 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
33194 this.store.proxy = new Roo.data.MemoryProxy(data);
33198 /** @return {String} a parenthesised list of the ids of the Records in the View. */
33199 getValue: function() {
33201 this.store.each(function(rec) {
33202 result += rec.id + ',';
33204 return result.substr(0, result.length - 1) + ')';
33207 getIds: function() {
33208 var i = 0, result = new Array(this.store.getCount());
33209 this.store.each(function(rec) {
33210 result[i++] = rec.id;
33215 isDirty: function() {
33216 return this.isDirtyFlag;
33220 * Part of the Roo.dd.DropZone interface. If no target node is found, the
33221 * whole Element becomes the target, and this causes the drop gesture to append.
33223 getTargetFromEvent : function(e) {
33224 var target = e.getTarget();
33225 while ((target !== null) && (target.parentNode != this.el.dom)) {
33226 target = target.parentNode;
33229 target = this.el.dom.lastChild || this.el.dom;
33235 * Create the drag data which consists of an object which has the property "ddel" as
33236 * the drag proxy element.
33238 getDragData : function(e) {
33239 var target = this.findItemFromChild(e.getTarget());
33241 this.handleSelection(e);
33242 var selNodes = this.getSelectedNodes();
33245 copy: this.copy || (this.allowCopy && e.ctrlKey),
33249 var selectedIndices = this.getSelectedIndexes();
33250 for (var i = 0; i < selectedIndices.length; i++) {
33251 dragData.records.push(this.store.getAt(selectedIndices[i]));
33253 if (selNodes.length == 1) {
33254 dragData.ddel = target.cloneNode(true); // the div element
33256 var div = document.createElement('div'); // create the multi element drag "ghost"
33257 div.className = 'multi-proxy';
33258 for (var i = 0, len = selNodes.length; i < len; i++) {
33259 div.appendChild(selNodes[i].cloneNode(true));
33261 dragData.ddel = div;
33263 //console.log(dragData)
33264 //console.log(dragData.ddel.innerHTML)
33267 //console.log('nodragData')
33271 /** Specify to which ddGroup items in this DDView may be dragged. */
33272 setDraggable: function(ddGroup) {
33273 if (ddGroup instanceof Array) {
33274 Roo.each(ddGroup, this.setDraggable, this);
33277 if (this.dragZone) {
33278 this.dragZone.addToGroup(ddGroup);
33280 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
33281 containerScroll: true,
33285 // Draggability implies selection. DragZone's mousedown selects the element.
33286 if (!this.multiSelect) { this.singleSelect = true; }
33288 // Wire the DragZone's handlers up to methods in *this*
33289 this.dragZone.getDragData = this.getDragData.createDelegate(this);
33293 /** Specify from which ddGroup this DDView accepts drops. */
33294 setDroppable: function(ddGroup) {
33295 if (ddGroup instanceof Array) {
33296 Roo.each(ddGroup, this.setDroppable, this);
33299 if (this.dropZone) {
33300 this.dropZone.addToGroup(ddGroup);
33302 this.dropZone = new Roo.dd.DropZone(this.getEl(), {
33303 containerScroll: true,
33307 // Wire the DropZone's handlers up to methods in *this*
33308 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
33309 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
33310 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
33311 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
33312 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
33316 /** Decide whether to drop above or below a View node. */
33317 getDropPoint : function(e, n, dd){
33318 if (n == this.el.dom) { return "above"; }
33319 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
33320 var c = t + (b - t) / 2;
33321 var y = Roo.lib.Event.getPageY(e);
33329 onNodeEnter : function(n, dd, e, data){
33333 onNodeOver : function(n, dd, e, data){
33334 var pt = this.getDropPoint(e, n, dd);
33335 // set the insert point style on the target node
33336 var dragElClass = this.dropNotAllowed;
33339 if (pt == "above"){
33340 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
33341 targetElClass = "x-view-drag-insert-above";
33343 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
33344 targetElClass = "x-view-drag-insert-below";
33346 if (this.lastInsertClass != targetElClass){
33347 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
33348 this.lastInsertClass = targetElClass;
33351 return dragElClass;
33354 onNodeOut : function(n, dd, e, data){
33355 this.removeDropIndicators(n);
33358 onNodeDrop : function(n, dd, e, data){
33359 if (this.fireEvent("drop", this, n, dd, e, data) === false) {
33362 var pt = this.getDropPoint(e, n, dd);
33363 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
33364 if (pt == "below") { insertAt++; }
33365 for (var i = 0; i < data.records.length; i++) {
33366 var r = data.records[i];
33367 var dup = this.store.getById(r.id);
33368 if (dup && (dd != this.dragZone)) {
33369 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
33372 this.store.insert(insertAt++, r.copy());
33374 data.source.isDirtyFlag = true;
33376 this.store.insert(insertAt++, r);
33378 this.isDirtyFlag = true;
33381 this.dragZone.cachedTarget = null;
33385 removeDropIndicators : function(n){
33387 Roo.fly(n).removeClass([
33388 "x-view-drag-insert-above",
33389 "x-view-drag-insert-below"]);
33390 this.lastInsertClass = "_noclass";
33395 * Utility method. Add a delete option to the DDView's context menu.
33396 * @param {String} imageUrl The URL of the "delete" icon image.
33398 setDeletable: function(imageUrl) {
33399 if (!this.singleSelect && !this.multiSelect) {
33400 this.singleSelect = true;
33402 var c = this.getContextMenu();
33403 this.contextMenu.on("itemclick", function(item) {
33406 this.remove(this.getSelectedIndexes());
33410 this.contextMenu.add({
33417 /** Return the context menu for this DDView. */
33418 getContextMenu: function() {
33419 if (!this.contextMenu) {
33420 // Create the View's context menu
33421 this.contextMenu = new Roo.menu.Menu({
33422 id: this.id + "-contextmenu"
33424 this.el.on("contextmenu", this.showContextMenu, this);
33426 return this.contextMenu;
33429 disableContextMenu: function() {
33430 if (this.contextMenu) {
33431 this.el.un("contextmenu", this.showContextMenu, this);
33435 showContextMenu: function(e, item) {
33436 item = this.findItemFromChild(e.getTarget());
33439 this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
33440 this.contextMenu.showAt(e.getXY());
33445 * Remove {@link Roo.data.Record}s at the specified indices.
33446 * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
33448 remove: function(selectedIndices) {
33449 selectedIndices = [].concat(selectedIndices);
33450 for (var i = 0; i < selectedIndices.length; i++) {
33451 var rec = this.store.getAt(selectedIndices[i]);
33452 this.store.remove(rec);
33457 * Double click fires the event, but also, if this is draggable, and there is only one other
33458 * related DropZone, it transfers the selected node.
33460 onDblClick : function(e){
33461 var item = this.findItemFromChild(e.getTarget());
33463 if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
33466 if (this.dragGroup) {
33467 var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
33468 while (targets.indexOf(this.dropZone) > -1) {
33469 targets.remove(this.dropZone);
33471 if (targets.length == 1) {
33472 this.dragZone.cachedTarget = null;
33473 var el = Roo.get(targets[0].getEl());
33474 var box = el.getBox(true);
33475 targets[0].onNodeDrop(el.dom, {
33477 xy: [box.x, box.y + box.height - 1]
33478 }, null, this.getDragData(e));
33484 handleSelection: function(e) {
33485 this.dragZone.cachedTarget = null;
33486 var item = this.findItemFromChild(e.getTarget());
33488 this.clearSelections(true);
33491 if (item && (this.multiSelect || this.singleSelect)){
33492 if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
33493 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
33494 }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
33495 this.unselect(item);
33497 this.select(item, this.multiSelect && e.ctrlKey);
33498 this.lastSelection = item;
33503 onItemClick : function(item, index, e){
33504 if(this.fireEvent("beforeclick", this, index, item, e) === false){
33510 unselect : function(nodeInfo, suppressEvent){
33511 var node = this.getNode(nodeInfo);
33512 if(node && this.isSelected(node)){
33513 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
33514 Roo.fly(node).removeClass(this.selectedClass);
33515 this.selections.remove(node);
33516 if(!suppressEvent){
33517 this.fireEvent("selectionchange", this, this.selections);
33525 * Ext JS Library 1.1.1
33526 * Copyright(c) 2006-2007, Ext JS, LLC.
33528 * Originally Released Under LGPL - original licence link has changed is not relivant.
33531 * <script type="text/javascript">
33535 * @class Roo.LayoutManager
33536 * @extends Roo.util.Observable
33537 * Base class for layout managers.
33539 Roo.LayoutManager = function(container, config){
33540 Roo.LayoutManager.superclass.constructor.call(this);
33541 this.el = Roo.get(container);
33542 // ie scrollbar fix
33543 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33544 document.body.scroll = "no";
33545 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33546 this.el.position('relative');
33548 this.id = this.el.id;
33549 this.el.addClass("x-layout-container");
33550 /** false to disable window resize monitoring @type Boolean */
33551 this.monitorWindowResize = true;
33556 * Fires when a layout is performed.
33557 * @param {Roo.LayoutManager} this
33561 * @event regionresized
33562 * Fires when the user resizes a region.
33563 * @param {Roo.LayoutRegion} region The resized region
33564 * @param {Number} newSize The new size (width for east/west, height for north/south)
33566 "regionresized" : true,
33568 * @event regioncollapsed
33569 * Fires when a region is collapsed.
33570 * @param {Roo.LayoutRegion} region The collapsed region
33572 "regioncollapsed" : true,
33574 * @event regionexpanded
33575 * Fires when a region is expanded.
33576 * @param {Roo.LayoutRegion} region The expanded region
33578 "regionexpanded" : true
33580 this.updating = false;
33581 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33584 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
33586 * Returns true if this layout is currently being updated
33587 * @return {Boolean}
33589 isUpdating : function(){
33590 return this.updating;
33594 * Suspend the LayoutManager from doing auto-layouts while
33595 * making multiple add or remove calls
33597 beginUpdate : function(){
33598 this.updating = true;
33602 * Restore auto-layouts and optionally disable the manager from performing a layout
33603 * @param {Boolean} noLayout true to disable a layout update
33605 endUpdate : function(noLayout){
33606 this.updating = false;
33612 layout: function(){
33616 onRegionResized : function(region, newSize){
33617 this.fireEvent("regionresized", region, newSize);
33621 onRegionCollapsed : function(region){
33622 this.fireEvent("regioncollapsed", region);
33625 onRegionExpanded : function(region){
33626 this.fireEvent("regionexpanded", region);
33630 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33631 * performs box-model adjustments.
33632 * @return {Object} The size as an object {width: (the width), height: (the height)}
33634 getViewSize : function(){
33636 if(this.el.dom != document.body){
33637 size = this.el.getSize();
33639 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33641 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33642 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33647 * Returns the Element this layout is bound to.
33648 * @return {Roo.Element}
33650 getEl : function(){
33655 * Returns the specified region.
33656 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33657 * @return {Roo.LayoutRegion}
33659 getRegion : function(target){
33660 return this.regions[target.toLowerCase()];
33663 onWindowResize : function(){
33664 if(this.monitorWindowResize){
33670 * Ext JS Library 1.1.1
33671 * Copyright(c) 2006-2007, Ext JS, LLC.
33673 * Originally Released Under LGPL - original licence link has changed is not relivant.
33676 * <script type="text/javascript">
33679 * @class Roo.BorderLayout
33680 * @extends Roo.LayoutManager
33681 * @children Roo.ContentPanel
33682 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33683 * please see: <br><br>
33684 * <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>
33685 * <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>
33688 var layout = new Roo.BorderLayout(document.body, {
33722 preferredTabWidth: 150
33727 var CP = Roo.ContentPanel;
33729 layout.beginUpdate();
33730 layout.add("north", new CP("north", "North"));
33731 layout.add("south", new CP("south", {title: "South", closable: true}));
33732 layout.add("west", new CP("west", {title: "West"}));
33733 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
33734 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
33735 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
33736 layout.getRegion("center").showPanel("center1");
33737 layout.endUpdate();
33740 <b>The container the layout is rendered into can be either the body element or any other element.
33741 If it is not the body element, the container needs to either be an absolute positioned element,
33742 or you will need to add "position:relative" to the css of the container. You will also need to specify
33743 the container size if it is not the body element.</b>
33746 * Create a new BorderLayout
33747 * @param {String/HTMLElement/Element} container The container this layout is bound to
33748 * @param {Object} config Configuration options
33750 Roo.BorderLayout = function(container, config){
33751 config = config || {};
33752 Roo.BorderLayout.superclass.constructor.call(this, container, config);
33753 this.factory = config.factory || Roo.BorderLayout.RegionFactory;
33754 for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
33755 var target = this.factory.validRegions[i];
33756 if(config[target]){
33757 this.addRegion(target, config[target]);
33762 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
33765 * @cfg {Roo.LayoutRegion} east
33768 * @cfg {Roo.LayoutRegion} west
33771 * @cfg {Roo.LayoutRegion} north
33774 * @cfg {Roo.LayoutRegion} south
33777 * @cfg {Roo.LayoutRegion} center
33780 * Creates and adds a new region if it doesn't already exist.
33781 * @param {String} target The target region key (north, south, east, west or center).
33782 * @param {Object} config The regions config object
33783 * @return {BorderLayoutRegion} The new region
33785 addRegion : function(target, config){
33786 if(!this.regions[target]){
33787 var r = this.factory.create(target, this, config);
33788 this.bindRegion(target, r);
33790 return this.regions[target];
33794 bindRegion : function(name, r){
33795 this.regions[name] = r;
33796 r.on("visibilitychange", this.layout, this);
33797 r.on("paneladded", this.layout, this);
33798 r.on("panelremoved", this.layout, this);
33799 r.on("invalidated", this.layout, this);
33800 r.on("resized", this.onRegionResized, this);
33801 r.on("collapsed", this.onRegionCollapsed, this);
33802 r.on("expanded", this.onRegionExpanded, this);
33806 * Performs a layout update.
33808 layout : function(){
33809 if(this.updating) {
33812 var size = this.getViewSize();
33813 var w = size.width;
33814 var h = size.height;
33819 //var x = 0, y = 0;
33821 var rs = this.regions;
33822 var north = rs["north"];
33823 var south = rs["south"];
33824 var west = rs["west"];
33825 var east = rs["east"];
33826 var center = rs["center"];
33827 //if(this.hideOnLayout){ // not supported anymore
33828 //c.el.setStyle("display", "none");
33830 if(north && north.isVisible()){
33831 var b = north.getBox();
33832 var m = north.getMargins();
33833 b.width = w - (m.left+m.right);
33836 centerY = b.height + b.y + m.bottom;
33837 centerH -= centerY;
33838 north.updateBox(this.safeBox(b));
33840 if(south && south.isVisible()){
33841 var b = south.getBox();
33842 var m = south.getMargins();
33843 b.width = w - (m.left+m.right);
33845 var totalHeight = (b.height + m.top + m.bottom);
33846 b.y = h - totalHeight + m.top;
33847 centerH -= totalHeight;
33848 south.updateBox(this.safeBox(b));
33850 if(west && west.isVisible()){
33851 var b = west.getBox();
33852 var m = west.getMargins();
33853 b.height = centerH - (m.top+m.bottom);
33855 b.y = centerY + m.top;
33856 var totalWidth = (b.width + m.left + m.right);
33857 centerX += totalWidth;
33858 centerW -= totalWidth;
33859 west.updateBox(this.safeBox(b));
33861 if(east && east.isVisible()){
33862 var b = east.getBox();
33863 var m = east.getMargins();
33864 b.height = centerH - (m.top+m.bottom);
33865 var totalWidth = (b.width + m.left + m.right);
33866 b.x = w - totalWidth + m.left;
33867 b.y = centerY + m.top;
33868 centerW -= totalWidth;
33869 east.updateBox(this.safeBox(b));
33872 var m = center.getMargins();
33874 x: centerX + m.left,
33875 y: centerY + m.top,
33876 width: centerW - (m.left+m.right),
33877 height: centerH - (m.top+m.bottom)
33879 //if(this.hideOnLayout){
33880 //center.el.setStyle("display", "block");
33882 center.updateBox(this.safeBox(centerBox));
33885 this.fireEvent("layout", this);
33889 safeBox : function(box){
33890 box.width = Math.max(0, box.width);
33891 box.height = Math.max(0, box.height);
33896 * Adds a ContentPanel (or subclass) to this layout.
33897 * @param {String} target The target region key (north, south, east, west or center).
33898 * @param {Roo.ContentPanel} panel The panel to add
33899 * @return {Roo.ContentPanel} The added panel
33901 add : function(target, panel){
33903 target = target.toLowerCase();
33904 return this.regions[target].add(panel);
33908 * Remove a ContentPanel (or subclass) to this layout.
33909 * @param {String} target The target region key (north, south, east, west or center).
33910 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33911 * @return {Roo.ContentPanel} The removed panel
33913 remove : function(target, panel){
33914 target = target.toLowerCase();
33915 return this.regions[target].remove(panel);
33919 * Searches all regions for a panel with the specified id
33920 * @param {String} panelId
33921 * @return {Roo.ContentPanel} The panel or null if it wasn't found
33923 findPanel : function(panelId){
33924 var rs = this.regions;
33925 for(var target in rs){
33926 if(typeof rs[target] != "function"){
33927 var p = rs[target].getPanel(panelId);
33937 * Searches all regions for a panel with the specified id and activates (shows) it.
33938 * @param {String/ContentPanel} panelId The panels id or the panel itself
33939 * @return {Roo.ContentPanel} The shown panel or null
33941 showPanel : function(panelId) {
33942 var rs = this.regions;
33943 for(var target in rs){
33944 var r = rs[target];
33945 if(typeof r != "function"){
33946 if(r.hasPanel(panelId)){
33947 return r.showPanel(panelId);
33955 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33956 * @param {Roo.state.Provider} provider (optional) An alternate state provider
33958 restoreState : function(provider){
33960 provider = Roo.state.Manager;
33962 var sm = new Roo.LayoutStateManager();
33963 sm.init(this, provider);
33967 * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object. This config
33968 * object should contain properties for each region to add ContentPanels to, and each property's value should be
33969 * a valid ContentPanel config object. Example:
33971 // Create the main layout
33972 var layout = new Roo.BorderLayout('main-ct', {
33983 // Create and add multiple ContentPanels at once via configs
33986 id: 'source-files',
33988 title:'Ext Source Files',
34001 * @param {Object} regions An object containing ContentPanel configs by region name
34003 batchAdd : function(regions){
34004 this.beginUpdate();
34005 for(var rname in regions){
34006 var lr = this.regions[rname];
34008 this.addTypedPanels(lr, regions[rname]);
34015 addTypedPanels : function(lr, ps){
34016 if(typeof ps == 'string'){
34017 lr.add(new Roo.ContentPanel(ps));
34019 else if(ps instanceof Array){
34020 for(var i =0, len = ps.length; i < len; i++){
34021 this.addTypedPanels(lr, ps[i]);
34024 else if(!ps.events){ // raw config?
34026 delete ps.el; // prevent conflict
34027 lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
34029 else { // panel object assumed!
34034 * Adds a xtype elements to the layout.
34038 xtype : 'ContentPanel',
34045 xtype : 'NestedLayoutPanel',
34051 items : [ ... list of content panels or nested layout panels.. ]
34055 * @param {Object} cfg Xtype definition of item to add.
34057 addxtype : function(cfg)
34059 // basically accepts a pannel...
34060 // can accept a layout region..!?!?
34061 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34063 if (!cfg.xtype.match(/Panel$/)) {
34068 if (typeof(cfg.region) == 'undefined') {
34069 Roo.log("Failed to add Panel, region was not set");
34073 var region = cfg.region;
34079 xitems = cfg.items;
34086 case 'ContentPanel': // ContentPanel (el, cfg)
34087 case 'ScrollPanel': // ContentPanel (el, cfg)
34089 if(cfg.autoCreate) {
34090 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34092 var el = this.el.createChild();
34093 ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34096 this.add(region, ret);
34100 case 'TreePanel': // our new panel!
34101 cfg.el = this.el.createChild();
34102 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34103 this.add(region, ret);
34106 case 'NestedLayoutPanel':
34107 // create a new Layout (which is a Border Layout...
34108 var el = this.el.createChild();
34109 var clayout = cfg.layout;
34111 clayout.items = clayout.items || [];
34112 // replace this exitems with the clayout ones..
34113 xitems = clayout.items;
34116 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34117 cfg.background = false;
34119 var layout = new Roo.BorderLayout(el, clayout);
34121 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
34122 //console.log('adding nested layout panel ' + cfg.toSource());
34123 this.add(region, ret);
34124 nb = {}; /// find first...
34129 // needs grid and region
34131 //var el = this.getRegion(region).el.createChild();
34132 var el = this.el.createChild();
34133 // create the grid first...
34135 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
34137 if (region == 'center' && this.active ) {
34138 cfg.background = false;
34140 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
34142 this.add(region, ret);
34143 if (cfg.background) {
34144 ret.on('activate', function(gp) {
34145 if (!gp.grid.rendered) {
34160 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34162 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34163 this.add(region, ret);
34166 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
34170 // GridPanel (grid, cfg)
34173 this.beginUpdate();
34177 Roo.each(xitems, function(i) {
34178 region = nb && i.region ? i.region : false;
34180 var add = ret.addxtype(i);
34183 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34184 if (!i.background) {
34185 abn[region] = nb[region] ;
34192 // make the last non-background panel active..
34193 //if (nb) { Roo.log(abn); }
34196 for(var r in abn) {
34197 region = this.getRegion(r);
34199 // tried using nb[r], but it does not work..
34201 region.showPanel(abn[r]);
34212 * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
34213 * the beginUpdate and endUpdate calls internally. The key to this method is the <b>panels</b> property that can be
34214 * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
34215 * during creation. The following code is equivalent to the constructor-based example at the beginning of this class:
34218 var CP = Roo.ContentPanel;
34220 var layout = Roo.BorderLayout.create({
34224 panels: [new CP("north", "North")]
34233 panels: [new CP("west", {title: "West"})]
34242 panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
34251 panels: [new CP("south", {title: "South", closable: true})]
34258 preferredTabWidth: 150,
34260 new CP("center1", {title: "Close Me", closable: true}),
34261 new CP("center2", {title: "Center Panel", closable: false})
34266 layout.getRegion("center").showPanel("center1");
34271 Roo.BorderLayout.create = function(config, targetEl){
34272 var layout = new Roo.BorderLayout(targetEl || document.body, config);
34273 layout.beginUpdate();
34274 var regions = Roo.BorderLayout.RegionFactory.validRegions;
34275 for(var j = 0, jlen = regions.length; j < jlen; j++){
34276 var lr = regions[j];
34277 if(layout.regions[lr] && config[lr].panels){
34278 var r = layout.regions[lr];
34279 var ps = config[lr].panels;
34280 layout.addTypedPanels(r, ps);
34283 layout.endUpdate();
34288 Roo.BorderLayout.RegionFactory = {
34290 validRegions : ["north","south","east","west","center"],
34293 create : function(target, mgr, config){
34294 target = target.toLowerCase();
34295 if(config.lightweight || config.basic){
34296 return new Roo.BasicLayoutRegion(mgr, config, target);
34300 return new Roo.NorthLayoutRegion(mgr, config);
34302 return new Roo.SouthLayoutRegion(mgr, config);
34304 return new Roo.EastLayoutRegion(mgr, config);
34306 return new Roo.WestLayoutRegion(mgr, config);
34308 return new Roo.CenterLayoutRegion(mgr, config);
34310 throw 'Layout region "'+target+'" not supported.';
34314 * Ext JS Library 1.1.1
34315 * Copyright(c) 2006-2007, Ext JS, LLC.
34317 * Originally Released Under LGPL - original licence link has changed is not relivant.
34320 * <script type="text/javascript">
34324 * @class Roo.BasicLayoutRegion
34325 * @extends Roo.util.Observable
34326 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34327 * and does not have a titlebar, tabs or any other features. All it does is size and position
34328 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34330 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
34332 this.position = pos;
34335 * @scope Roo.BasicLayoutRegion
34339 * @event beforeremove
34340 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34341 * @param {Roo.LayoutRegion} this
34342 * @param {Roo.ContentPanel} panel The panel
34343 * @param {Object} e The cancel event object
34345 "beforeremove" : true,
34347 * @event invalidated
34348 * Fires when the layout for this region is changed.
34349 * @param {Roo.LayoutRegion} this
34351 "invalidated" : true,
34353 * @event visibilitychange
34354 * Fires when this region is shown or hidden
34355 * @param {Roo.LayoutRegion} this
34356 * @param {Boolean} visibility true or false
34358 "visibilitychange" : true,
34360 * @event paneladded
34361 * Fires when a panel is added.
34362 * @param {Roo.LayoutRegion} this
34363 * @param {Roo.ContentPanel} panel The panel
34365 "paneladded" : true,
34367 * @event panelremoved
34368 * Fires when a panel is removed.
34369 * @param {Roo.LayoutRegion} this
34370 * @param {Roo.ContentPanel} panel The panel
34372 "panelremoved" : true,
34374 * @event beforecollapse
34375 * Fires when this region before collapse.
34376 * @param {Roo.LayoutRegion} this
34378 "beforecollapse" : true,
34381 * Fires when this region is collapsed.
34382 * @param {Roo.LayoutRegion} this
34384 "collapsed" : true,
34387 * Fires when this region is expanded.
34388 * @param {Roo.LayoutRegion} this
34393 * Fires when this region is slid into view.
34394 * @param {Roo.LayoutRegion} this
34396 "slideshow" : true,
34399 * Fires when this region slides out of view.
34400 * @param {Roo.LayoutRegion} this
34402 "slidehide" : true,
34404 * @event panelactivated
34405 * Fires when a panel is activated.
34406 * @param {Roo.LayoutRegion} this
34407 * @param {Roo.ContentPanel} panel The activated panel
34409 "panelactivated" : true,
34412 * Fires when the user resizes this region.
34413 * @param {Roo.LayoutRegion} this
34414 * @param {Number} newSize The new size (width for east/west, height for north/south)
34418 /** A collection of panels in this region. @type Roo.util.MixedCollection */
34419 this.panels = new Roo.util.MixedCollection();
34420 this.panels.getKey = this.getPanelId.createDelegate(this);
34422 this.activePanel = null;
34423 // ensure listeners are added...
34425 if (config.listeners || config.events) {
34426 Roo.BasicLayoutRegion.superclass.constructor.call(this, {
34427 listeners : config.listeners || {},
34428 events : config.events || {}
34432 if(skipConfig !== true){
34433 this.applyConfig(config);
34437 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
34438 getPanelId : function(p){
34442 applyConfig : function(config){
34443 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34444 this.config = config;
34449 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
34450 * the width, for horizontal (north, south) the height.
34451 * @param {Number} newSize The new width or height
34453 resizeTo : function(newSize){
34454 var el = this.el ? this.el :
34455 (this.activePanel ? this.activePanel.getEl() : null);
34457 switch(this.position){
34460 el.setWidth(newSize);
34461 this.fireEvent("resized", this, newSize);
34465 el.setHeight(newSize);
34466 this.fireEvent("resized", this, newSize);
34472 getBox : function(){
34473 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34476 getMargins : function(){
34477 return this.margins;
34480 updateBox : function(box){
34482 var el = this.activePanel.getEl();
34483 el.dom.style.left = box.x + "px";
34484 el.dom.style.top = box.y + "px";
34485 this.activePanel.setSize(box.width, box.height);
34489 * Returns the container element for this region.
34490 * @return {Roo.Element}
34492 getEl : function(){
34493 return this.activePanel;
34497 * Returns true if this region is currently visible.
34498 * @return {Boolean}
34500 isVisible : function(){
34501 return this.activePanel ? true : false;
34504 setActivePanel : function(panel){
34505 panel = this.getPanel(panel);
34506 if(this.activePanel && this.activePanel != panel){
34507 this.activePanel.setActiveState(false);
34508 this.activePanel.getEl().setLeftTop(-10000,-10000);
34510 this.activePanel = panel;
34511 panel.setActiveState(true);
34513 panel.setSize(this.box.width, this.box.height);
34515 this.fireEvent("panelactivated", this, panel);
34516 this.fireEvent("invalidated");
34520 * Show the specified panel.
34521 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34522 * @return {Roo.ContentPanel} The shown panel or null
34524 showPanel : function(panel){
34525 if(panel = this.getPanel(panel)){
34526 this.setActivePanel(panel);
34532 * Get the active panel for this region.
34533 * @return {Roo.ContentPanel} The active panel or null
34535 getActivePanel : function(){
34536 return this.activePanel;
34540 * Add the passed ContentPanel(s)
34541 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34542 * @return {Roo.ContentPanel} The panel added (if only one was added)
34544 add : function(panel){
34545 if(arguments.length > 1){
34546 for(var i = 0, len = arguments.length; i < len; i++) {
34547 this.add(arguments[i]);
34551 if(this.hasPanel(panel)){
34552 this.showPanel(panel);
34555 var el = panel.getEl();
34556 if(el.dom.parentNode != this.mgr.el.dom){
34557 this.mgr.el.dom.appendChild(el.dom);
34559 if(panel.setRegion){
34560 panel.setRegion(this);
34562 this.panels.add(panel);
34563 el.setStyle("position", "absolute");
34564 if(!panel.background){
34565 this.setActivePanel(panel);
34566 if(this.config.initialSize && this.panels.getCount()==1){
34567 this.resizeTo(this.config.initialSize);
34570 this.fireEvent("paneladded", this, panel);
34575 * Returns true if the panel is in this region.
34576 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34577 * @return {Boolean}
34579 hasPanel : function(panel){
34580 if(typeof panel == "object"){ // must be panel obj
34581 panel = panel.getId();
34583 return this.getPanel(panel) ? true : false;
34587 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34588 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34589 * @param {Boolean} preservePanel Overrides the config preservePanel option
34590 * @return {Roo.ContentPanel} The panel that was removed
34592 remove : function(panel, preservePanel){
34593 panel = this.getPanel(panel);
34598 this.fireEvent("beforeremove", this, panel, e);
34599 if(e.cancel === true){
34602 var panelId = panel.getId();
34603 this.panels.removeKey(panelId);
34608 * Returns the panel specified or null if it's not in this region.
34609 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34610 * @return {Roo.ContentPanel}
34612 getPanel : function(id){
34613 if(typeof id == "object"){ // must be panel obj
34616 return this.panels.get(id);
34620 * Returns this regions position (north/south/east/west/center).
34623 getPosition: function(){
34624 return this.position;
34628 * Ext JS Library 1.1.1
34629 * Copyright(c) 2006-2007, Ext JS, LLC.
34631 * Originally Released Under LGPL - original licence link has changed is not relivant.
34634 * <script type="text/javascript">
34638 * @class Roo.LayoutRegion
34639 * @extends Roo.BasicLayoutRegion
34640 * This class represents a region in a layout manager.
34641 * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
34642 * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
34643 * @cfg {Boolean} floatable False to disable floating (defaults to true)
34644 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34645 * @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})
34646 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
34647 * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
34648 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
34649 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
34650 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
34651 * @cfg {String} title The title for the region (overrides panel titles)
34652 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
34653 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34654 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
34655 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34656 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
34657 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34658 * the space available, similar to FireFox 1.5 tabs (defaults to false)
34659 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
34660 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
34661 * @cfg {Boolean} showPin True to show a pin button
34662 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
34663 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
34664 * @cfg {Boolean} disableTabTips True to disable tab tooltips
34665 * @cfg {Number} width For East/West panels
34666 * @cfg {Number} height For North/South panels
34667 * @cfg {Boolean} split To show the splitter
34668 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
34670 Roo.LayoutRegion = function(mgr, config, pos){
34671 Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
34672 var dh = Roo.DomHelper;
34673 /** This region's container element
34674 * @type Roo.Element */
34675 this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
34676 /** This region's title element
34677 * @type Roo.Element */
34679 this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
34680 {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: " "},
34681 {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
34683 this.titleEl.enableDisplayMode();
34684 /** This region's title text element
34685 * @type HTMLElement */
34686 this.titleTextEl = this.titleEl.dom.firstChild;
34687 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34688 this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
34689 this.closeBtn.enableDisplayMode();
34690 this.closeBtn.on("click", this.closeClicked, this);
34691 this.closeBtn.hide();
34693 this.createBody(config);
34694 this.visible = true;
34695 this.collapsed = false;
34697 if(config.hideWhenEmpty){
34699 this.on("paneladded", this.validateVisibility, this);
34700 this.on("panelremoved", this.validateVisibility, this);
34702 this.applyConfig(config);
34705 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
34707 createBody : function(){
34708 /** This region's body element
34709 * @type Roo.Element */
34710 this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
34713 applyConfig : function(c){
34714 if(c.collapsible && this.position != "center" && !this.collapsedEl){
34715 var dh = Roo.DomHelper;
34716 if(c.titlebar !== false){
34717 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
34718 this.collapseBtn.on("click", this.collapse, this);
34719 this.collapseBtn.enableDisplayMode();
34721 if(c.showPin === true || this.showPin){
34722 this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
34723 this.stickBtn.enableDisplayMode();
34724 this.stickBtn.on("click", this.expand, this);
34725 this.stickBtn.hide();
34728 /** This region's collapsed element
34729 * @type Roo.Element */
34730 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34731 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34733 if(c.floatable !== false){
34734 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34735 this.collapsedEl.on("click", this.collapseClick, this);
34738 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34739 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34740 id: "message", unselectable: "on", style:{"float":"left"}});
34741 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34743 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34744 this.expandBtn.on("click", this.expand, this);
34746 if(this.collapseBtn){
34747 this.collapseBtn.setVisible(c.collapsible == true);
34749 this.cmargins = c.cmargins || this.cmargins ||
34750 (this.position == "west" || this.position == "east" ?
34751 {top: 0, left: 2, right:2, bottom: 0} :
34752 {top: 2, left: 0, right:0, bottom: 2});
34753 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34754 this.bottomTabs = c.tabPosition != "top";
34755 this.autoScroll = c.autoScroll || false;
34756 if(this.autoScroll){
34757 this.bodyEl.setStyle("overflow", "auto");
34759 this.bodyEl.setStyle("overflow", "hidden");
34761 //if(c.titlebar !== false){
34762 if((!c.titlebar && !c.title) || c.titlebar === false){
34763 this.titleEl.hide();
34765 this.titleEl.show();
34767 this.titleTextEl.innerHTML = c.title;
34771 this.duration = c.duration || .30;
34772 this.slideDuration = c.slideDuration || .45;
34775 this.collapse(true);
34782 * Returns true if this region is currently visible.
34783 * @return {Boolean}
34785 isVisible : function(){
34786 return this.visible;
34790 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34791 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
34793 setCollapsedTitle : function(title){
34794 title = title || " ";
34795 if(this.collapsedTitleTextEl){
34796 this.collapsedTitleTextEl.innerHTML = title;
34800 getBox : function(){
34802 if(!this.collapsed){
34803 b = this.el.getBox(false, true);
34805 b = this.collapsedEl.getBox(false, true);
34810 getMargins : function(){
34811 return this.collapsed ? this.cmargins : this.margins;
34814 highlight : function(){
34815 this.el.addClass("x-layout-panel-dragover");
34818 unhighlight : function(){
34819 this.el.removeClass("x-layout-panel-dragover");
34822 updateBox : function(box){
34824 if(!this.collapsed){
34825 this.el.dom.style.left = box.x + "px";
34826 this.el.dom.style.top = box.y + "px";
34827 this.updateBody(box.width, box.height);
34829 this.collapsedEl.dom.style.left = box.x + "px";
34830 this.collapsedEl.dom.style.top = box.y + "px";
34831 this.collapsedEl.setSize(box.width, box.height);
34834 this.tabs.autoSizeTabs();
34838 updateBody : function(w, h){
34840 this.el.setWidth(w);
34841 w -= this.el.getBorderWidth("rl");
34842 if(this.config.adjustments){
34843 w += this.config.adjustments[0];
34847 this.el.setHeight(h);
34848 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34849 h -= this.el.getBorderWidth("tb");
34850 if(this.config.adjustments){
34851 h += this.config.adjustments[1];
34853 this.bodyEl.setHeight(h);
34855 h = this.tabs.syncHeight(h);
34858 if(this.panelSize){
34859 w = w !== null ? w : this.panelSize.width;
34860 h = h !== null ? h : this.panelSize.height;
34862 if(this.activePanel){
34863 var el = this.activePanel.getEl();
34864 w = w !== null ? w : el.getWidth();
34865 h = h !== null ? h : el.getHeight();
34866 this.panelSize = {width: w, height: h};
34867 this.activePanel.setSize(w, h);
34869 if(Roo.isIE && this.tabs){
34870 this.tabs.el.repaint();
34875 * Returns the container element for this region.
34876 * @return {Roo.Element}
34878 getEl : function(){
34883 * Hides this region.
34886 if(!this.collapsed){
34887 this.el.dom.style.left = "-2000px";
34890 this.collapsedEl.dom.style.left = "-2000px";
34891 this.collapsedEl.hide();
34893 this.visible = false;
34894 this.fireEvent("visibilitychange", this, false);
34898 * Shows this region if it was previously hidden.
34901 if(!this.collapsed){
34904 this.collapsedEl.show();
34906 this.visible = true;
34907 this.fireEvent("visibilitychange", this, true);
34910 closeClicked : function(){
34911 if(this.activePanel){
34912 this.remove(this.activePanel);
34916 collapseClick : function(e){
34918 e.stopPropagation();
34921 e.stopPropagation();
34927 * Collapses this region.
34928 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34930 collapse : function(skipAnim, skipCheck){
34931 if(this.collapsed) {
34935 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34937 this.collapsed = true;
34939 this.split.el.hide();
34941 if(this.config.animate && skipAnim !== true){
34942 this.fireEvent("invalidated", this);
34943 this.animateCollapse();
34945 this.el.setLocation(-20000,-20000);
34947 this.collapsedEl.show();
34948 this.fireEvent("collapsed", this);
34949 this.fireEvent("invalidated", this);
34955 animateCollapse : function(){
34960 * Expands this region if it was previously collapsed.
34961 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34962 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34964 expand : function(e, skipAnim){
34966 e.stopPropagation();
34968 if(!this.collapsed || this.el.hasActiveFx()) {
34972 this.afterSlideIn();
34975 this.collapsed = false;
34976 if(this.config.animate && skipAnim !== true){
34977 this.animateExpand();
34981 this.split.el.show();
34983 this.collapsedEl.setLocation(-2000,-2000);
34984 this.collapsedEl.hide();
34985 this.fireEvent("invalidated", this);
34986 this.fireEvent("expanded", this);
34990 animateExpand : function(){
34994 initTabs : function()
34996 this.bodyEl.setStyle("overflow", "hidden");
34997 var ts = new Roo.TabPanel(
35000 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35001 disableTooltips: this.config.disableTabTips,
35002 toolbar : this.config.toolbar
35005 if(this.config.hideTabs){
35006 ts.stripWrap.setDisplayed(false);
35009 ts.resizeTabs = this.config.resizeTabs === true;
35010 ts.minTabWidth = this.config.minTabWidth || 40;
35011 ts.maxTabWidth = this.config.maxTabWidth || 250;
35012 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35013 ts.monitorResize = false;
35014 ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35015 ts.bodyEl.addClass('x-layout-tabs-body');
35016 this.panels.each(this.initPanelAsTab, this);
35019 initPanelAsTab : function(panel){
35020 var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
35021 this.config.closeOnTab && panel.isClosable());
35022 if(panel.tabTip !== undefined){
35023 ti.setTooltip(panel.tabTip);
35025 ti.on("activate", function(){
35026 this.setActivePanel(panel);
35028 if(this.config.closeOnTab){
35029 ti.on("beforeclose", function(t, e){
35031 this.remove(panel);
35037 updatePanelTitle : function(panel, title){
35038 if(this.activePanel == panel){
35039 this.updateTitle(title);
35042 var ti = this.tabs.getTab(panel.getEl().id);
35044 if(panel.tabTip !== undefined){
35045 ti.setTooltip(panel.tabTip);
35050 updateTitle : function(title){
35051 if(this.titleTextEl && !this.config.title){
35052 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35056 setActivePanel : function(panel){
35057 panel = this.getPanel(panel);
35058 if(this.activePanel && this.activePanel != panel){
35059 this.activePanel.setActiveState(false);
35061 this.activePanel = panel;
35062 panel.setActiveState(true);
35063 if(this.panelSize){
35064 panel.setSize(this.panelSize.width, this.panelSize.height);
35067 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35069 this.updateTitle(panel.getTitle());
35071 this.fireEvent("invalidated", this);
35073 this.fireEvent("panelactivated", this, panel);
35077 * Shows the specified panel.
35078 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35079 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35081 showPanel : function(panel)
35083 panel = this.getPanel(panel);
35086 var tab = this.tabs.getTab(panel.getEl().id);
35087 if(tab.isHidden()){
35088 this.tabs.unhideTab(tab.id);
35092 this.setActivePanel(panel);
35099 * Get the active panel for this region.
35100 * @return {Roo.ContentPanel} The active panel or null
35102 getActivePanel : function(){
35103 return this.activePanel;
35106 validateVisibility : function(){
35107 if(this.panels.getCount() < 1){
35108 this.updateTitle(" ");
35109 this.closeBtn.hide();
35112 if(!this.isVisible()){
35119 * Adds the passed ContentPanel(s) to this region.
35120 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35121 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35123 add : function(panel){
35124 if(arguments.length > 1){
35125 for(var i = 0, len = arguments.length; i < len; i++) {
35126 this.add(arguments[i]);
35130 if(this.hasPanel(panel)){
35131 this.showPanel(panel);
35134 panel.setRegion(this);
35135 this.panels.add(panel);
35136 if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35137 this.bodyEl.dom.appendChild(panel.getEl().dom);
35138 if(panel.background !== true){
35139 this.setActivePanel(panel);
35141 this.fireEvent("paneladded", this, panel);
35147 this.initPanelAsTab(panel);
35149 if(panel.background !== true){
35150 this.tabs.activate(panel.getEl().id);
35152 this.fireEvent("paneladded", this, panel);
35157 * Hides the tab for the specified panel.
35158 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35160 hidePanel : function(panel){
35161 if(this.tabs && (panel = this.getPanel(panel))){
35162 this.tabs.hideTab(panel.getEl().id);
35167 * Unhides the tab for a previously hidden panel.
35168 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35170 unhidePanel : function(panel){
35171 if(this.tabs && (panel = this.getPanel(panel))){
35172 this.tabs.unhideTab(panel.getEl().id);
35176 clearPanels : function(){
35177 while(this.panels.getCount() > 0){
35178 this.remove(this.panels.first());
35183 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35184 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35185 * @param {Boolean} preservePanel Overrides the config preservePanel option
35186 * @return {Roo.ContentPanel} The panel that was removed
35188 remove : function(panel, preservePanel){
35189 panel = this.getPanel(panel);
35194 this.fireEvent("beforeremove", this, panel, e);
35195 if(e.cancel === true){
35198 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35199 var panelId = panel.getId();
35200 this.panels.removeKey(panelId);
35202 document.body.appendChild(panel.getEl().dom);
35205 this.tabs.removeTab(panel.getEl().id);
35206 }else if (!preservePanel){
35207 this.bodyEl.dom.removeChild(panel.getEl().dom);
35209 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35210 var p = this.panels.first();
35211 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35212 tempEl.appendChild(p.getEl().dom);
35213 this.bodyEl.update("");
35214 this.bodyEl.dom.appendChild(p.getEl().dom);
35216 this.updateTitle(p.getTitle());
35218 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35219 this.setActivePanel(p);
35221 panel.setRegion(null);
35222 if(this.activePanel == panel){
35223 this.activePanel = null;
35225 if(this.config.autoDestroy !== false && preservePanel !== true){
35226 try{panel.destroy();}catch(e){}
35228 this.fireEvent("panelremoved", this, panel);
35233 * Returns the TabPanel component used by this region
35234 * @return {Roo.TabPanel}
35236 getTabs : function(){
35240 createTool : function(parentEl, className){
35241 var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
35242 children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: " "}]}, true);
35243 btn.addClassOnOver("x-layout-tools-button-over");
35248 * Ext JS Library 1.1.1
35249 * Copyright(c) 2006-2007, Ext JS, LLC.
35251 * Originally Released Under LGPL - original licence link has changed is not relivant.
35254 * <script type="text/javascript">
35260 * @class Roo.SplitLayoutRegion
35261 * @extends Roo.LayoutRegion
35262 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35264 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
35265 this.cursor = cursor;
35266 Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
35269 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
35270 splitTip : "Drag to resize.",
35271 collapsibleSplitTip : "Drag to resize. Double click to hide.",
35272 useSplitTips : false,
35274 applyConfig : function(config){
35275 Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
35278 var splitEl = Roo.DomHelper.append(this.mgr.el.dom,
35279 {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: " "});
35280 /** The SplitBar for this region
35281 * @type Roo.SplitBar */
35282 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
35283 this.split.on("moved", this.onSplitMove, this);
35284 this.split.useShim = config.useShim === true;
35285 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35286 if(this.useSplitTips){
35287 this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35289 if(config.collapsible){
35290 this.split.el.on("dblclick", this.collapse, this);
35293 if(typeof config.minSize != "undefined"){
35294 this.split.minSize = config.minSize;
35296 if(typeof config.maxSize != "undefined"){
35297 this.split.maxSize = config.maxSize;
35299 if(config.hideWhenEmpty || config.hidden || config.collapsed){
35300 this.hideSplitter();
35305 getHMaxSize : function(){
35306 var cmax = this.config.maxSize || 10000;
35307 var center = this.mgr.getRegion("center");
35308 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35311 getVMaxSize : function(){
35312 var cmax = this.config.maxSize || 10000;
35313 var center = this.mgr.getRegion("center");
35314 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35317 onSplitMove : function(split, newSize){
35318 this.fireEvent("resized", this, newSize);
35322 * Returns the {@link Roo.SplitBar} for this region.
35323 * @return {Roo.SplitBar}
35325 getSplitBar : function(){
35330 this.hideSplitter();
35331 Roo.SplitLayoutRegion.superclass.hide.call(this);
35334 hideSplitter : function(){
35336 this.split.el.setLocation(-2000,-2000);
35337 this.split.el.hide();
35343 this.split.el.show();
35345 Roo.SplitLayoutRegion.superclass.show.call(this);
35348 beforeSlide: function(){
35349 if(Roo.isGecko){// firefox overflow auto bug workaround
35350 this.bodyEl.clip();
35352 this.tabs.bodyEl.clip();
35354 if(this.activePanel){
35355 this.activePanel.getEl().clip();
35357 if(this.activePanel.beforeSlide){
35358 this.activePanel.beforeSlide();
35364 afterSlide : function(){
35365 if(Roo.isGecko){// firefox overflow auto bug workaround
35366 this.bodyEl.unclip();
35368 this.tabs.bodyEl.unclip();
35370 if(this.activePanel){
35371 this.activePanel.getEl().unclip();
35372 if(this.activePanel.afterSlide){
35373 this.activePanel.afterSlide();
35379 initAutoHide : function(){
35380 if(this.autoHide !== false){
35381 if(!this.autoHideHd){
35382 var st = new Roo.util.DelayedTask(this.slideIn, this);
35383 this.autoHideHd = {
35384 "mouseout": function(e){
35385 if(!e.within(this.el, true)){
35389 "mouseover" : function(e){
35395 this.el.on(this.autoHideHd);
35399 clearAutoHide : function(){
35400 if(this.autoHide !== false){
35401 this.el.un("mouseout", this.autoHideHd.mouseout);
35402 this.el.un("mouseover", this.autoHideHd.mouseover);
35406 clearMonitor : function(){
35407 Roo.get(document).un("click", this.slideInIf, this);
35410 // these names are backwards but not changed for compat
35411 slideOut : function(){
35412 if(this.isSlid || this.el.hasActiveFx()){
35415 this.isSlid = true;
35416 if(this.collapseBtn){
35417 this.collapseBtn.hide();
35419 this.closeBtnState = this.closeBtn.getStyle('display');
35420 this.closeBtn.hide();
35422 this.stickBtn.show();
35425 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35426 this.beforeSlide();
35427 this.el.setStyle("z-index", 10001);
35428 this.el.slideIn(this.getSlideAnchor(), {
35429 callback: function(){
35431 this.initAutoHide();
35432 Roo.get(document).on("click", this.slideInIf, this);
35433 this.fireEvent("slideshow", this);
35440 afterSlideIn : function(){
35441 this.clearAutoHide();
35442 this.isSlid = false;
35443 this.clearMonitor();
35444 this.el.setStyle("z-index", "");
35445 if(this.collapseBtn){
35446 this.collapseBtn.show();
35448 this.closeBtn.setStyle('display', this.closeBtnState);
35450 this.stickBtn.hide();
35452 this.fireEvent("slidehide", this);
35455 slideIn : function(cb){
35456 if(!this.isSlid || this.el.hasActiveFx()){
35460 this.isSlid = false;
35461 this.beforeSlide();
35462 this.el.slideOut(this.getSlideAnchor(), {
35463 callback: function(){
35464 this.el.setLeftTop(-10000, -10000);
35466 this.afterSlideIn();
35474 slideInIf : function(e){
35475 if(!e.within(this.el)){
35480 animateCollapse : function(){
35481 this.beforeSlide();
35482 this.el.setStyle("z-index", 20000);
35483 var anchor = this.getSlideAnchor();
35484 this.el.slideOut(anchor, {
35485 callback : function(){
35486 this.el.setStyle("z-index", "");
35487 this.collapsedEl.slideIn(anchor, {duration:.3});
35489 this.el.setLocation(-10000,-10000);
35491 this.fireEvent("collapsed", this);
35498 animateExpand : function(){
35499 this.beforeSlide();
35500 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35501 this.el.setStyle("z-index", 20000);
35502 this.collapsedEl.hide({
35505 this.el.slideIn(this.getSlideAnchor(), {
35506 callback : function(){
35507 this.el.setStyle("z-index", "");
35510 this.split.el.show();
35512 this.fireEvent("invalidated", this);
35513 this.fireEvent("expanded", this);
35541 getAnchor : function(){
35542 return this.anchors[this.position];
35545 getCollapseAnchor : function(){
35546 return this.canchors[this.position];
35549 getSlideAnchor : function(){
35550 return this.sanchors[this.position];
35553 getAlignAdj : function(){
35554 var cm = this.cmargins;
35555 switch(this.position){
35571 getExpandAdj : function(){
35572 var c = this.collapsedEl, cm = this.cmargins;
35573 switch(this.position){
35575 return [-(cm.right+c.getWidth()+cm.left), 0];
35578 return [cm.right+c.getWidth()+cm.left, 0];
35581 return [0, -(cm.top+cm.bottom+c.getHeight())];
35584 return [0, cm.top+cm.bottom+c.getHeight()];
35590 * Ext JS Library 1.1.1
35591 * Copyright(c) 2006-2007, Ext JS, LLC.
35593 * Originally Released Under LGPL - original licence link has changed is not relivant.
35596 * <script type="text/javascript">
35599 * These classes are private internal classes
35601 Roo.CenterLayoutRegion = function(mgr, config){
35602 Roo.LayoutRegion.call(this, mgr, config, "center");
35603 this.visible = true;
35604 this.minWidth = config.minWidth || 20;
35605 this.minHeight = config.minHeight || 20;
35608 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
35610 // center panel can't be hidden
35614 // center panel can't be hidden
35617 getMinWidth: function(){
35618 return this.minWidth;
35621 getMinHeight: function(){
35622 return this.minHeight;
35627 Roo.NorthLayoutRegion = function(mgr, config){
35628 Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
35630 this.split.placement = Roo.SplitBar.TOP;
35631 this.split.orientation = Roo.SplitBar.VERTICAL;
35632 this.split.el.addClass("x-layout-split-v");
35634 var size = config.initialSize || config.height;
35635 if(typeof size != "undefined"){
35636 this.el.setHeight(size);
35639 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
35640 orientation: Roo.SplitBar.VERTICAL,
35641 getBox : function(){
35642 if(this.collapsed){
35643 return this.collapsedEl.getBox();
35645 var box = this.el.getBox();
35647 box.height += this.split.el.getHeight();
35652 updateBox : function(box){
35653 if(this.split && !this.collapsed){
35654 box.height -= this.split.el.getHeight();
35655 this.split.el.setLeft(box.x);
35656 this.split.el.setTop(box.y+box.height);
35657 this.split.el.setWidth(box.width);
35659 if(this.collapsed){
35660 this.updateBody(box.width, null);
35662 Roo.LayoutRegion.prototype.updateBox.call(this, box);
35666 Roo.SouthLayoutRegion = function(mgr, config){
35667 Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
35669 this.split.placement = Roo.SplitBar.BOTTOM;
35670 this.split.orientation = Roo.SplitBar.VERTICAL;
35671 this.split.el.addClass("x-layout-split-v");
35673 var size = config.initialSize || config.height;
35674 if(typeof size != "undefined"){
35675 this.el.setHeight(size);
35678 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
35679 orientation: Roo.SplitBar.VERTICAL,
35680 getBox : function(){
35681 if(this.collapsed){
35682 return this.collapsedEl.getBox();
35684 var box = this.el.getBox();
35686 var sh = this.split.el.getHeight();
35693 updateBox : function(box){
35694 if(this.split && !this.collapsed){
35695 var sh = this.split.el.getHeight();
35698 this.split.el.setLeft(box.x);
35699 this.split.el.setTop(box.y-sh);
35700 this.split.el.setWidth(box.width);
35702 if(this.collapsed){
35703 this.updateBody(box.width, null);
35705 Roo.LayoutRegion.prototype.updateBox.call(this, box);
35709 Roo.EastLayoutRegion = function(mgr, config){
35710 Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
35712 this.split.placement = Roo.SplitBar.RIGHT;
35713 this.split.orientation = Roo.SplitBar.HORIZONTAL;
35714 this.split.el.addClass("x-layout-split-h");
35716 var size = config.initialSize || config.width;
35717 if(typeof size != "undefined"){
35718 this.el.setWidth(size);
35721 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
35722 orientation: Roo.SplitBar.HORIZONTAL,
35723 getBox : function(){
35724 if(this.collapsed){
35725 return this.collapsedEl.getBox();
35727 var box = this.el.getBox();
35729 var sw = this.split.el.getWidth();
35736 updateBox : function(box){
35737 if(this.split && !this.collapsed){
35738 var sw = this.split.el.getWidth();
35740 this.split.el.setLeft(box.x);
35741 this.split.el.setTop(box.y);
35742 this.split.el.setHeight(box.height);
35745 if(this.collapsed){
35746 this.updateBody(null, box.height);
35748 Roo.LayoutRegion.prototype.updateBox.call(this, box);
35752 Roo.WestLayoutRegion = function(mgr, config){
35753 Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
35755 this.split.placement = Roo.SplitBar.LEFT;
35756 this.split.orientation = Roo.SplitBar.HORIZONTAL;
35757 this.split.el.addClass("x-layout-split-h");
35759 var size = config.initialSize || config.width;
35760 if(typeof size != "undefined"){
35761 this.el.setWidth(size);
35764 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
35765 orientation: Roo.SplitBar.HORIZONTAL,
35766 getBox : function(){
35767 if(this.collapsed){
35768 return this.collapsedEl.getBox();
35770 var box = this.el.getBox();
35772 box.width += this.split.el.getWidth();
35777 updateBox : function(box){
35778 if(this.split && !this.collapsed){
35779 var sw = this.split.el.getWidth();
35781 this.split.el.setLeft(box.x+box.width);
35782 this.split.el.setTop(box.y);
35783 this.split.el.setHeight(box.height);
35785 if(this.collapsed){
35786 this.updateBody(null, box.height);
35788 Roo.LayoutRegion.prototype.updateBox.call(this, box);
35793 * Ext JS Library 1.1.1
35794 * Copyright(c) 2006-2007, Ext JS, LLC.
35796 * Originally Released Under LGPL - original licence link has changed is not relivant.
35799 * <script type="text/javascript">
35804 * Private internal class for reading and applying state
35806 Roo.LayoutStateManager = function(layout){
35807 // default empty state
35816 Roo.LayoutStateManager.prototype = {
35817 init : function(layout, provider){
35818 this.provider = provider;
35819 var state = provider.get(layout.id+"-layout-state");
35821 var wasUpdating = layout.isUpdating();
35823 layout.beginUpdate();
35825 for(var key in state){
35826 if(typeof state[key] != "function"){
35827 var rstate = state[key];
35828 var r = layout.getRegion(key);
35831 r.resizeTo(rstate.size);
35833 if(rstate.collapsed == true){
35836 r.expand(null, true);
35842 layout.endUpdate();
35844 this.state = state;
35846 this.layout = layout;
35847 layout.on("regionresized", this.onRegionResized, this);
35848 layout.on("regioncollapsed", this.onRegionCollapsed, this);
35849 layout.on("regionexpanded", this.onRegionExpanded, this);
35852 storeState : function(){
35853 this.provider.set(this.layout.id+"-layout-state", this.state);
35856 onRegionResized : function(region, newSize){
35857 this.state[region.getPosition()].size = newSize;
35861 onRegionCollapsed : function(region){
35862 this.state[region.getPosition()].collapsed = true;
35866 onRegionExpanded : function(region){
35867 this.state[region.getPosition()].collapsed = false;
35872 * Ext JS Library 1.1.1
35873 * Copyright(c) 2006-2007, Ext JS, LLC.
35875 * Originally Released Under LGPL - original licence link has changed is not relivant.
35878 * <script type="text/javascript">
35881 * @class Roo.ContentPanel
35882 * @extends Roo.util.Observable
35883 * @children Roo.form.Form Roo.JsonView Roo.View
35884 * @parent Roo.BorderLayout Roo.LayoutDialog builder
35885 * A basic ContentPanel element.
35886 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
35887 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
35888 * @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
35889 * @cfg {Boolean} closable True if the panel can be closed/removed
35890 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
35891 * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35892 * @cfg {Roo.Toolbar} toolbar A toolbar for this panel
35893 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
35894 * @cfg {String} title The title for this panel
35895 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35896 * @cfg {String} url Calls {@link #setUrl} with this value
35897 * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
35898 * @cfg {String|Object} params When used with {@link #url}, calls {@link #setUrl} with this value
35899 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
35900 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
35901 * @cfg {String} style Extra style to add to the content panel
35902 * @cfg {Roo.menu.Menu} menu popup menu
35905 * Create a new ContentPanel.
35906 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35907 * @param {String/Object} config A string to set only the title or a config object
35908 * @param {String} content (optional) Set the HTML content for this panel
35909 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35911 Roo.ContentPanel = function(el, config, content){
35915 if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
35919 if (config && config.parentLayout) {
35920 el = config.parentLayout.el.createChild();
35923 if(el.autoCreate){ // xtype is available if this is called from factory
35927 this.el = Roo.get(el);
35928 if(!this.el && config && config.autoCreate){
35929 if(typeof config.autoCreate == "object"){
35930 if(!config.autoCreate.id){
35931 config.autoCreate.id = config.id||el;
35933 this.el = Roo.DomHelper.append(document.body,
35934 config.autoCreate, true);
35936 this.el = Roo.DomHelper.append(document.body,
35937 {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
35942 this.closable = false;
35943 this.loaded = false;
35944 this.active = false;
35945 if(typeof config == "string"){
35946 this.title = config;
35948 Roo.apply(this, config);
35951 if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
35952 this.wrapEl = this.el.wrap();
35953 this.toolbar.container = this.el.insertSibling(false, 'before');
35954 this.toolbar = new Roo.Toolbar(this.toolbar);
35957 // xtype created footer. - not sure if will work as we normally have to render first..
35958 if (this.footer && !this.footer.el && this.footer.xtype) {
35959 if (!this.wrapEl) {
35960 this.wrapEl = this.el.wrap();
35963 this.footer.container = this.wrapEl.createChild();
35965 this.footer = Roo.factory(this.footer, Roo);
35970 this.resizeEl = Roo.get(this.resizeEl, true);
35972 this.resizeEl = this.el;
35974 // handle view.xtype
35982 * Fires when this panel is activated.
35983 * @param {Roo.ContentPanel} this
35987 * @event deactivate
35988 * Fires when this panel is activated.
35989 * @param {Roo.ContentPanel} this
35991 "deactivate" : true,
35995 * Fires when this panel is resized if fitToFrame is true.
35996 * @param {Roo.ContentPanel} this
35997 * @param {Number} width The width after any component adjustments
35998 * @param {Number} height The height after any component adjustments
36004 * Fires when this tab is created
36005 * @param {Roo.ContentPanel} this
36015 if(this.autoScroll){
36016 this.resizeEl.setStyle("overflow", "auto");
36018 // fix randome scrolling
36019 this.el.on('scroll', function() {
36020 Roo.log('fix random scolling');
36021 this.scrollTo('top',0);
36024 content = content || this.content;
36026 this.setContent(content);
36028 if(config && config.url){
36029 this.setUrl(this.url, this.params, this.loadOnce);
36034 Roo.ContentPanel.superclass.constructor.call(this);
36036 if (this.view && typeof(this.view.xtype) != 'undefined') {
36037 this.view.el = this.el.appendChild(document.createElement("div"));
36038 this.view = Roo.factory(this.view);
36039 this.view.render && this.view.render(false, '');
36043 this.fireEvent('render', this);
36046 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
36048 setRegion : function(region){
36049 this.region = region;
36051 this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
36053 this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
36058 * Returns the toolbar for this Panel if one was configured.
36059 * @return {Roo.Toolbar}
36061 getToolbar : function(){
36062 return this.toolbar;
36065 setActiveState : function(active){
36066 this.active = active;
36068 this.fireEvent("deactivate", this);
36070 this.fireEvent("activate", this);
36074 * Updates this panel's element
36075 * @param {String} content The new content
36076 * @param {Boolean} loadScripts (optional) true to look for and process scripts
36078 setContent : function(content, loadScripts){
36079 this.el.update(content, loadScripts);
36082 ignoreResize : function(w, h){
36083 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36086 this.lastSize = {width: w, height: h};
36091 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36092 * @return {Roo.UpdateManager} The UpdateManager
36094 getUpdateManager : function(){
36095 return this.el.getUpdateManager();
36098 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36099 * @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:
36102 url: "your-url.php",
36103 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36104 callback: yourFunction,
36105 scope: yourObject, //(optional scope)
36108 text: "Loading...",
36113 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36114 * 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.
36115 * @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}
36116 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36117 * @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.
36118 * @return {Roo.ContentPanel} this
36121 var um = this.el.getUpdateManager();
36122 um.update.apply(um, arguments);
36128 * 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.
36129 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36130 * @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)
36131 * @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)
36132 * @return {Roo.UpdateManager} The UpdateManager
36134 setUrl : function(url, params, loadOnce){
36135 if(this.refreshDelegate){
36136 this.removeListener("activate", this.refreshDelegate);
36138 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36139 this.on("activate", this.refreshDelegate);
36140 return this.el.getUpdateManager();
36143 _handleRefresh : function(url, params, loadOnce){
36144 if(!loadOnce || !this.loaded){
36145 var updater = this.el.getUpdateManager();
36146 updater.update(url, params, this._setLoaded.createDelegate(this));
36150 _setLoaded : function(){
36151 this.loaded = true;
36155 * Returns this panel's id
36158 getId : function(){
36163 * Returns this panel's element - used by regiosn to add.
36164 * @return {Roo.Element}
36166 getEl : function(){
36167 return this.wrapEl || this.el;
36170 adjustForComponents : function(width, height)
36172 //Roo.log('adjustForComponents ');
36173 if(this.resizeEl != this.el){
36174 width -= this.el.getFrameWidth('lr');
36175 height -= this.el.getFrameWidth('tb');
36178 var te = this.toolbar.getEl();
36179 height -= te.getHeight();
36180 te.setWidth(width);
36183 var te = this.footer.getEl();
36184 //Roo.log("footer:" + te.getHeight());
36186 height -= te.getHeight();
36187 te.setWidth(width);
36191 if(this.adjustments){
36192 width += this.adjustments[0];
36193 height += this.adjustments[1];
36195 return {"width": width, "height": height};
36198 setSize : function(width, height){
36199 if(this.fitToFrame && !this.ignoreResize(width, height)){
36200 if(this.fitContainer && this.resizeEl != this.el){
36201 this.el.setSize(width, height);
36203 var size = this.adjustForComponents(width, height);
36204 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36205 this.fireEvent('resize', this, size.width, size.height);
36210 * Returns this panel's title
36213 getTitle : function(){
36218 * Set this panel's title
36219 * @param {String} title
36221 setTitle : function(title){
36222 this.title = title;
36224 this.region.updatePanelTitle(this, title);
36229 * Returns true is this panel was configured to be closable
36230 * @return {Boolean}
36232 isClosable : function(){
36233 return this.closable;
36236 beforeSlide : function(){
36238 this.resizeEl.clip();
36241 afterSlide : function(){
36243 this.resizeEl.unclip();
36247 * Force a content refresh from the URL specified in the {@link #setUrl} method.
36248 * Will fail silently if the {@link #setUrl} method has not been called.
36249 * This does not activate the panel, just updates its content.
36251 refresh : function(){
36252 if(this.refreshDelegate){
36253 this.loaded = false;
36254 this.refreshDelegate();
36259 * Destroys this panel
36261 destroy : function(){
36262 this.el.removeAllListeners();
36263 var tempEl = document.createElement("span");
36264 tempEl.appendChild(this.el.dom);
36265 tempEl.innerHTML = "";
36271 * form - if the content panel contains a form - this is a reference to it.
36272 * @type {Roo.form.Form}
36276 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36277 * This contains a reference to it.
36283 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36293 * @param {Object} cfg Xtype definition of item to add.
36296 addxtype : function(cfg) {
36298 if (cfg.xtype.match(/^Form$/)) {
36301 //if (this.footer) {
36302 // el = this.footer.container.insertSibling(false, 'before');
36304 el = this.el.createChild();
36307 this.form = new Roo.form.Form(cfg);
36310 if ( this.form.allItems.length) {
36311 this.form.render(el.dom);
36315 // should only have one of theses..
36316 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36317 // views.. should not be just added - used named prop 'view''
36319 cfg.el = this.el.appendChild(document.createElement("div"));
36322 var ret = new Roo.factory(cfg);
36324 ret.render && ret.render(false, ''); // render blank..
36344 * @class Roo.GridPanel
36345 * @extends Roo.ContentPanel
36346 * @parent Roo.BorderLayout Roo.LayoutDialog builder
36348 * Create a new GridPanel.
36349 * @cfg {Roo.grid.Grid} grid The grid for this panel
36351 Roo.GridPanel = function(grid, config){
36353 // universal ctor...
36354 if (typeof(grid.grid) != 'undefined') {
36356 grid = config.grid;
36358 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36359 {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
36361 this.wrapper.dom.appendChild(grid.getGridEl().dom);
36363 Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
36366 this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
36368 // xtype created footer. - not sure if will work as we normally have to render first..
36369 if (this.footer && !this.footer.el && this.footer.xtype) {
36371 this.footer.container = this.grid.getView().getFooterPanel(true);
36372 this.footer.dataSource = this.grid.dataSource;
36373 this.footer = Roo.factory(this.footer, Roo);
36377 grid.monitorWindowResize = false; // turn off autosizing
36378 grid.autoHeight = false;
36379 grid.autoWidth = false;
36381 this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
36384 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
36385 getId : function(){
36386 return this.grid.id;
36390 * Returns the grid for this panel
36391 * @return {Roo.grid.Grid}
36393 getGrid : function(){
36397 setSize : function(width, height){
36398 if(!this.ignoreResize(width, height)){
36399 var grid = this.grid;
36400 var size = this.adjustForComponents(width, height);
36401 grid.getGridEl().setSize(size.width, size.height);
36406 beforeSlide : function(){
36407 this.grid.getView().scroller.clip();
36410 afterSlide : function(){
36411 this.grid.getView().scroller.unclip();
36414 destroy : function(){
36415 this.grid.destroy();
36417 Roo.GridPanel.superclass.destroy.call(this);
36423 * @class Roo.NestedLayoutPanel
36424 * @extends Roo.ContentPanel
36425 * @parent Roo.BorderLayout Roo.LayoutDialog builder
36426 * @cfg {Roo.BorderLayout} layout [required] The layout for this panel
36430 * Create a new NestedLayoutPanel.
36433 * @param {Roo.BorderLayout} layout [required] The layout for this panel
36434 * @param {String/Object} config A string to set only the title or a config object
36436 Roo.NestedLayoutPanel = function(layout, config)
36438 // construct with only one argument..
36439 /* FIXME - implement nicer consturctors
36440 if (layout.layout) {
36442 layout = config.layout;
36443 delete config.layout;
36445 if (layout.xtype && !layout.getEl) {
36446 // then layout needs constructing..
36447 layout = Roo.factory(layout, Roo);
36452 Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
36454 layout.monitorWindowResize = false; // turn off autosizing
36455 this.layout = layout;
36456 this.layout.getEl().addClass("x-layout-nested-layout");
36463 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
36467 setSize : function(width, height){
36468 if(!this.ignoreResize(width, height)){
36469 var size = this.adjustForComponents(width, height);
36470 var el = this.layout.getEl();
36471 el.setSize(size.width, size.height);
36472 var touch = el.dom.offsetWidth;
36473 this.layout.layout();
36474 // ie requires a double layout on the first pass
36475 if(Roo.isIE && !this.initialized){
36476 this.initialized = true;
36477 this.layout.layout();
36482 // activate all subpanels if not currently active..
36484 setActiveState : function(active){
36485 this.active = active;
36487 this.fireEvent("deactivate", this);
36491 this.fireEvent("activate", this);
36492 // not sure if this should happen before or after..
36493 if (!this.layout) {
36494 return; // should not happen..
36497 for (var r in this.layout.regions) {
36498 reg = this.layout.getRegion(r);
36499 if (reg.getActivePanel()) {
36500 //reg.showPanel(reg.getActivePanel()); // force it to activate..
36501 reg.setActivePanel(reg.getActivePanel());
36504 if (!reg.panels.length) {
36507 reg.showPanel(reg.getPanel(0));
36516 * Returns the nested BorderLayout for this panel
36517 * @return {Roo.BorderLayout}
36519 getLayout : function(){
36520 return this.layout;
36524 * Adds a xtype elements to the layout of the nested panel
36528 xtype : 'ContentPanel',
36535 xtype : 'NestedLayoutPanel',
36541 items : [ ... list of content panels or nested layout panels.. ]
36545 * @param {Object} cfg Xtype definition of item to add.
36547 addxtype : function(cfg) {
36548 return this.layout.addxtype(cfg);
36553 Roo.ScrollPanel = function(el, config, content){
36554 config = config || {};
36555 config.fitToFrame = true;
36556 Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
36558 this.el.dom.style.overflow = "hidden";
36559 var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
36560 this.el.removeClass("x-layout-inactive-content");
36561 this.el.on("mousewheel", this.onWheel, this);
36563 var up = wrap.createChild({cls: "x-scroller-up", html: " "}, this.el.dom);
36564 var down = wrap.createChild({cls: "x-scroller-down", html: " "});
36565 up.unselectable(); down.unselectable();
36566 up.on("click", this.scrollUp, this);
36567 down.on("click", this.scrollDown, this);
36568 up.addClassOnOver("x-scroller-btn-over");
36569 down.addClassOnOver("x-scroller-btn-over");
36570 up.addClassOnClick("x-scroller-btn-click");
36571 down.addClassOnClick("x-scroller-btn-click");
36572 this.adjustments = [0, -(up.getHeight() + down.getHeight())];
36574 this.resizeEl = this.el;
36575 this.el = wrap; this.up = up; this.down = down;
36578 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
36580 wheelIncrement : 5,
36581 scrollUp : function(){
36582 this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
36585 scrollDown : function(){
36586 this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
36589 afterScroll : function(){
36590 var el = this.resizeEl;
36591 var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
36592 this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36593 this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
36596 setSize : function(){
36597 Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
36598 this.afterScroll();
36601 onWheel : function(e){
36602 var d = e.getWheelDelta();
36603 this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
36604 this.afterScroll();
36608 setContent : function(content, loadScripts){
36609 this.resizeEl.update(content, loadScripts);
36617 * @class Roo.TreePanel
36618 * @extends Roo.ContentPanel
36619 * @parent Roo.BorderLayout Roo.LayoutDialog builder
36620 * Treepanel component
36623 * Create a new TreePanel. - defaults to fit/scoll contents.
36624 * @param {String/Object} config A string to set only the panel's title, or a config object
36626 Roo.TreePanel = function(config){
36627 var el = config.el;
36628 var tree = config.tree;
36629 delete config.tree;
36630 delete config.el; // hopefull!
36632 // wrapper for IE7 strict & safari scroll issue
36634 var treeEl = el.createChild();
36635 config.resizeEl = treeEl;
36639 Roo.TreePanel.superclass.constructor.call(this, el, config);
36642 this.tree = new Roo.tree.TreePanel(treeEl , tree);
36643 //console.log(tree);
36644 this.on('activate', function()
36646 if (this.tree.rendered) {
36649 //console.log('render tree');
36650 this.tree.render();
36652 // this should not be needed.. - it's actually the 'el' that resizes?
36653 // actuall it breaks the containerScroll - dragging nodes auto scroll at top
36655 //this.on('resize', function (cp, w, h) {
36656 // this.tree.innerCt.setWidth(w);
36657 // this.tree.innerCt.setHeight(h);
36658 // //this.tree.innerCt.setStyle('overflow-y', 'auto');
36665 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {
36669 * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
36676 * Ext JS Library 1.1.1
36677 * Copyright(c) 2006-2007, Ext JS, LLC.
36679 * Originally Released Under LGPL - original licence link has changed is not relivant.
36682 * <script type="text/javascript">
36687 * @class Roo.ReaderLayout
36688 * @extends Roo.BorderLayout
36689 * This is a pre-built layout that represents a classic, 5-pane application. It consists of a header, a primary
36690 * center region containing two nested regions (a top one for a list view and one for item preview below),
36691 * and regions on either side that can be used for navigation, application commands, informational displays, etc.
36692 * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
36693 * expedites the setup of the overall layout and regions for this common application style.
36696 var reader = new Roo.ReaderLayout();
36697 var CP = Roo.ContentPanel; // shortcut for adding
36699 reader.beginUpdate();
36700 reader.add("north", new CP("north", "North"));
36701 reader.add("west", new CP("west", {title: "West"}));
36702 reader.add("east", new CP("east", {title: "East"}));
36704 reader.regions.listView.add(new CP("listView", "List"));
36705 reader.regions.preview.add(new CP("preview", "Preview"));
36706 reader.endUpdate();
36709 * Create a new ReaderLayout
36710 * @param {Object} config Configuration options
36711 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
36712 * document.body if omitted)
36714 Roo.ReaderLayout = function(config, renderTo){
36715 var c = config || {size:{}};
36716 Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
36717 north: c.north !== false ? Roo.apply({
36721 }, c.north) : false,
36722 west: c.west !== false ? Roo.apply({
36730 margins:{left:5,right:0,bottom:5,top:5},
36731 cmargins:{left:5,right:5,bottom:5,top:5}
36732 }, c.west) : false,
36733 east: c.east !== false ? Roo.apply({
36741 margins:{left:0,right:5,bottom:5,top:5},
36742 cmargins:{left:5,right:5,bottom:5,top:5}
36743 }, c.east) : false,
36744 center: Roo.apply({
36745 tabPosition: 'top',
36749 margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
36753 this.el.addClass('x-reader');
36755 this.beginUpdate();
36757 var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
36758 south: c.preview !== false ? Roo.apply({
36765 cmargins:{top:5,left:0, right:0, bottom:0}
36766 }, c.preview) : false,
36767 center: Roo.apply({
36773 this.add('center', new Roo.NestedLayoutPanel(inner,
36774 Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
36778 this.regions.preview = inner.getRegion('south');
36779 this.regions.listView = inner.getRegion('center');
36782 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
36784 * Ext JS Library 1.1.1
36785 * Copyright(c) 2006-2007, Ext JS, LLC.
36787 * Originally Released Under LGPL - original licence link has changed is not relivant.
36790 * <script type="text/javascript">
36794 * @class Roo.grid.Grid
36795 * @extends Roo.util.Observable
36796 * This class represents the primary interface of a component based grid control.
36797 * <br><br>Usage:<pre><code>
36798 var grid = new Roo.grid.Grid("my-container-id", {
36801 selModel: mySelectionModel,
36802 autoSizeColumns: true,
36803 monitorWindowResize: false,
36804 trackMouseOver: true
36809 * <b>Common Problems:</b><br/>
36810 * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
36811 * element will correct this<br/>
36812 * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
36813 * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
36814 * are unpredictable.<br/>
36815 * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
36816 * grid to calculate dimensions/offsets.<br/>
36818 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36819 * The container MUST have some type of size defined for the grid to fill. The container will be
36820 * automatically set to position relative if it isn't already.
36821 * @param {Object} config A config object that sets properties on this grid.
36823 Roo.grid.Grid = function(container, config){
36824 // initialize the container
36825 this.container = Roo.get(container);
36826 this.container.update("");
36827 this.container.setStyle("overflow", "hidden");
36828 this.container.addClass('x-grid-container');
36830 this.id = this.container.id;
36832 Roo.apply(this, config);
36833 // check and correct shorthanded configs
36835 this.dataSource = this.ds;
36839 this.colModel = this.cm;
36843 this.selModel = this.sm;
36847 if (this.selModel) {
36848 this.selModel = Roo.factory(this.selModel, Roo.grid);
36849 this.sm = this.selModel;
36850 this.sm.xmodule = this.xmodule || false;
36852 if (typeof(this.colModel.config) == 'undefined') {
36853 this.colModel = new Roo.grid.ColumnModel(this.colModel);
36854 this.cm = this.colModel;
36855 this.cm.xmodule = this.xmodule || false;
36857 if (this.dataSource) {
36858 this.dataSource= Roo.factory(this.dataSource, Roo.data);
36859 this.ds = this.dataSource;
36860 this.ds.xmodule = this.xmodule || false;
36867 this.container.setWidth(this.width);
36871 this.container.setHeight(this.height);
36878 * The raw click event for the entire grid.
36879 * @param {Roo.EventObject} e
36884 * The raw dblclick event for the entire grid.
36885 * @param {Roo.EventObject} e
36889 * @event contextmenu
36890 * The raw contextmenu event for the entire grid.
36891 * @param {Roo.EventObject} e
36893 "contextmenu" : true,
36896 * The raw mousedown event for the entire grid.
36897 * @param {Roo.EventObject} e
36899 "mousedown" : true,
36902 * The raw mouseup event for the entire grid.
36903 * @param {Roo.EventObject} e
36908 * The raw mouseover event for the entire grid.
36909 * @param {Roo.EventObject} e
36911 "mouseover" : true,
36914 * The raw mouseout event for the entire grid.
36915 * @param {Roo.EventObject} e
36920 * The raw keypress event for the entire grid.
36921 * @param {Roo.EventObject} e
36926 * The raw keydown event for the entire grid.
36927 * @param {Roo.EventObject} e
36935 * Fires when a cell is clicked
36936 * @param {Grid} this
36937 * @param {Number} rowIndex
36938 * @param {Number} columnIndex
36939 * @param {Roo.EventObject} e
36941 "cellclick" : true,
36943 * @event celldblclick
36944 * Fires when a cell is double clicked
36945 * @param {Grid} this
36946 * @param {Number} rowIndex
36947 * @param {Number} columnIndex
36948 * @param {Roo.EventObject} e
36950 "celldblclick" : true,
36953 * Fires when a row is clicked
36954 * @param {Grid} this
36955 * @param {Number} rowIndex
36956 * @param {Roo.EventObject} e
36960 * @event rowdblclick
36961 * Fires when a row is double clicked
36962 * @param {Grid} this
36963 * @param {Number} rowIndex
36964 * @param {Roo.EventObject} e
36966 "rowdblclick" : true,
36968 * @event headerclick
36969 * Fires when a header is clicked
36970 * @param {Grid} this
36971 * @param {Number} columnIndex
36972 * @param {Roo.EventObject} e
36974 "headerclick" : true,
36976 * @event headerdblclick
36977 * Fires when a header cell is double clicked
36978 * @param {Grid} this
36979 * @param {Number} columnIndex
36980 * @param {Roo.EventObject} e
36982 "headerdblclick" : true,
36984 * @event rowcontextmenu
36985 * Fires when a row is right clicked
36986 * @param {Grid} this
36987 * @param {Number} rowIndex
36988 * @param {Roo.EventObject} e
36990 "rowcontextmenu" : true,
36992 * @event cellcontextmenu
36993 * Fires when a cell is right clicked
36994 * @param {Grid} this
36995 * @param {Number} rowIndex
36996 * @param {Number} cellIndex
36997 * @param {Roo.EventObject} e
36999 "cellcontextmenu" : true,
37001 * @event headercontextmenu
37002 * Fires when a header is right clicked
37003 * @param {Grid} this
37004 * @param {Number} columnIndex
37005 * @param {Roo.EventObject} e
37007 "headercontextmenu" : true,
37009 * @event bodyscroll
37010 * Fires when the body element is scrolled
37011 * @param {Number} scrollLeft
37012 * @param {Number} scrollTop
37014 "bodyscroll" : true,
37016 * @event columnresize
37017 * Fires when the user resizes a column
37018 * @param {Number} columnIndex
37019 * @param {Number} newSize
37021 "columnresize" : true,
37023 * @event columnmove
37024 * Fires when the user moves a column
37025 * @param {Number} oldIndex
37026 * @param {Number} newIndex
37028 "columnmove" : true,
37031 * Fires when row(s) start being dragged
37032 * @param {Grid} this
37033 * @param {Roo.GridDD} dd The drag drop object
37034 * @param {event} e The raw browser event
37036 "startdrag" : true,
37039 * Fires when a drag operation is complete
37040 * @param {Grid} this
37041 * @param {Roo.GridDD} dd The drag drop object
37042 * @param {event} e The raw browser event
37047 * Fires when dragged row(s) are dropped on a valid DD target
37048 * @param {Grid} this
37049 * @param {Roo.GridDD} dd The drag drop object
37050 * @param {String} targetId The target drag drop object
37051 * @param {event} e The raw browser event
37056 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37057 * @param {Grid} this
37058 * @param {Roo.GridDD} dd The drag drop object
37059 * @param {String} targetId The target drag drop object
37060 * @param {event} e The raw browser event
37065 * Fires when the dragged row(s) first cross another DD target while being dragged
37066 * @param {Grid} this
37067 * @param {Roo.GridDD} dd The drag drop object
37068 * @param {String} targetId The target drag drop object
37069 * @param {event} e The raw browser event
37071 "dragenter" : true,
37074 * Fires when the dragged row(s) leave another DD target while being dragged
37075 * @param {Grid} this
37076 * @param {Roo.GridDD} dd The drag drop object
37077 * @param {String} targetId The target drag drop object
37078 * @param {event} e The raw browser event
37083 * Fires when a row is rendered, so you can change add a style to it.
37084 * @param {GridView} gridview The grid view
37085 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
37091 * Fires when the grid is rendered
37092 * @param {Grid} grid
37097 Roo.grid.Grid.superclass.constructor.call(this);
37099 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
37102 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
37105 * @cfg {Roo.grid.GridView} view The view that renders the grid (default = Roo.grid.GridView)
37108 * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
37111 * @cfg {Roo.data.Store} ds The data store for the grid
37114 * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
37117 * @cfg {String} ddGroup - drag drop group.
37120 * @cfg {String} dragGroup - drag group (?? not sure if needed.)
37124 * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
37126 minColumnWidth : 25,
37129 * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
37130 * <b>on initial render.</b> It is more efficient to explicitly size the columns
37131 * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option. Default is false.
37133 autoSizeColumns : false,
37136 * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
37138 autoSizeHeaders : true,
37141 * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
37143 monitorWindowResize : true,
37146 * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
37147 * rows measured to get a columns size. Default is 0 (all rows).
37149 maxRowsToMeasure : 0,
37152 * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
37154 trackMouseOver : true,
37157 * @cfg {Boolean} enableDrag True to enable drag of rows. Default is false. (double check if this is needed?)
37160 * @cfg {Boolean} enableDrop True to enable drop of elements. Default is false. (double check if this is needed?)
37164 * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
37166 enableDragDrop : false,
37169 * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
37171 enableColumnMove : true,
37174 * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
37176 enableColumnHide : true,
37179 * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
37181 enableRowHeightSync : false,
37184 * @cfg {Boolean} stripeRows True to stripe the rows. Default is true.
37189 * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
37191 autoHeight : false,
37194 * @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.
37196 autoExpandColumn : false,
37199 * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
37202 autoExpandMin : 50,
37205 * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
37207 autoExpandMax : 1000,
37210 * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
37215 * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
37219 * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
37223 * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
37225 sortColMenu : false,
37231 * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
37232 * of a fixed width. Default is false.
37235 * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
37240 * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
37241 * %0 is replaced with the number of selected rows.
37243 ddText : "{0} selected row{1}",
37247 * Called once after all setup has been completed and the grid is ready to be rendered.
37248 * @return {Roo.grid.Grid} this
37250 render : function()
37252 var c = this.container;
37253 // try to detect autoHeight/width mode
37254 if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
37255 this.autoHeight = true;
37257 var view = this.getView();
37260 c.on("click", this.onClick, this);
37261 c.on("dblclick", this.onDblClick, this);
37262 c.on("contextmenu", this.onContextMenu, this);
37263 c.on("keydown", this.onKeyDown, this);
37265 c.on("touchstart", this.onTouchStart, this);
37268 this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
37270 this.getSelectionModel().init(this);
37275 this.loadMask = new Roo.LoadMask(this.container,
37276 Roo.apply({store:this.dataSource}, this.loadMask));
37280 if (this.toolbar && this.toolbar.xtype) {
37281 this.toolbar.container = this.getView().getHeaderPanel(true);
37282 this.toolbar = new Roo.Toolbar(this.toolbar);
37284 if (this.footer && this.footer.xtype) {
37285 this.footer.dataSource = this.getDataSource();
37286 this.footer.container = this.getView().getFooterPanel(true);
37287 this.footer = Roo.factory(this.footer, Roo);
37289 if (this.dropTarget && this.dropTarget.xtype) {
37290 delete this.dropTarget.xtype;
37291 this.dropTarget = new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
37295 this.rendered = true;
37296 this.fireEvent('render', this);
37301 * Reconfigures the grid to use a different Store and Column Model.
37302 * The View will be bound to the new objects and refreshed.
37303 * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
37304 * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
37306 reconfigure : function(dataSource, colModel){
37308 this.loadMask.destroy();
37309 this.loadMask = new Roo.LoadMask(this.container,
37310 Roo.apply({store:dataSource}, this.loadMask));
37312 this.view.bind(dataSource, colModel);
37313 this.dataSource = dataSource;
37314 this.colModel = colModel;
37315 this.view.refresh(true);
37319 * Add's a column, default at the end..
37321 * @param {int} position to add (default end)
37322 * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel}
37324 addColumns : function(pos, ar)
37327 for (var i =0;i< ar.length;i++) {
37329 cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
37330 this.cm.lookup[cfg.id] = cfg;
37334 if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
37335 pos = this.cm.config.length; //this.cm.config.push(cfg);
37337 pos = Math.max(0,pos);
37340 this.cm.config.splice.apply(this.cm.config, ar);
37344 this.view.generateRules(this.cm);
37345 this.view.refresh(true);
37353 onKeyDown : function(e){
37354 this.fireEvent("keydown", e);
37358 * Destroy this grid.
37359 * @param {Boolean} removeEl True to remove the element
37361 destroy : function(removeEl, keepListeners){
37363 this.loadMask.destroy();
37365 var c = this.container;
37366 c.removeAllListeners();
37367 this.view.destroy();
37368 this.colModel.purgeListeners();
37369 if(!keepListeners){
37370 this.purgeListeners();
37373 if(removeEl === true){
37379 processEvent : function(name, e){
37380 // does this fire select???
37381 //Roo.log('grid:processEvent ' + name);
37383 if (name != 'touchstart' ) {
37384 this.fireEvent(name, e);
37387 var t = e.getTarget();
37389 var header = v.findHeaderIndex(t);
37390 if(header !== false){
37391 var ename = name == 'touchstart' ? 'click' : name;
37393 this.fireEvent("header" + ename, this, header, e);
37395 var row = v.findRowIndex(t);
37396 var cell = v.findCellIndex(t);
37397 if (name == 'touchstart') {
37398 // first touch is always a click.
37399 // hopefull this happens after selection is updated.?
37402 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
37403 var cs = this.selModel.getSelectedCell();
37404 if (row == cs[0] && cell == cs[1]){
37408 if (typeof(this.selModel.getSelections) != 'undefined') {
37409 var cs = this.selModel.getSelections();
37410 var ds = this.dataSource;
37411 if (cs.length == 1 && ds.getAt(row) == cs[0]){
37422 this.fireEvent("row" + name, this, row, e);
37423 if(cell !== false){
37424 this.fireEvent("cell" + name, this, row, cell, e);
37431 onClick : function(e){
37432 this.processEvent("click", e);
37435 onTouchStart : function(e){
37436 this.processEvent("touchstart", e);
37440 onContextMenu : function(e, t){
37441 this.processEvent("contextmenu", e);
37445 onDblClick : function(e){
37446 this.processEvent("dblclick", e);
37450 walkCells : function(row, col, step, fn, scope){
37451 var cm = this.colModel, clen = cm.getColumnCount();
37452 var ds = this.dataSource, rlen = ds.getCount(), first = true;
37464 if(fn.call(scope || this, row, col, cm) === true){
37482 if(fn.call(scope || this, row, col, cm) === true){
37494 getSelections : function(){
37495 return this.selModel.getSelections();
37499 * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
37500 * but if manual update is required this method will initiate it.
37502 autoSize : function(){
37504 this.view.layout();
37505 if(this.view.adjustForScroll){
37506 this.view.adjustForScroll();
37512 * Returns the grid's underlying element.
37513 * @return {Element} The element
37515 getGridEl : function(){
37516 return this.container;
37519 // private for compatibility, overridden by editor grid
37520 stopEditing : function(){},
37523 * Returns the grid's SelectionModel.
37524 * @return {SelectionModel}
37526 getSelectionModel : function(){
37527 if(!this.selModel){
37528 this.selModel = new Roo.grid.RowSelectionModel();
37530 return this.selModel;
37534 * Returns the grid's DataSource.
37535 * @return {DataSource}
37537 getDataSource : function(){
37538 return this.dataSource;
37542 * Returns the grid's ColumnModel.
37543 * @return {ColumnModel}
37545 getColumnModel : function(){
37546 return this.colModel;
37550 * Returns the grid's GridView object.
37551 * @return {GridView}
37553 getView : function(){
37555 this.view = new Roo.grid.GridView(this.viewConfig);
37556 this.relayEvents(this.view, [
37557 "beforerowremoved", "beforerowsinserted",
37558 "beforerefresh", "rowremoved",
37559 "rowsinserted", "rowupdated" ,"refresh"
37565 * Called to get grid's drag proxy text, by default returns this.ddText.
37566 * Override this to put something different in the dragged text.
37569 getDragDropText : function(){
37570 var count = this.selModel.getCount();
37571 return String.format(this.ddText, count, count == 1 ? '' : 's');
37576 * Ext JS Library 1.1.1
37577 * Copyright(c) 2006-2007, Ext JS, LLC.
37579 * Originally Released Under LGPL - original licence link has changed is not relivant.
37582 * <script type="text/javascript">
37585 * @class Roo.grid.AbstractGridView
37586 * @extends Roo.util.Observable
37588 * Abstract base class for grid Views
37591 Roo.grid.AbstractGridView = function(){
37595 "beforerowremoved" : true,
37596 "beforerowsinserted" : true,
37597 "beforerefresh" : true,
37598 "rowremoved" : true,
37599 "rowsinserted" : true,
37600 "rowupdated" : true,
37603 Roo.grid.AbstractGridView.superclass.constructor.call(this);
37606 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
37607 rowClass : "x-grid-row",
37608 cellClass : "x-grid-cell",
37609 tdClass : "x-grid-td",
37610 hdClass : "x-grid-hd",
37611 splitClass : "x-grid-hd-split",
37613 init: function(grid){
37615 var cid = this.grid.getGridEl().id;
37616 this.colSelector = "#" + cid + " ." + this.cellClass + "-";
37617 this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
37618 this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
37619 this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
37622 getColumnRenderers : function(){
37623 var renderers = [];
37624 var cm = this.grid.colModel;
37625 var colCount = cm.getColumnCount();
37626 for(var i = 0; i < colCount; i++){
37627 renderers[i] = cm.getRenderer(i);
37632 getColumnIds : function(){
37634 var cm = this.grid.colModel;
37635 var colCount = cm.getColumnCount();
37636 for(var i = 0; i < colCount; i++){
37637 ids[i] = cm.getColumnId(i);
37642 getDataIndexes : function(){
37643 if(!this.indexMap){
37644 this.indexMap = this.buildIndexMap();
37646 return this.indexMap.colToData;
37649 getColumnIndexByDataIndex : function(dataIndex){
37650 if(!this.indexMap){
37651 this.indexMap = this.buildIndexMap();
37653 return this.indexMap.dataToCol[dataIndex];
37657 * Set a css style for a column dynamically.
37658 * @param {Number} colIndex The index of the column
37659 * @param {String} name The css property name
37660 * @param {String} value The css value
37662 setCSSStyle : function(colIndex, name, value){
37663 var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
37664 Roo.util.CSS.updateRule(selector, name, value);
37667 generateRules : function(cm){
37668 var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
37669 Roo.util.CSS.removeStyleSheet(rulesId);
37670 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
37671 var cid = cm.getColumnId(i);
37672 ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
37673 this.tdSelector, cid, " {\n}\n",
37674 this.hdSelector, cid, " {\n}\n",
37675 this.splitSelector, cid, " {\n}\n");
37677 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
37681 * Ext JS Library 1.1.1
37682 * Copyright(c) 2006-2007, Ext JS, LLC.
37684 * Originally Released Under LGPL - original licence link has changed is not relivant.
37687 * <script type="text/javascript">
37691 // This is a support class used internally by the Grid components
37692 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
37694 this.view = grid.getView();
37695 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37696 Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
37698 this.setHandleElId(Roo.id(hd));
37699 this.setOuterHandleElId(Roo.id(hd2));
37701 this.scroll = false;
37703 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
37705 getDragData : function(e){
37706 var t = Roo.lib.Event.getTarget(e);
37707 var h = this.view.findHeaderCell(t);
37709 return {ddel: h.firstChild, header:h};
37714 onInitDrag : function(e){
37715 this.view.headersDisabled = true;
37716 var clone = this.dragData.ddel.cloneNode(true);
37717 clone.id = Roo.id();
37718 clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
37719 this.proxy.update(clone);
37723 afterValidDrop : function(){
37725 setTimeout(function(){
37726 v.headersDisabled = false;
37730 afterInvalidDrop : function(){
37732 setTimeout(function(){
37733 v.headersDisabled = false;
37739 * Ext JS Library 1.1.1
37740 * Copyright(c) 2006-2007, Ext JS, LLC.
37742 * Originally Released Under LGPL - original licence link has changed is not relivant.
37745 * <script type="text/javascript">
37748 // This is a support class used internally by the Grid components
37749 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
37751 this.view = grid.getView();
37752 // split the proxies so they don't interfere with mouse events
37753 this.proxyTop = Roo.DomHelper.append(document.body, {
37754 cls:"col-move-top", html:" "
37756 this.proxyBottom = Roo.DomHelper.append(document.body, {
37757 cls:"col-move-bottom", html:" "
37759 this.proxyTop.hide = this.proxyBottom.hide = function(){
37760 this.setLeftTop(-100,-100);
37761 this.setStyle("visibility", "hidden");
37763 this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
37764 // temporarily disabled
37765 //Roo.dd.ScrollManager.register(this.view.scroller.dom);
37766 Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
37768 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
37769 proxyOffsets : [-4, -9],
37770 fly: Roo.Element.fly,
37772 getTargetFromEvent : function(e){
37773 var t = Roo.lib.Event.getTarget(e);
37774 var cindex = this.view.findCellIndex(t);
37775 if(cindex !== false){
37776 return this.view.getHeaderCell(cindex);
37781 nextVisible : function(h){
37782 var v = this.view, cm = this.grid.colModel;
37785 if(!cm.isHidden(v.getCellIndex(h))){
37793 prevVisible : function(h){
37794 var v = this.view, cm = this.grid.colModel;
37797 if(!cm.isHidden(v.getCellIndex(h))){
37805 positionIndicator : function(h, n, e){
37806 var x = Roo.lib.Event.getPageX(e);
37807 var r = Roo.lib.Dom.getRegion(n.firstChild);
37808 var px, pt, py = r.top + this.proxyOffsets[1];
37809 if((r.right - x) <= (r.right-r.left)/2){
37810 px = r.right+this.view.borderWidth;
37816 var oldIndex = this.view.getCellIndex(h);
37817 var newIndex = this.view.getCellIndex(n);
37819 if(this.grid.colModel.isFixed(newIndex)){
37823 var locked = this.grid.colModel.isLocked(newIndex);
37828 if(oldIndex < newIndex){
37831 if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
37834 px += this.proxyOffsets[0];
37835 this.proxyTop.setLeftTop(px, py);
37836 this.proxyTop.show();
37837 if(!this.bottomOffset){
37838 this.bottomOffset = this.view.mainHd.getHeight();
37840 this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
37841 this.proxyBottom.show();
37845 onNodeEnter : function(n, dd, e, data){
37846 if(data.header != n){
37847 this.positionIndicator(data.header, n, e);
37851 onNodeOver : function(n, dd, e, data){
37852 var result = false;
37853 if(data.header != n){
37854 result = this.positionIndicator(data.header, n, e);
37857 this.proxyTop.hide();
37858 this.proxyBottom.hide();
37860 return result ? this.dropAllowed : this.dropNotAllowed;
37863 onNodeOut : function(n, dd, e, data){
37864 this.proxyTop.hide();
37865 this.proxyBottom.hide();
37868 onNodeDrop : function(n, dd, e, data){
37869 var h = data.header;
37871 var cm = this.grid.colModel;
37872 var x = Roo.lib.Event.getPageX(e);
37873 var r = Roo.lib.Dom.getRegion(n.firstChild);
37874 var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
37875 var oldIndex = this.view.getCellIndex(h);
37876 var newIndex = this.view.getCellIndex(n);
37877 var locked = cm.isLocked(newIndex);
37881 if(oldIndex < newIndex){
37884 if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
37887 cm.setLocked(oldIndex, locked, true);
37888 cm.moveColumn(oldIndex, newIndex);
37889 this.grid.fireEvent("columnmove", oldIndex, newIndex);
37897 * Ext JS Library 1.1.1
37898 * Copyright(c) 2006-2007, Ext JS, LLC.
37900 * Originally Released Under LGPL - original licence link has changed is not relivant.
37903 * <script type="text/javascript">
37907 * @class Roo.grid.GridView
37908 * @extends Roo.util.Observable
37911 * @param {Object} config
37913 Roo.grid.GridView = function(config){
37914 Roo.grid.GridView.superclass.constructor.call(this);
37917 Roo.apply(this, config);
37920 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
37922 unselectable : 'unselectable="on"',
37923 unselectableCls : 'x-unselectable',
37926 rowClass : "x-grid-row",
37928 cellClass : "x-grid-col",
37930 tdClass : "x-grid-td",
37932 hdClass : "x-grid-hd",
37934 splitClass : "x-grid-split",
37936 sortClasses : ["sort-asc", "sort-desc"],
37938 enableMoveAnim : false,
37942 dh : Roo.DomHelper,
37944 fly : Roo.Element.fly,
37946 css : Roo.util.CSS,
37952 scrollIncrement : 22,
37954 cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
37956 findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
37958 bind : function(ds, cm){
37960 this.ds.un("load", this.onLoad, this);
37961 this.ds.un("datachanged", this.onDataChange, this);
37962 this.ds.un("add", this.onAdd, this);
37963 this.ds.un("remove", this.onRemove, this);
37964 this.ds.un("update", this.onUpdate, this);
37965 this.ds.un("clear", this.onClear, this);
37968 ds.on("load", this.onLoad, this);
37969 ds.on("datachanged", this.onDataChange, this);
37970 ds.on("add", this.onAdd, this);
37971 ds.on("remove", this.onRemove, this);
37972 ds.on("update", this.onUpdate, this);
37973 ds.on("clear", this.onClear, this);
37978 this.cm.un("widthchange", this.onColWidthChange, this);
37979 this.cm.un("headerchange", this.onHeaderChange, this);
37980 this.cm.un("hiddenchange", this.onHiddenChange, this);
37981 this.cm.un("columnmoved", this.onColumnMove, this);
37982 this.cm.un("columnlockchange", this.onColumnLock, this);
37985 this.generateRules(cm);
37986 cm.on("widthchange", this.onColWidthChange, this);
37987 cm.on("headerchange", this.onHeaderChange, this);
37988 cm.on("hiddenchange", this.onHiddenChange, this);
37989 cm.on("columnmoved", this.onColumnMove, this);
37990 cm.on("columnlockchange", this.onColumnLock, this);
37995 init: function(grid){
37996 Roo.grid.GridView.superclass.init.call(this, grid);
37998 this.bind(grid.dataSource, grid.colModel);
38000 grid.on("headerclick", this.handleHeaderClick, this);
38002 if(grid.trackMouseOver){
38003 grid.on("mouseover", this.onRowOver, this);
38004 grid.on("mouseout", this.onRowOut, this);
38006 grid.cancelTextSelection = function(){};
38007 this.gridId = grid.id;
38009 var tpls = this.templates || {};
38012 tpls.master = new Roo.Template(
38013 '<div class="x-grid" hidefocus="true">',
38014 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
38015 '<div class="x-grid-topbar"></div>',
38016 '<div class="x-grid-scroller"><div></div></div>',
38017 '<div class="x-grid-locked">',
38018 '<div class="x-grid-header">{lockedHeader}</div>',
38019 '<div class="x-grid-body">{lockedBody}</div>',
38021 '<div class="x-grid-viewport">',
38022 '<div class="x-grid-header">{header}</div>',
38023 '<div class="x-grid-body">{body}</div>',
38025 '<div class="x-grid-bottombar"></div>',
38027 '<div class="x-grid-resize-proxy"> </div>',
38030 tpls.master.disableformats = true;
38034 tpls.header = new Roo.Template(
38035 '<table border="0" cellspacing="0" cellpadding="0">',
38036 '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
38039 tpls.header.disableformats = true;
38041 tpls.header.compile();
38044 tpls.hcell = new Roo.Template(
38045 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
38046 '<div class="x-grid-hd-text ' + this.unselectableCls + '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
38049 tpls.hcell.disableFormats = true;
38051 tpls.hcell.compile();
38054 tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
38055 this.unselectableCls + '" ' + this.unselectable +'> </div>');
38056 tpls.hsplit.disableFormats = true;
38058 tpls.hsplit.compile();
38061 tpls.body = new Roo.Template(
38062 '<table border="0" cellspacing="0" cellpadding="0">',
38063 "<tbody>{rows}</tbody>",
38066 tpls.body.disableFormats = true;
38068 tpls.body.compile();
38071 tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
38072 tpls.row.disableFormats = true;
38074 tpls.row.compile();
38077 tpls.cell = new Roo.Template(
38078 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
38079 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
38080 this.unselectableCls + '" ' + this.unselectable +'" {attr}>{value}</div></div>',
38083 tpls.cell.disableFormats = true;
38085 tpls.cell.compile();
38087 this.templates = tpls;
38090 // remap these for backwards compat
38091 onColWidthChange : function(){
38092 this.updateColumns.apply(this, arguments);
38094 onHeaderChange : function(){
38095 this.updateHeaders.apply(this, arguments);
38097 onHiddenChange : function(){
38098 this.handleHiddenChange.apply(this, arguments);
38100 onColumnMove : function(){
38101 this.handleColumnMove.apply(this, arguments);
38103 onColumnLock : function(){
38104 this.handleLockChange.apply(this, arguments);
38107 onDataChange : function(){
38109 this.updateHeaderSortState();
38112 onClear : function(){
38116 onUpdate : function(ds, record){
38117 this.refreshRow(record);
38120 refreshRow : function(record){
38121 var ds = this.ds, index;
38122 if(typeof record == 'number'){
38124 record = ds.getAt(index);
38126 index = ds.indexOf(record);
38128 this.insertRows(ds, index, index, true);
38129 this.onRemove(ds, record, index+1, true);
38130 this.syncRowHeights(index, index);
38132 this.fireEvent("rowupdated", this, index, record);
38135 onAdd : function(ds, records, index){
38136 this.insertRows(ds, index, index + (records.length-1));
38139 onRemove : function(ds, record, index, isUpdate){
38140 if(isUpdate !== true){
38141 this.fireEvent("beforerowremoved", this, index, record);
38143 var bt = this.getBodyTable(), lt = this.getLockedTable();
38144 if(bt.rows[index]){
38145 bt.firstChild.removeChild(bt.rows[index]);
38147 if(lt.rows[index]){
38148 lt.firstChild.removeChild(lt.rows[index]);
38150 if(isUpdate !== true){
38151 this.stripeRows(index);
38152 this.syncRowHeights(index, index);
38154 this.fireEvent("rowremoved", this, index, record);
38158 onLoad : function(){
38159 this.scrollToTop();
38163 * Scrolls the grid to the top
38165 scrollToTop : function(){
38167 this.scroller.dom.scrollTop = 0;
38173 * Gets a panel in the header of the grid that can be used for toolbars etc.
38174 * After modifying the contents of this panel a call to grid.autoSize() may be
38175 * required to register any changes in size.
38176 * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
38177 * @return Roo.Element
38179 getHeaderPanel : function(doShow){
38181 this.headerPanel.show();
38183 return this.headerPanel;
38187 * Gets a panel in the footer of the grid that can be used for toolbars etc.
38188 * After modifying the contents of this panel a call to grid.autoSize() may be
38189 * required to register any changes in size.
38190 * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
38191 * @return Roo.Element
38193 getFooterPanel : function(doShow){
38195 this.footerPanel.show();
38197 return this.footerPanel;
38200 initElements : function(){
38201 var E = Roo.Element;
38202 var el = this.grid.getGridEl().dom.firstChild;
38203 var cs = el.childNodes;
38205 this.el = new E(el);
38207 this.focusEl = new E(el.firstChild);
38208 this.focusEl.swallowEvent("click", true);
38210 this.headerPanel = new E(cs[1]);
38211 this.headerPanel.enableDisplayMode("block");
38213 this.scroller = new E(cs[2]);
38214 this.scrollSizer = new E(this.scroller.dom.firstChild);
38216 this.lockedWrap = new E(cs[3]);
38217 this.lockedHd = new E(this.lockedWrap.dom.firstChild);
38218 this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
38220 this.mainWrap = new E(cs[4]);
38221 this.mainHd = new E(this.mainWrap.dom.firstChild);
38222 this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
38224 this.footerPanel = new E(cs[5]);
38225 this.footerPanel.enableDisplayMode("block");
38227 this.resizeProxy = new E(cs[6]);
38229 this.headerSelector = String.format(
38230 '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
38231 this.lockedHd.id, this.mainHd.id
38234 this.splitterSelector = String.format(
38235 '#{0} div.x-grid-split, #{1} div.x-grid-split',
38236 this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
38239 idToCssName : function(s)
38241 return s.replace(/[^a-z0-9]+/ig, '-');
38244 getHeaderCell : function(index){
38245 return Roo.DomQuery.select(this.headerSelector)[index];
38248 getHeaderCellMeasure : function(index){
38249 return this.getHeaderCell(index).firstChild;
38252 getHeaderCellText : function(index){
38253 return this.getHeaderCell(index).firstChild.firstChild;
38256 getLockedTable : function(){
38257 return this.lockedBody.dom.firstChild;
38260 getBodyTable : function(){
38261 return this.mainBody.dom.firstChild;
38264 getLockedRow : function(index){
38265 return this.getLockedTable().rows[index];
38268 getRow : function(index){
38269 return this.getBodyTable().rows[index];
38272 getRowComposite : function(index){
38274 this.rowEl = new Roo.CompositeElementLite();
38276 var els = [], lrow, mrow;
38277 if(lrow = this.getLockedRow(index)){
38280 if(mrow = this.getRow(index)){
38283 this.rowEl.elements = els;
38287 * Gets the 'td' of the cell
38289 * @param {Integer} rowIndex row to select
38290 * @param {Integer} colIndex column to select
38294 getCell : function(rowIndex, colIndex){
38295 var locked = this.cm.getLockedCount();
38297 if(colIndex < locked){
38298 source = this.lockedBody.dom.firstChild;
38300 source = this.mainBody.dom.firstChild;
38301 colIndex -= locked;
38303 return source.rows[rowIndex].childNodes[colIndex];
38306 getCellText : function(rowIndex, colIndex){
38307 return this.getCell(rowIndex, colIndex).firstChild.firstChild;
38310 getCellBox : function(cell){
38311 var b = this.fly(cell).getBox();
38312 if(Roo.isOpera){ // opera fails to report the Y
38313 b.y = cell.offsetTop + this.mainBody.getY();
38318 getCellIndex : function(cell){
38319 var id = String(cell.className).match(this.cellRE);
38321 return parseInt(id[1], 10);
38326 findHeaderIndex : function(n){
38327 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38328 return r ? this.getCellIndex(r) : false;
38331 findHeaderCell : function(n){
38332 var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
38333 return r ? r : false;
38336 findRowIndex : function(n){
38340 var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
38341 return r ? r.rowIndex : false;
38344 findCellIndex : function(node){
38345 var stop = this.el.dom;
38346 while(node && node != stop){
38347 if(this.findRE.test(node.className)){
38348 return this.getCellIndex(node);
38350 node = node.parentNode;
38355 getColumnId : function(index){
38356 return this.cm.getColumnId(index);
38359 getSplitters : function()
38361 if(this.splitterSelector){
38362 return Roo.DomQuery.select(this.splitterSelector);
38368 getSplitter : function(index){
38369 return this.getSplitters()[index];
38372 onRowOver : function(e, t){
38374 if((row = this.findRowIndex(t)) !== false){
38375 this.getRowComposite(row).addClass("x-grid-row-over");
38379 onRowOut : function(e, t){
38381 if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
38382 this.getRowComposite(row).removeClass("x-grid-row-over");
38386 renderHeaders : function(){
38388 var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
38389 var cb = [], lb = [], sb = [], lsb = [], p = {};
38390 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38391 p.cellId = "x-grid-hd-0-" + i;
38392 p.splitId = "x-grid-csplit-0-" + i;
38393 p.id = cm.getColumnId(i);
38394 p.value = cm.getColumnHeader(i) || "";
38395 p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</) ? '' : p.value || "";
38396 p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
38397 if(!cm.isLocked(i)){
38398 cb[cb.length] = ct.apply(p);
38399 sb[sb.length] = st.apply(p);
38401 lb[lb.length] = ct.apply(p);
38402 lsb[lsb.length] = st.apply(p);
38405 return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
38406 ht.apply({cells: cb.join(""), splits:sb.join("")})];
38409 updateHeaders : function(){
38410 var html = this.renderHeaders();
38411 this.lockedHd.update(html[0]);
38412 this.mainHd.update(html[1]);
38416 * Focuses the specified row.
38417 * @param {Number} row The row index
38419 focusRow : function(row)
38421 //Roo.log('GridView.focusRow');
38422 var x = this.scroller.dom.scrollLeft;
38423 this.focusCell(row, 0, false);
38424 this.scroller.dom.scrollLeft = x;
38428 * Focuses the specified cell.
38429 * @param {Number} row The row index
38430 * @param {Number} col The column index
38431 * @param {Boolean} hscroll false to disable horizontal scrolling
38433 focusCell : function(row, col, hscroll)
38435 //Roo.log('GridView.focusCell');
38436 var el = this.ensureVisible(row, col, hscroll);
38437 this.focusEl.alignTo(el, "tl-tl");
38439 this.focusEl.focus();
38441 this.focusEl.focus.defer(1, this.focusEl);
38446 * Scrolls the specified cell into view
38447 * @param {Number} row The row index
38448 * @param {Number} col The column index
38449 * @param {Boolean} hscroll false to disable horizontal scrolling
38451 ensureVisible : function(row, col, hscroll)
38453 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
38454 //return null; //disable for testing.
38455 if(typeof row != "number"){
38456 row = row.rowIndex;
38458 if(row < 0 && row >= this.ds.getCount()){
38461 col = (col !== undefined ? col : 0);
38462 var cm = this.grid.colModel;
38463 while(cm.isHidden(col)){
38467 var el = this.getCell(row, col);
38471 var c = this.scroller.dom;
38473 var ctop = parseInt(el.offsetTop, 10);
38474 var cleft = parseInt(el.offsetLeft, 10);
38475 var cbot = ctop + el.offsetHeight;
38476 var cright = cleft + el.offsetWidth;
38478 var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
38479 var stop = parseInt(c.scrollTop, 10);
38480 var sleft = parseInt(c.scrollLeft, 10);
38481 var sbot = stop + ch;
38482 var sright = sleft + c.clientWidth;
38484 Roo.log('GridView.ensureVisible:' +
38486 ' c.clientHeight:' + c.clientHeight +
38487 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
38495 c.scrollTop = ctop;
38496 //Roo.log("set scrolltop to ctop DISABLE?");
38497 }else if(cbot > sbot){
38498 //Roo.log("set scrolltop to cbot-ch");
38499 c.scrollTop = cbot-ch;
38502 if(hscroll !== false){
38504 c.scrollLeft = cleft;
38505 }else if(cright > sright){
38506 c.scrollLeft = cright-c.clientWidth;
38513 updateColumns : function(){
38514 this.grid.stopEditing();
38515 var cm = this.grid.colModel, colIds = this.getColumnIds();
38516 //var totalWidth = cm.getTotalWidth();
38518 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38519 //if(cm.isHidden(i)) continue;
38520 var w = cm.getColumnWidth(i);
38521 this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38522 this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
38524 this.updateSplitters();
38527 generateRules : function(cm){
38528 var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
38529 Roo.util.CSS.removeStyleSheet(rulesId);
38530 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38531 var cid = cm.getColumnId(i);
38533 if(cm.config[i].align){
38534 align = 'text-align:'+cm.config[i].align+';';
38537 if(cm.isHidden(i)){
38538 hidden = 'display:none;';
38540 var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
38542 this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
38543 this.hdSelector, cid, " {\n", align, width, "}\n",
38544 this.tdSelector, cid, " {\n",hidden,"\n}\n",
38545 this.splitSelector, cid, " {\n", hidden , "\n}\n");
38547 return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
38550 updateSplitters : function(){
38551 var cm = this.cm, s = this.getSplitters();
38552 if(s){ // splitters not created yet
38553 var pos = 0, locked = true;
38554 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
38555 if(cm.isHidden(i)) {
38558 var w = cm.getColumnWidth(i); // make sure it's a number
38559 if(!cm.isLocked(i) && locked){
38564 s[i].style.left = (pos-this.splitOffset) + "px";
38569 handleHiddenChange : function(colModel, colIndex, hidden){
38571 this.hideColumn(colIndex);
38573 this.unhideColumn(colIndex);
38577 hideColumn : function(colIndex){
38578 var cid = this.getColumnId(colIndex);
38579 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
38580 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
38582 this.updateHeaders();
38584 this.updateSplitters();
38588 unhideColumn : function(colIndex){
38589 var cid = this.getColumnId(colIndex);
38590 this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
38591 this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
38594 this.updateHeaders();
38596 this.updateSplitters();
38600 insertRows : function(dm, firstRow, lastRow, isUpdate){
38601 if(firstRow == 0 && lastRow == dm.getCount()-1){
38605 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
38607 var s = this.getScrollState();
38608 var markup = this.renderRows(firstRow, lastRow);
38609 this.bufferRows(markup[0], this.getLockedTable(), firstRow);
38610 this.bufferRows(markup[1], this.getBodyTable(), firstRow);
38611 this.restoreScroll(s);
38613 this.fireEvent("rowsinserted", this, firstRow, lastRow);
38614 this.syncRowHeights(firstRow, lastRow);
38615 this.stripeRows(firstRow);
38621 bufferRows : function(markup, target, index){
38622 var before = null, trows = target.rows, tbody = target.tBodies[0];
38623 if(index < trows.length){
38624 before = trows[index];
38626 var b = document.createElement("div");
38627 b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
38628 var rows = b.firstChild.rows;
38629 for(var i = 0, len = rows.length; i < len; i++){
38631 tbody.insertBefore(rows[0], before);
38633 tbody.appendChild(rows[0]);
38640 deleteRows : function(dm, firstRow, lastRow){
38641 if(dm.getRowCount()<1){
38642 this.fireEvent("beforerefresh", this);
38643 this.mainBody.update("");
38644 this.lockedBody.update("");
38645 this.fireEvent("refresh", this);
38647 this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
38648 var bt = this.getBodyTable();
38649 var tbody = bt.firstChild;
38650 var rows = bt.rows;
38651 for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
38652 tbody.removeChild(rows[firstRow]);
38654 this.stripeRows(firstRow);
38655 this.fireEvent("rowsdeleted", this, firstRow, lastRow);
38659 updateRows : function(dataSource, firstRow, lastRow){
38660 var s = this.getScrollState();
38662 this.restoreScroll(s);
38665 handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
38669 this.updateHeaderSortState();
38672 getScrollState : function(){
38674 var sb = this.scroller.dom;
38675 return {left: sb.scrollLeft, top: sb.scrollTop};
38678 stripeRows : function(startRow){
38679 if(!this.grid.stripeRows || this.ds.getCount() < 1){
38682 startRow = startRow || 0;
38683 var rows = this.getBodyTable().rows;
38684 var lrows = this.getLockedTable().rows;
38685 var cls = ' x-grid-row-alt ';
38686 for(var i = startRow, len = rows.length; i < len; i++){
38687 var row = rows[i], lrow = lrows[i];
38688 var isAlt = ((i+1) % 2 == 0);
38689 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
38690 if(isAlt == hasAlt){
38694 row.className += " x-grid-row-alt";
38696 row.className = row.className.replace("x-grid-row-alt", "");
38699 lrow.className = row.className;
38704 restoreScroll : function(state){
38705 //Roo.log('GridView.restoreScroll');
38706 var sb = this.scroller.dom;
38707 sb.scrollLeft = state.left;
38708 sb.scrollTop = state.top;
38712 syncScroll : function(){
38713 //Roo.log('GridView.syncScroll');
38714 var sb = this.scroller.dom;
38715 var sh = this.mainHd.dom;
38716 var bs = this.mainBody.dom;
38717 var lv = this.lockedBody.dom;
38718 sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
38719 lv.scrollTop = bs.scrollTop = sb.scrollTop;
38722 handleScroll : function(e){
38724 var sb = this.scroller.dom;
38725 this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
38729 handleWheel : function(e){
38730 var d = e.getWheelDelta();
38731 this.scroller.dom.scrollTop -= d*22;
38732 // set this here to prevent jumpy scrolling on large tables
38733 this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
38737 renderRows : function(startRow, endRow){
38738 // pull in all the crap needed to render rows
38739 var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
38740 var colCount = cm.getColumnCount();
38742 if(ds.getCount() < 1){
38746 // build a map for all the columns
38748 for(var i = 0; i < colCount; i++){
38749 var name = cm.getDataIndex(i);
38751 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
38752 renderer : cm.getRenderer(i),
38753 id : cm.getColumnId(i),
38754 locked : cm.isLocked(i),
38755 has_editor : cm.isCellEditable(i)
38759 startRow = startRow || 0;
38760 endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
38762 // records to render
38763 var rs = ds.getRange(startRow, endRow);
38765 return this.doRender(cs, rs, ds, startRow, colCount, stripe);
38768 // As much as I hate to duplicate code, this was branched because FireFox really hates
38769 // [].join("") on strings. The performance difference was substantial enough to
38770 // branch this function
38771 doRender : Roo.isGecko ?
38772 function(cs, rs, ds, startRow, colCount, stripe){
38773 var ts = this.templates, ct = ts.cell, rt = ts.row;
38775 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38777 var hasListener = this.grid.hasListener('rowclass');
38779 for(var j = 0, len = rs.length; j < len; j++){
38780 r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
38781 for(var i = 0; i < colCount; i++){
38783 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38785 p.css = p.attr = "";
38786 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38787 if(p.value == undefined || p.value === "") {
38788 p.value = " ";
38791 p.css += ' x-grid-editable-cell';
38793 if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
38794 p.css += ' x-grid-dirty-cell';
38796 var markup = ct.apply(p);
38804 if(stripe && ((rowIndex+1) % 2 == 0)){
38805 alt.push("x-grid-row-alt")
38808 alt.push( " x-grid-dirty-row");
38811 if(this.getRowClass){
38812 alt.push(this.getRowClass(r, rowIndex));
38818 rowIndex : rowIndex,
38821 this.grid.fireEvent('rowclass', this, rowcfg);
38822 alt.push(rowcfg.rowClass);
38824 rp.alt = alt.join(" ");
38825 lbuf+= rt.apply(rp);
38827 buf+= rt.apply(rp);
38829 return [lbuf, buf];
38831 function(cs, rs, ds, startRow, colCount, stripe){
38832 var ts = this.templates, ct = ts.cell, rt = ts.row;
38834 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
38835 var hasListener = this.grid.hasListener('rowclass');
38838 for(var j = 0, len = rs.length; j < len; j++){
38839 r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
38840 for(var i = 0; i < colCount; i++){
38842 p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
38844 p.css = p.attr = "";
38845 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
38846 if(p.value == undefined || p.value === "") {
38847 p.value = " ";
38851 p.css += ' x-grid-editable-cell';
38853 if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
38854 p.css += ' x-grid-dirty-cell'
38857 var markup = ct.apply(p);
38859 cb[cb.length] = markup;
38861 lcb[lcb.length] = markup;
38865 if(stripe && ((rowIndex+1) % 2 == 0)){
38866 alt.push( "x-grid-row-alt");
38869 alt.push(" x-grid-dirty-row");
38872 if(this.getRowClass){
38873 alt.push( this.getRowClass(r, rowIndex));
38879 rowIndex : rowIndex,
38882 this.grid.fireEvent('rowclass', this, rowcfg);
38883 alt.push(rowcfg.rowClass);
38886 rp.alt = alt.join(" ");
38887 rp.cells = lcb.join("");
38888 lbuf[lbuf.length] = rt.apply(rp);
38889 rp.cells = cb.join("");
38890 buf[buf.length] = rt.apply(rp);
38892 return [lbuf.join(""), buf.join("")];
38895 renderBody : function(){
38896 var markup = this.renderRows();
38897 var bt = this.templates.body;
38898 return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
38902 * Refreshes the grid
38903 * @param {Boolean} headersToo
38905 refresh : function(headersToo){
38906 this.fireEvent("beforerefresh", this);
38907 this.grid.stopEditing();
38908 var result = this.renderBody();
38909 this.lockedBody.update(result[0]);
38910 this.mainBody.update(result[1]);
38911 if(headersToo === true){
38912 this.updateHeaders();
38913 this.updateColumns();
38914 this.updateSplitters();
38915 this.updateHeaderSortState();
38917 this.syncRowHeights();
38919 this.fireEvent("refresh", this);
38922 handleColumnMove : function(cm, oldIndex, newIndex){
38923 this.indexMap = null;
38924 var s = this.getScrollState();
38925 this.refresh(true);
38926 this.restoreScroll(s);
38927 this.afterMove(newIndex);
38930 afterMove : function(colIndex){
38931 if(this.enableMoveAnim && Roo.enableFx){
38932 this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
38934 // if multisort - fix sortOrder, and reload..
38935 if (this.grid.dataSource.multiSort) {
38936 // the we can call sort again..
38937 var dm = this.grid.dataSource;
38938 var cm = this.grid.colModel;
38940 for(var i = 0; i < cm.config.length; i++ ) {
38942 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
38943 continue; // dont' bother, it's not in sort list or being set.
38946 so.push(cm.config[i].dataIndex);
38949 dm.load(dm.lastOptions);
38956 updateCell : function(dm, rowIndex, dataIndex){
38957 var colIndex = this.getColumnIndexByDataIndex(dataIndex);
38958 if(typeof colIndex == "undefined"){ // not present in grid
38961 var cm = this.grid.colModel;
38962 var cell = this.getCell(rowIndex, colIndex);
38963 var cellText = this.getCellText(rowIndex, colIndex);
38966 cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
38967 id : cm.getColumnId(colIndex),
38968 css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
38970 var renderer = cm.getRenderer(colIndex);
38971 var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
38972 if(typeof val == "undefined" || val === "") {
38975 cellText.innerHTML = val;
38976 cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
38977 this.syncRowHeights(rowIndex, rowIndex);
38980 calcColumnWidth : function(colIndex, maxRowsToMeasure){
38982 if(this.grid.autoSizeHeaders){
38983 var h = this.getHeaderCellMeasure(colIndex);
38984 maxWidth = Math.max(maxWidth, h.scrollWidth);
38987 if(this.cm.isLocked(colIndex)){
38988 tb = this.getLockedTable();
38991 tb = this.getBodyTable();
38992 index = colIndex - this.cm.getLockedCount();
38995 var rows = tb.rows;
38996 var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
38997 for(var i = 0; i < stopIndex; i++){
38998 var cell = rows[i].childNodes[index].firstChild;
38999 maxWidth = Math.max(maxWidth, cell.scrollWidth);
39002 return maxWidth + /*margin for error in IE*/ 5;
39005 * Autofit a column to its content.
39006 * @param {Number} colIndex
39007 * @param {Boolean} forceMinSize true to force the column to go smaller if possible
39009 autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
39010 if(this.cm.isHidden(colIndex)){
39011 return; // can't calc a hidden column
39014 var cid = this.cm.getColumnId(colIndex);
39015 this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
39016 if(this.grid.autoSizeHeaders){
39017 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
39020 var newWidth = this.calcColumnWidth(colIndex);
39021 this.cm.setColumnWidth(colIndex,
39022 Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
39023 if(!suppressEvent){
39024 this.grid.fireEvent("columnresize", colIndex, newWidth);
39029 * Autofits all columns to their content and then expands to fit any extra space in the grid
39031 autoSizeColumns : function(){
39032 var cm = this.grid.colModel;
39033 var colCount = cm.getColumnCount();
39034 for(var i = 0; i < colCount; i++){
39035 this.autoSizeColumn(i, true, true);
39037 if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
39040 this.updateColumns();
39046 * Autofits all columns to the grid's width proportionate with their current size
39047 * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
39049 fitColumns : function(reserveScrollSpace){
39050 var cm = this.grid.colModel;
39051 var colCount = cm.getColumnCount();
39055 for (i = 0; i < colCount; i++){
39056 if(!cm.isHidden(i) && !cm.isFixed(i)){
39057 w = cm.getColumnWidth(i);
39063 var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
39064 if(reserveScrollSpace){
39067 var frac = (avail - cm.getTotalWidth())/width;
39068 while (cols.length){
39071 cm.setColumnWidth(i, Math.floor(w + w*frac), true);
39073 this.updateColumns();
39077 onRowSelect : function(rowIndex){
39078 var row = this.getRowComposite(rowIndex);
39079 row.addClass("x-grid-row-selected");
39082 onRowDeselect : function(rowIndex){
39083 var row = this.getRowComposite(rowIndex);
39084 row.removeClass("x-grid-row-selected");
39087 onCellSelect : function(row, col){
39088 var cell = this.getCell(row, col);
39090 Roo.fly(cell).addClass("x-grid-cell-selected");
39094 onCellDeselect : function(row, col){
39095 var cell = this.getCell(row, col);
39097 Roo.fly(cell).removeClass("x-grid-cell-selected");
39101 updateHeaderSortState : function(){
39103 // sort state can be single { field: xxx, direction : yyy}
39104 // or { xxx=>ASC , yyy : DESC ..... }
39107 if (!this.ds.multiSort) {
39108 var state = this.ds.getSortState();
39112 mstate[state.field] = state.direction;
39113 // FIXME... - this is not used here.. but might be elsewhere..
39114 this.sortState = state;
39117 mstate = this.ds.sortToggle;
39119 //remove existing sort classes..
39121 var sc = this.sortClasses;
39122 var hds = this.el.select(this.headerSelector).removeClass(sc);
39124 for(var f in mstate) {
39126 var sortColumn = this.cm.findColumnIndex(f);
39128 if(sortColumn != -1){
39129 var sortDir = mstate[f];
39130 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
39139 handleHeaderClick : function(g, index,e){
39141 Roo.log("header click");
39144 // touch events on header are handled by context
39145 this.handleHdCtx(g,index,e);
39150 if(this.headersDisabled){
39153 var dm = g.dataSource, cm = g.colModel;
39154 if(!cm.isSortable(index)){
39159 if (dm.multiSort) {
39160 // update the sortOrder
39162 for(var i = 0; i < cm.config.length; i++ ) {
39164 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
39165 continue; // dont' bother, it's not in sort list or being set.
39168 so.push(cm.config[i].dataIndex);
39174 dm.sort(cm.getDataIndex(index));
39178 destroy : function(){
39180 this.colMenu.removeAll();
39181 Roo.menu.MenuMgr.unregister(this.colMenu);
39182 this.colMenu.getEl().remove();
39183 delete this.colMenu;
39186 this.hmenu.removeAll();
39187 Roo.menu.MenuMgr.unregister(this.hmenu);
39188 this.hmenu.getEl().remove();
39191 if(this.grid.enableColumnMove){
39192 var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
39194 for(var dd in dds){
39195 if(!dds[dd].config.isTarget && dds[dd].dragElId){
39196 var elid = dds[dd].dragElId;
39198 Roo.get(elid).remove();
39199 } else if(dds[dd].config.isTarget){
39200 dds[dd].proxyTop.remove();
39201 dds[dd].proxyBottom.remove();
39204 if(Roo.dd.DDM.locationCache[dd]){
39205 delete Roo.dd.DDM.locationCache[dd];
39208 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
39211 Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
39212 this.bind(null, null);
39213 Roo.EventManager.removeResizeListener(this.onWindowResize, this);
39216 handleLockChange : function(){
39217 this.refresh(true);
39220 onDenyColumnLock : function(){
39224 onDenyColumnHide : function(){
39228 handleHdMenuClick : function(item){
39229 var index = this.hdCtxIndex;
39230 var cm = this.cm, ds = this.ds;
39233 ds.sort(cm.getDataIndex(index), "ASC");
39236 ds.sort(cm.getDataIndex(index), "DESC");
39239 var lc = cm.getLockedCount();
39240 if(cm.getColumnCount(true) <= lc+1){
39241 this.onDenyColumnLock();
39245 cm.setLocked(index, true, true);
39246 cm.moveColumn(index, lc);
39247 this.grid.fireEvent("columnmove", index, lc);
39249 cm.setLocked(index, true);
39253 var lc = cm.getLockedCount();
39254 if((lc-1) != index){
39255 cm.setLocked(index, false, true);
39256 cm.moveColumn(index, lc-1);
39257 this.grid.fireEvent("columnmove", index, lc-1);
39259 cm.setLocked(index, false);
39262 case 'wider': // used to expand cols on touch..
39264 var cw = cm.getColumnWidth(index);
39265 cw += (item.id == 'wider' ? 1 : -1) * 50;
39266 cw = Math.max(0, cw);
39267 cw = Math.min(cw,4000);
39268 cm.setColumnWidth(index, cw);
39272 index = cm.getIndexById(item.id.substr(4));
39274 if(item.checked && cm.getColumnCount(true) <= 1){
39275 this.onDenyColumnHide();
39278 cm.setHidden(index, item.checked);
39284 beforeColMenuShow : function(){
39285 var cm = this.cm, colCount = cm.getColumnCount();
39286 this.colMenu.removeAll();
39289 for(var i = 0; i < colCount; i++){
39291 id: "col-"+cm.getColumnId(i),
39292 text: cm.getColumnHeader(i),
39293 checked: !cm.isHidden(i),
39298 if (this.grid.sortColMenu) {
39299 items.sort(function(a,b) {
39300 if (a.text == b.text) {
39303 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
39307 for(var i = 0; i < colCount; i++){
39308 this.colMenu.add(new Roo.menu.CheckItem(items[i]));
39312 handleHdCtx : function(g, index, e){
39314 var hd = this.getHeaderCell(index);
39315 this.hdCtxIndex = index;
39316 var ms = this.hmenu.items, cm = this.cm;
39317 ms.get("asc").setDisabled(!cm.isSortable(index));
39318 ms.get("desc").setDisabled(!cm.isSortable(index));
39319 if(this.grid.enableColLock !== false){
39320 ms.get("lock").setDisabled(cm.isLocked(index));
39321 ms.get("unlock").setDisabled(!cm.isLocked(index));
39323 this.hmenu.show(hd, "tl-bl");
39326 handleHdOver : function(e){
39327 var hd = this.findHeaderCell(e.getTarget());
39328 if(hd && !this.headersDisabled){
39329 if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
39330 this.fly(hd).addClass("x-grid-hd-over");
39335 handleHdOut : function(e){
39336 var hd = this.findHeaderCell(e.getTarget());
39338 this.fly(hd).removeClass("x-grid-hd-over");
39342 handleSplitDblClick : function(e, t){
39343 var i = this.getCellIndex(t);
39344 if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
39345 this.autoSizeColumn(i, true);
39350 render : function(){
39353 var colCount = cm.getColumnCount();
39355 if(this.grid.monitorWindowResize === true){
39356 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
39358 var header = this.renderHeaders();
39359 var body = this.templates.body.apply({rows:""});
39360 var html = this.templates.master.apply({
39363 lockedHeader: header[0],
39367 //this.updateColumns();
39369 this.grid.getGridEl().dom.innerHTML = html;
39371 this.initElements();
39373 // a kludge to fix the random scolling effect in webkit
39374 this.el.on("scroll", function() {
39375 this.el.dom.scrollTop=0; // hopefully not recursive..
39378 this.scroller.on("scroll", this.handleScroll, this);
39379 this.lockedBody.on("mousewheel", this.handleWheel, this);
39380 this.mainBody.on("mousewheel", this.handleWheel, this);
39382 this.mainHd.on("mouseover", this.handleHdOver, this);
39383 this.mainHd.on("mouseout", this.handleHdOut, this);
39384 this.mainHd.on("dblclick", this.handleSplitDblClick, this,
39385 {delegate: "."+this.splitClass});
39387 this.lockedHd.on("mouseover", this.handleHdOver, this);
39388 this.lockedHd.on("mouseout", this.handleHdOut, this);
39389 this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
39390 {delegate: "."+this.splitClass});
39392 if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
39393 new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39396 this.updateSplitters();
39398 if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
39399 new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39400 new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
39403 if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
39404 this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
39406 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
39407 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
39409 if(this.grid.enableColLock !== false){
39410 this.hmenu.add('-',
39411 {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
39412 {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
39416 this.hmenu.add('-',
39417 {id:"wider", text: this.columnsWiderText},
39418 {id:"narrow", text: this.columnsNarrowText }
39424 if(this.grid.enableColumnHide !== false){
39426 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
39427 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
39428 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
39430 this.hmenu.add('-',
39431 {id:"columns", text: this.columnsText, menu: this.colMenu}
39434 this.hmenu.on("itemclick", this.handleHdMenuClick, this);
39436 this.grid.on("headercontextmenu", this.handleHdCtx, this);
39439 if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
39440 this.dd = new Roo.grid.GridDragZone(this.grid, {
39441 ddGroup : this.grid.ddGroup || 'GridDD'
39447 for(var i = 0; i < colCount; i++){
39448 if(cm.isHidden(i)){
39449 this.hideColumn(i);
39451 if(cm.config[i].align){
39452 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
39453 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
39457 this.updateHeaderSortState();
39459 this.beforeInitialResize();
39462 // two part rendering gives faster view to the user
39463 this.renderPhase2.defer(1, this);
39466 renderPhase2 : function(){
39467 // render the rows now
39469 if(this.grid.autoSizeColumns){
39470 this.autoSizeColumns();
39474 beforeInitialResize : function(){
39478 onColumnSplitterMoved : function(i, w){
39479 this.userResized = true;
39480 var cm = this.grid.colModel;
39481 cm.setColumnWidth(i, w, true);
39482 var cid = cm.getColumnId(i);
39483 this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39484 this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
39485 this.updateSplitters();
39487 this.grid.fireEvent("columnresize", i, w);
39490 syncRowHeights : function(startIndex, endIndex){
39491 if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
39492 startIndex = startIndex || 0;
39493 var mrows = this.getBodyTable().rows;
39494 var lrows = this.getLockedTable().rows;
39495 var len = mrows.length-1;
39496 endIndex = Math.min(endIndex || len, len);
39497 for(var i = startIndex; i <= endIndex; i++){
39498 var m = mrows[i], l = lrows[i];
39499 var h = Math.max(m.offsetHeight, l.offsetHeight);
39500 m.style.height = l.style.height = h + "px";
39505 layout : function(initialRender, is2ndPass)
39508 var auto = g.autoHeight;
39509 var scrollOffset = 16;
39510 var c = g.getGridEl(), cm = this.cm,
39511 expandCol = g.autoExpandColumn,
39513 //c.beginMeasure();
39515 if(!c.dom.offsetWidth){ // display:none?
39517 this.lockedWrap.show();
39518 this.mainWrap.show();
39523 var hasLock = this.cm.isLocked(0);
39525 var tbh = this.headerPanel.getHeight();
39526 var bbh = this.footerPanel.getHeight();
39529 var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
39530 var newHeight = ch + c.getBorderWidth("tb");
39532 newHeight = Math.min(g.maxHeight, newHeight);
39534 c.setHeight(newHeight);
39538 c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
39541 var s = this.scroller;
39543 var csize = c.getSize(true);
39545 this.el.setSize(csize.width, csize.height);
39547 this.headerPanel.setWidth(csize.width);
39548 this.footerPanel.setWidth(csize.width);
39550 var hdHeight = this.mainHd.getHeight();
39551 var vw = csize.width;
39552 var vh = csize.height - (tbh + bbh);
39556 var bt = this.getBodyTable();
39558 if(cm.getLockedCount() == cm.config.length){
39559 bt = this.getLockedTable();
39562 var ltWidth = hasLock ?
39563 Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
39565 var scrollHeight = bt.offsetHeight;
39566 var scrollWidth = ltWidth + bt.offsetWidth;
39567 var vscroll = false, hscroll = false;
39569 this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
39571 var lw = this.lockedWrap, mw = this.mainWrap;
39572 var lb = this.lockedBody, mb = this.mainBody;
39574 setTimeout(function(){
39575 var t = s.dom.offsetTop;
39576 var w = s.dom.clientWidth,
39577 h = s.dom.clientHeight;
39580 lw.setSize(ltWidth, h);
39582 mw.setLeftTop(ltWidth, t);
39583 mw.setSize(w-ltWidth, h);
39585 lb.setHeight(h-hdHeight);
39586 mb.setHeight(h-hdHeight);
39588 if(is2ndPass !== true && !gv.userResized && expandCol){
39589 // high speed resize without full column calculation
39591 var ci = cm.getIndexById(expandCol);
39593 ci = cm.findColumnIndex(expandCol);
39595 ci = Math.max(0, ci); // make sure it's got at least the first col.
39596 var expandId = cm.getColumnId(ci);
39597 var tw = cm.getTotalWidth(false);
39598 var currentWidth = cm.getColumnWidth(ci);
39599 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
39600 if(currentWidth != cw){
39601 cm.setColumnWidth(ci, cw, true);
39602 gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39603 gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
39604 gv.updateSplitters();
39605 gv.layout(false, true);
39617 onWindowResize : function(){
39618 if(!this.grid.monitorWindowResize || this.grid.autoHeight){
39624 appendFooter : function(parentEl){
39628 sortAscText : "Sort Ascending",
39629 sortDescText : "Sort Descending",
39630 lockText : "Lock Column",
39631 unlockText : "Unlock Column",
39632 columnsText : "Columns",
39634 columnsWiderText : "Wider",
39635 columnsNarrowText : "Thinner"
39639 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
39640 Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
39641 this.proxy.el.addClass('x-grid3-col-dd');
39644 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
39645 handleMouseDown : function(e){
39649 callHandleMouseDown : function(e){
39650 Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
39655 * Ext JS Library 1.1.1
39656 * Copyright(c) 2006-2007, Ext JS, LLC.
39658 * Originally Released Under LGPL - original licence link has changed is not relivant.
39661 * <script type="text/javascript">
39664 * @extends Roo.dd.DDProxy
39665 * @class Roo.grid.SplitDragZone
39666 * Support for Column Header resizing
39668 * @param {Object} config
39671 // This is a support class used internally by the Grid components
39672 Roo.grid.SplitDragZone = function(grid, hd, hd2){
39674 this.view = grid.getView();
39675 this.proxy = this.view.resizeProxy;
39676 Roo.grid.SplitDragZone.superclass.constructor.call(
39679 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
39681 dragElId : Roo.id(this.proxy.dom),
39686 this.setHandleElId(Roo.id(hd));
39687 if (hd2 !== false) {
39688 this.setOuterHandleElId(Roo.id(hd2));
39691 this.scroll = false;
39693 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
39694 fly: Roo.Element.fly,
39696 b4StartDrag : function(x, y){
39697 this.view.headersDisabled = true;
39698 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
39699 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
39701 this.proxy.setHeight(h);
39703 // for old system colWidth really stored the actual width?
39704 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
39705 // which in reality did not work.. - it worked only for fixed sizes
39706 // for resizable we need to use actual sizes.
39707 var w = this.cm.getColumnWidth(this.cellIndex);
39708 if (!this.view.mainWrap) {
39710 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
39715 // this was w-this.grid.minColumnWidth;
39716 // doesnt really make sense? - w = thie curren width or the rendered one?
39717 var minw = Math.max(w-this.grid.minColumnWidth, 0);
39718 this.resetConstraints();
39719 this.setXConstraint(minw, 1000);
39720 this.setYConstraint(0, 0);
39721 this.minX = x - minw;
39722 this.maxX = x + 1000;
39724 if (!this.view.mainWrap) { // this is Bootstrap code..
39725 this.getDragEl().style.display='block';
39728 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
39732 handleMouseDown : function(e){
39733 ev = Roo.EventObject.setEvent(e);
39734 var t = this.fly(ev.getTarget());
39735 if(t.hasClass("x-grid-split")){
39736 this.cellIndex = this.view.getCellIndex(t.dom);
39737 this.split = t.dom;
39738 this.cm = this.grid.colModel;
39739 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
39740 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
39745 endDrag : function(e){
39746 this.view.headersDisabled = false;
39747 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
39748 var diff = endX - this.startPos;
39750 var w = this.cm.getColumnWidth(this.cellIndex);
39751 if (!this.view.mainWrap) {
39754 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
39757 autoOffset : function(){
39758 this.setDelta(0,0);
39762 * Ext JS Library 1.1.1
39763 * Copyright(c) 2006-2007, Ext JS, LLC.
39765 * Originally Released Under LGPL - original licence link has changed is not relivant.
39768 * <script type="text/javascript">
39772 // This is a support class used internally by the Grid components
39773 Roo.grid.GridDragZone = function(grid, config){
39774 this.view = grid.getView();
39775 Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
39776 if(this.view.lockedBody){
39777 this.setHandleElId(Roo.id(this.view.mainBody.dom));
39778 this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
39780 this.scroll = false;
39782 this.ddel = document.createElement('div');
39783 this.ddel.className = 'x-grid-dd-wrap';
39786 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
39787 ddGroup : "GridDD",
39789 getDragData : function(e){
39790 var t = Roo.lib.Event.getTarget(e);
39791 var rowIndex = this.view.findRowIndex(t);
39792 var sm = this.grid.selModel;
39794 //Roo.log(rowIndex);
39796 if (sm.getSelectedCell) {
39797 // cell selection..
39798 if (!sm.getSelectedCell()) {
39801 if (rowIndex != sm.getSelectedCell()[0]) {
39806 if (sm.getSelections && sm.getSelections().length < 1) {
39811 // before it used to all dragging of unseleted... - now we dont do that.
39812 if(rowIndex !== false){
39817 //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
39819 //if(!sm.isSelected(rowIndex) || e.hasModifier()){
39822 if (e.hasModifier()){
39823 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
39826 Roo.log("getDragData");
39831 rowIndex: rowIndex,
39832 selections: sm.getSelections ? sm.getSelections() : (
39833 sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
39840 onInitDrag : function(e){
39841 var data = this.dragData;
39842 this.ddel.innerHTML = this.grid.getDragDropText();
39843 this.proxy.update(this.ddel);
39844 // fire start drag?
39847 afterRepair : function(){
39848 this.dragging = false;
39851 getRepairXY : function(e, data){
39855 onEndDrag : function(data, e){
39859 onValidDrop : function(dd, e, id){
39864 beforeInvalidDrop : function(e, id){
39869 * Ext JS Library 1.1.1
39870 * Copyright(c) 2006-2007, Ext JS, LLC.
39872 * Originally Released Under LGPL - original licence link has changed is not relivant.
39875 * <script type="text/javascript">
39880 * @class Roo.grid.ColumnModel
39881 * @extends Roo.util.Observable
39882 * This is the default implementation of a ColumnModel used by the Grid. It defines
39883 * the columns in the grid.
39886 var colModel = new Roo.grid.ColumnModel([
39887 {header: "Ticker", width: 60, sortable: true, locked: true},
39888 {header: "Company Name", width: 150, sortable: true},
39889 {header: "Market Cap.", width: 100, sortable: true},
39890 {header: "$ Sales", width: 100, sortable: true, renderer: money},
39891 {header: "Employees", width: 100, sortable: true, resizable: false}
39896 * The config options listed for this class are options which may appear in each
39897 * individual column definition.
39898 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
39900 * @param {Object} config An Array of column config objects. See this class's
39901 * config objects for details.
39903 Roo.grid.ColumnModel = function(config){
39905 * The config passed into the constructor
39907 this.config = []; //config;
39910 // if no id, create one
39911 // if the column does not have a dataIndex mapping,
39912 // map it to the order it is in the config
39913 for(var i = 0, len = config.length; i < len; i++){
39914 this.addColumn(config[i]);
39919 * The width of columns which have no width specified (defaults to 100)
39922 this.defaultWidth = 100;
39925 * Default sortable of columns which have no sortable specified (defaults to false)
39928 this.defaultSortable = false;
39932 * @event widthchange
39933 * Fires when the width of a column changes.
39934 * @param {ColumnModel} this
39935 * @param {Number} columnIndex The column index
39936 * @param {Number} newWidth The new width
39938 "widthchange": true,
39940 * @event headerchange
39941 * Fires when the text of a header changes.
39942 * @param {ColumnModel} this
39943 * @param {Number} columnIndex The column index
39944 * @param {Number} newText The new header text
39946 "headerchange": true,
39948 * @event hiddenchange
39949 * Fires when a column is hidden or "unhidden".
39950 * @param {ColumnModel} this
39951 * @param {Number} columnIndex The column index
39952 * @param {Boolean} hidden true if hidden, false otherwise
39954 "hiddenchange": true,
39956 * @event columnmoved
39957 * Fires when a column is moved.
39958 * @param {ColumnModel} this
39959 * @param {Number} oldIndex
39960 * @param {Number} newIndex
39962 "columnmoved" : true,
39964 * @event columlockchange
39965 * Fires when a column's locked state is changed
39966 * @param {ColumnModel} this
39967 * @param {Number} colIndex
39968 * @param {Boolean} locked true if locked
39970 "columnlockchange" : true
39972 Roo.grid.ColumnModel.superclass.constructor.call(this);
39974 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
39976 * @cfg {String} header [required] The header text to display in the Grid view.
39979 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
39982 * @cfg {String} smHeader Header at Bootsrap Small width
39985 * @cfg {String} mdHeader Header at Bootsrap Medium width
39988 * @cfg {String} lgHeader Header at Bootsrap Large width
39991 * @cfg {String} xlHeader Header at Bootsrap extra Large width
39994 * @cfg {String} dataIndex The name of the field in the grid's {@link Roo.data.Store}'s
39995 * {@link Roo.data.Record} definition from which to draw the column's value. If not
39996 * specified, the column's index is used as an index into the Record's data Array.
39999 * @cfg {Number} width The initial width in pixels of the column. Using this
40000 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
40003 * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
40004 * Defaults to the value of the {@link #defaultSortable} property.
40005 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
40008 * @cfg {Boolean} locked True to lock the column in place while scrolling the Grid. Defaults to false.
40011 * @cfg {Boolean} fixed True if the column width cannot be changed. Defaults to false.
40014 * @cfg {Boolean} resizable False to disable column resizing. Defaults to true.
40017 * @cfg {Boolean} hidden True to hide the column. Defaults to false.
40020 * @cfg {Function} renderer A function used to generate HTML markup for a cell
40021 * given the cell's data value. See {@link #setRenderer}. If not specified, the
40022 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
40023 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
40026 * @cfg {Roo.grid.GridEditor} editor For grid editors - returns the grid editor
40029 * @cfg {String} align (left|right) Set the CSS text-align property of the column. Defaults to undefined (left).
40032 * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined (middle)
40035 * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
40038 * @cfg {String} tooltip mouse over tooltip text
40041 * @cfg {Number} xs can be '0' for hidden at this size (number less than 12)
40044 * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
40047 * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
40050 * @cfg {Number} lg can be '0' for hidden at this size (number less than 12)
40053 * @cfg {Number} xl can be '0' for hidden at this size (number less than 12)
40056 * Returns the id of the column at the specified index.
40057 * @param {Number} index The column index
40058 * @return {String} the id
40060 getColumnId : function(index){
40061 return this.config[index].id;
40065 * Returns the column for a specified id.
40066 * @param {String} id The column id
40067 * @return {Object} the column
40069 getColumnById : function(id){
40070 return this.lookup[id];
40075 * Returns the column Object for a specified dataIndex.
40076 * @param {String} dataIndex The column dataIndex
40077 * @return {Object|Boolean} the column or false if not found
40079 getColumnByDataIndex: function(dataIndex){
40080 var index = this.findColumnIndex(dataIndex);
40081 return index > -1 ? this.config[index] : false;
40085 * Returns the index for a specified column id.
40086 * @param {String} id The column id
40087 * @return {Number} the index, or -1 if not found
40089 getIndexById : function(id){
40090 for(var i = 0, len = this.config.length; i < len; i++){
40091 if(this.config[i].id == id){
40099 * Returns the index for a specified column dataIndex.
40100 * @param {String} dataIndex The column dataIndex
40101 * @return {Number} the index, or -1 if not found
40104 findColumnIndex : function(dataIndex){
40105 for(var i = 0, len = this.config.length; i < len; i++){
40106 if(this.config[i].dataIndex == dataIndex){
40114 moveColumn : function(oldIndex, newIndex){
40115 var c = this.config[oldIndex];
40116 this.config.splice(oldIndex, 1);
40117 this.config.splice(newIndex, 0, c);
40118 this.dataMap = null;
40119 this.fireEvent("columnmoved", this, oldIndex, newIndex);
40122 isLocked : function(colIndex){
40123 return this.config[colIndex].locked === true;
40126 setLocked : function(colIndex, value, suppressEvent){
40127 if(this.isLocked(colIndex) == value){
40130 this.config[colIndex].locked = value;
40131 if(!suppressEvent){
40132 this.fireEvent("columnlockchange", this, colIndex, value);
40136 getTotalLockedWidth : function(){
40137 var totalWidth = 0;
40138 for(var i = 0; i < this.config.length; i++){
40139 if(this.isLocked(i) && !this.isHidden(i)){
40140 this.totalWidth += this.getColumnWidth(i);
40146 getLockedCount : function(){
40147 for(var i = 0, len = this.config.length; i < len; i++){
40148 if(!this.isLocked(i)){
40153 return this.config.length;
40157 * Returns the number of columns.
40160 getColumnCount : function(visibleOnly){
40161 if(visibleOnly === true){
40163 for(var i = 0, len = this.config.length; i < len; i++){
40164 if(!this.isHidden(i)){
40170 return this.config.length;
40174 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
40175 * @param {Function} fn
40176 * @param {Object} scope (optional)
40177 * @return {Array} result
40179 getColumnsBy : function(fn, scope){
40181 for(var i = 0, len = this.config.length; i < len; i++){
40182 var c = this.config[i];
40183 if(fn.call(scope||this, c, i) === true){
40191 * Returns true if the specified column is sortable.
40192 * @param {Number} col The column index
40193 * @return {Boolean}
40195 isSortable : function(col){
40196 if(typeof this.config[col].sortable == "undefined"){
40197 return this.defaultSortable;
40199 return this.config[col].sortable;
40203 * Returns the rendering (formatting) function defined for the column.
40204 * @param {Number} col The column index.
40205 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
40207 getRenderer : function(col){
40208 if(!this.config[col].renderer){
40209 return Roo.grid.ColumnModel.defaultRenderer;
40211 return this.config[col].renderer;
40215 * Sets the rendering (formatting) function for a column.
40216 * @param {Number} col The column index
40217 * @param {Function} fn The function to use to process the cell's raw data
40218 * to return HTML markup for the grid view. The render function is called with
40219 * the following parameters:<ul>
40220 * <li>Data value.</li>
40221 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
40222 * <li>css A CSS style string to apply to the table cell.</li>
40223 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
40224 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
40225 * <li>Row index</li>
40226 * <li>Column index</li>
40227 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
40229 setRenderer : function(col, fn){
40230 this.config[col].renderer = fn;
40234 * Returns the width for the specified column.
40235 * @param {Number} col The column index
40236 * @param (optional) {String} gridSize bootstrap width size.
40239 getColumnWidth : function(col, gridSize)
40241 var cfg = this.config[col];
40243 if (typeof(gridSize) == 'undefined') {
40244 return cfg.width * 1 || this.defaultWidth;
40246 if (gridSize === false) { // if we set it..
40247 return cfg.width || false;
40249 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
40251 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
40252 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
40255 return cfg[ sizes[i] ];
40262 * Sets the width for a column.
40263 * @param {Number} col The column index
40264 * @param {Number} width The new width
40266 setColumnWidth : function(col, width, suppressEvent){
40267 this.config[col].width = width;
40268 this.totalWidth = null;
40269 if(!suppressEvent){
40270 this.fireEvent("widthchange", this, col, width);
40275 * Returns the total width of all columns.
40276 * @param {Boolean} includeHidden True to include hidden column widths
40279 getTotalWidth : function(includeHidden){
40280 if(!this.totalWidth){
40281 this.totalWidth = 0;
40282 for(var i = 0, len = this.config.length; i < len; i++){
40283 if(includeHidden || !this.isHidden(i)){
40284 this.totalWidth += this.getColumnWidth(i);
40288 return this.totalWidth;
40292 * Returns the header for the specified column.
40293 * @param {Number} col The column index
40296 getColumnHeader : function(col){
40297 return this.config[col].header;
40301 * Sets the header for a column.
40302 * @param {Number} col The column index
40303 * @param {String} header The new header
40305 setColumnHeader : function(col, header){
40306 this.config[col].header = header;
40307 this.fireEvent("headerchange", this, col, header);
40311 * Returns the tooltip for the specified column.
40312 * @param {Number} col The column index
40315 getColumnTooltip : function(col){
40316 return this.config[col].tooltip;
40319 * Sets the tooltip for a column.
40320 * @param {Number} col The column index
40321 * @param {String} tooltip The new tooltip
40323 setColumnTooltip : function(col, tooltip){
40324 this.config[col].tooltip = tooltip;
40328 * Returns the dataIndex for the specified column.
40329 * @param {Number} col The column index
40332 getDataIndex : function(col){
40333 return this.config[col].dataIndex;
40337 * Sets the dataIndex for a column.
40338 * @param {Number} col The column index
40339 * @param {Number} dataIndex The new dataIndex
40341 setDataIndex : function(col, dataIndex){
40342 this.config[col].dataIndex = dataIndex;
40348 * Returns true if the cell is editable.
40349 * @param {Number} colIndex The column index
40350 * @param {Number} rowIndex The row index - this is nto actually used..?
40351 * @return {Boolean}
40353 isCellEditable : function(colIndex, rowIndex){
40354 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
40358 * Returns the editor defined for the cell/column.
40359 * return false or null to disable editing.
40360 * @param {Number} colIndex The column index
40361 * @param {Number} rowIndex The row index
40364 getCellEditor : function(colIndex, rowIndex){
40365 return this.config[colIndex].editor;
40369 * Sets if a column is editable.
40370 * @param {Number} col The column index
40371 * @param {Boolean} editable True if the column is editable
40373 setEditable : function(col, editable){
40374 this.config[col].editable = editable;
40379 * Returns true if the column is hidden.
40380 * @param {Number} colIndex The column index
40381 * @return {Boolean}
40383 isHidden : function(colIndex){
40384 return this.config[colIndex].hidden;
40389 * Returns true if the column width cannot be changed
40391 isFixed : function(colIndex){
40392 return this.config[colIndex].fixed;
40396 * Returns true if the column can be resized
40397 * @return {Boolean}
40399 isResizable : function(colIndex){
40400 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
40403 * Sets if a column is hidden.
40404 * @param {Number} colIndex The column index
40405 * @param {Boolean} hidden True if the column is hidden
40407 setHidden : function(colIndex, hidden){
40408 this.config[colIndex].hidden = hidden;
40409 this.totalWidth = null;
40410 this.fireEvent("hiddenchange", this, colIndex, hidden);
40414 * Sets the editor for a column.
40415 * @param {Number} col The column index
40416 * @param {Object} editor The editor object
40418 setEditor : function(col, editor){
40419 this.config[col].editor = editor;
40422 * Add a column (experimental...) - defaults to adding to the end..
40423 * @param {Object} config
40425 addColumn : function(c)
40428 var i = this.config.length;
40429 this.config[i] = c;
40431 if(typeof c.dataIndex == "undefined"){
40434 if(typeof c.renderer == "string"){
40435 c.renderer = Roo.util.Format[c.renderer];
40437 if(typeof c.id == "undefined"){
40440 if(c.editor && c.editor.xtype){
40441 c.editor = Roo.factory(c.editor, Roo.grid);
40443 if(c.editor && c.editor.isFormField){
40444 c.editor = new Roo.grid.GridEditor(c.editor);
40446 this.lookup[c.id] = c;
40451 Roo.grid.ColumnModel.defaultRenderer = function(value)
40453 if(typeof value == "object") {
40456 if(typeof value == "string" && value.length < 1){
40460 return String.format("{0}", value);
40463 // Alias for backwards compatibility
40464 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
40467 * Ext JS Library 1.1.1
40468 * Copyright(c) 2006-2007, Ext JS, LLC.
40470 * Originally Released Under LGPL - original licence link has changed is not relivant.
40473 * <script type="text/javascript">
40477 * @class Roo.grid.AbstractSelectionModel
40478 * @extends Roo.util.Observable
40480 * Abstract base class for grid SelectionModels. It provides the interface that should be
40481 * implemented by descendant classes. This class should not be directly instantiated.
40484 Roo.grid.AbstractSelectionModel = function(){
40485 this.locked = false;
40486 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
40489 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
40490 /** @ignore Called by the grid automatically. Do not call directly. */
40491 init : function(grid){
40497 * Locks the selections.
40500 this.locked = true;
40504 * Unlocks the selections.
40506 unlock : function(){
40507 this.locked = false;
40511 * Returns true if the selections are locked.
40512 * @return {Boolean}
40514 isLocked : function(){
40515 return this.locked;
40519 * Ext JS Library 1.1.1
40520 * Copyright(c) 2006-2007, Ext JS, LLC.
40522 * Originally Released Under LGPL - original licence link has changed is not relivant.
40525 * <script type="text/javascript">
40528 * @extends Roo.grid.AbstractSelectionModel
40529 * @class Roo.grid.RowSelectionModel
40530 * The default SelectionModel used by {@link Roo.grid.Grid}.
40531 * It supports multiple selections and keyboard selection/navigation.
40533 * @param {Object} config
40535 Roo.grid.RowSelectionModel = function(config){
40536 Roo.apply(this, config);
40537 this.selections = new Roo.util.MixedCollection(false, function(o){
40542 this.lastActive = false;
40546 * @event selectionchange
40547 * Fires when the selection changes
40548 * @param {SelectionModel} this
40550 "selectionchange" : true,
40552 * @event afterselectionchange
40553 * Fires after the selection changes (eg. by key press or clicking)
40554 * @param {SelectionModel} this
40556 "afterselectionchange" : true,
40558 * @event beforerowselect
40559 * Fires when a row is selected being selected, return false to cancel.
40560 * @param {SelectionModel} this
40561 * @param {Number} rowIndex The selected index
40562 * @param {Boolean} keepExisting False if other selections will be cleared
40564 "beforerowselect" : true,
40567 * Fires when a row is selected.
40568 * @param {SelectionModel} this
40569 * @param {Number} rowIndex The selected index
40570 * @param {Roo.data.Record} r The record
40572 "rowselect" : true,
40574 * @event rowdeselect
40575 * Fires when a row is deselected.
40576 * @param {SelectionModel} this
40577 * @param {Number} rowIndex The selected index
40579 "rowdeselect" : true
40581 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
40582 this.locked = false;
40585 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
40587 * @cfg {Boolean} singleSelect
40588 * True to allow selection of only one row at a time (defaults to false)
40590 singleSelect : false,
40593 initEvents : function(){
40595 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
40596 this.grid.on("mousedown", this.handleMouseDown, this);
40597 }else{ // allow click to work like normal
40598 this.grid.on("rowclick", this.handleDragableRowClick, this);
40600 // bootstrap does not have a view..
40601 var view = this.grid.view ? this.grid.view : this.grid;
40602 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
40603 "up" : function(e){
40605 this.selectPrevious(e.shiftKey);
40606 }else if(this.last !== false && this.lastActive !== false){
40607 var last = this.last;
40608 this.selectRange(this.last, this.lastActive-1);
40609 view.focusRow(this.lastActive);
40610 if(last !== false){
40614 this.selectFirstRow();
40616 this.fireEvent("afterselectionchange", this);
40618 "down" : function(e){
40620 this.selectNext(e.shiftKey);
40621 }else if(this.last !== false && this.lastActive !== false){
40622 var last = this.last;
40623 this.selectRange(this.last, this.lastActive+1);
40624 view.focusRow(this.lastActive);
40625 if(last !== false){
40629 this.selectFirstRow();
40631 this.fireEvent("afterselectionchange", this);
40637 view.on("refresh", this.onRefresh, this);
40638 view.on("rowupdated", this.onRowUpdated, this);
40639 view.on("rowremoved", this.onRemove, this);
40643 onRefresh : function(){
40644 var ds = this.grid.ds, i, v = this.grid.view;
40645 var s = this.selections;
40646 s.each(function(r){
40647 if((i = ds.indexOfId(r.id)) != -1){
40649 s.add(ds.getAt(i)); // updating the selection relate data
40657 onRemove : function(v, index, r){
40658 this.selections.remove(r);
40662 onRowUpdated : function(v, index, r){
40663 if(this.isSelected(r)){
40664 v.onRowSelect(index);
40670 * @param {Array} records The records to select
40671 * @param {Boolean} keepExisting (optional) True to keep existing selections
40673 selectRecords : function(records, keepExisting){
40675 this.clearSelections();
40677 var ds = this.grid.ds;
40678 for(var i = 0, len = records.length; i < len; i++){
40679 this.selectRow(ds.indexOf(records[i]), true);
40684 * Gets the number of selected rows.
40687 getCount : function(){
40688 return this.selections.length;
40692 * Selects the first row in the grid.
40694 selectFirstRow : function(){
40699 * Select the last row.
40700 * @param {Boolean} keepExisting (optional) True to keep existing selections
40702 selectLastRow : function(keepExisting){
40703 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
40707 * Selects the row immediately following the last selected row.
40708 * @param {Boolean} keepExisting (optional) True to keep existing selections
40710 selectNext : function(keepExisting){
40711 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
40712 this.selectRow(this.last+1, keepExisting);
40713 var view = this.grid.view ? this.grid.view : this.grid;
40714 view.focusRow(this.last);
40719 * Selects the row that precedes the last selected row.
40720 * @param {Boolean} keepExisting (optional) True to keep existing selections
40722 selectPrevious : function(keepExisting){
40724 this.selectRow(this.last-1, keepExisting);
40725 var view = this.grid.view ? this.grid.view : this.grid;
40726 view.focusRow(this.last);
40731 * Returns the selected records
40732 * @return {Array} Array of selected records
40734 getSelections : function(){
40735 return [].concat(this.selections.items);
40739 * Returns the first selected record.
40742 getSelected : function(){
40743 return this.selections.itemAt(0);
40748 * Clears all selections.
40750 clearSelections : function(fast){
40755 var ds = this.grid.ds;
40756 var s = this.selections;
40757 s.each(function(r){
40758 this.deselectRow(ds.indexOfId(r.id));
40762 this.selections.clear();
40769 * Selects all rows.
40771 selectAll : function(){
40775 this.selections.clear();
40776 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
40777 this.selectRow(i, true);
40782 * Returns True if there is a selection.
40783 * @return {Boolean}
40785 hasSelection : function(){
40786 return this.selections.length > 0;
40790 * Returns True if the specified row is selected.
40791 * @param {Number/Record} record The record or index of the record to check
40792 * @return {Boolean}
40794 isSelected : function(index){
40795 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
40796 return (r && this.selections.key(r.id) ? true : false);
40800 * Returns True if the specified record id is selected.
40801 * @param {String} id The id of record to check
40802 * @return {Boolean}
40804 isIdSelected : function(id){
40805 return (this.selections.key(id) ? true : false);
40809 handleMouseDown : function(e, t)
40811 var view = this.grid.view ? this.grid.view : this.grid;
40813 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
40816 if(e.shiftKey && this.last !== false){
40817 var last = this.last;
40818 this.selectRange(last, rowIndex, e.ctrlKey);
40819 this.last = last; // reset the last
40820 view.focusRow(rowIndex);
40822 var isSelected = this.isSelected(rowIndex);
40823 if(e.button !== 0 && isSelected){
40824 view.focusRow(rowIndex);
40825 }else if(e.ctrlKey && isSelected){
40826 this.deselectRow(rowIndex);
40827 }else if(!isSelected){
40828 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
40829 view.focusRow(rowIndex);
40832 this.fireEvent("afterselectionchange", this);
40835 handleDragableRowClick : function(grid, rowIndex, e)
40837 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
40838 this.selectRow(rowIndex, false);
40839 var view = this.grid.view ? this.grid.view : this.grid;
40840 view.focusRow(rowIndex);
40841 this.fireEvent("afterselectionchange", this);
40846 * Selects multiple rows.
40847 * @param {Array} rows Array of the indexes of the row to select
40848 * @param {Boolean} keepExisting (optional) True to keep existing selections
40850 selectRows : function(rows, keepExisting){
40852 this.clearSelections();
40854 for(var i = 0, len = rows.length; i < len; i++){
40855 this.selectRow(rows[i], true);
40860 * Selects a range of rows. All rows in between startRow and endRow are also selected.
40861 * @param {Number} startRow The index of the first row in the range
40862 * @param {Number} endRow The index of the last row in the range
40863 * @param {Boolean} keepExisting (optional) True to retain existing selections
40865 selectRange : function(startRow, endRow, keepExisting){
40870 this.clearSelections();
40872 if(startRow <= endRow){
40873 for(var i = startRow; i <= endRow; i++){
40874 this.selectRow(i, true);
40877 for(var i = startRow; i >= endRow; i--){
40878 this.selectRow(i, true);
40884 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
40885 * @param {Number} startRow The index of the first row in the range
40886 * @param {Number} endRow The index of the last row in the range
40888 deselectRange : function(startRow, endRow, preventViewNotify){
40892 for(var i = startRow; i <= endRow; i++){
40893 this.deselectRow(i, preventViewNotify);
40899 * @param {Number} row The index of the row to select
40900 * @param {Boolean} keepExisting (optional) True to keep existing selections
40902 selectRow : function(index, keepExisting, preventViewNotify){
40903 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
40906 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
40907 if(!keepExisting || this.singleSelect){
40908 this.clearSelections();
40910 var r = this.grid.ds.getAt(index);
40911 this.selections.add(r);
40912 this.last = this.lastActive = index;
40913 if(!preventViewNotify){
40914 var view = this.grid.view ? this.grid.view : this.grid;
40915 view.onRowSelect(index);
40917 this.fireEvent("rowselect", this, index, r);
40918 this.fireEvent("selectionchange", this);
40924 * @param {Number} row The index of the row to deselect
40926 deselectRow : function(index, preventViewNotify){
40930 if(this.last == index){
40933 if(this.lastActive == index){
40934 this.lastActive = false;
40936 var r = this.grid.ds.getAt(index);
40937 this.selections.remove(r);
40938 if(!preventViewNotify){
40939 var view = this.grid.view ? this.grid.view : this.grid;
40940 view.onRowDeselect(index);
40942 this.fireEvent("rowdeselect", this, index);
40943 this.fireEvent("selectionchange", this);
40947 restoreLast : function(){
40949 this.last = this._last;
40954 acceptsNav : function(row, col, cm){
40955 return !cm.isHidden(col) && cm.isCellEditable(col, row);
40959 onEditorKey : function(field, e){
40960 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
40965 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
40967 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
40969 }else if(k == e.ENTER && !e.ctrlKey){
40973 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
40975 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
40977 }else if(k == e.ESC){
40981 g.startEditing(newCell[0], newCell[1]);
40986 * Ext JS Library 1.1.1
40987 * Copyright(c) 2006-2007, Ext JS, LLC.
40989 * Originally Released Under LGPL - original licence link has changed is not relivant.
40992 * <script type="text/javascript">
40995 * @class Roo.grid.CellSelectionModel
40996 * @extends Roo.grid.AbstractSelectionModel
40997 * This class provides the basic implementation for cell selection in a grid.
40999 * @param {Object} config The object containing the configuration of this model.
41000 * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
41002 Roo.grid.CellSelectionModel = function(config){
41003 Roo.apply(this, config);
41005 this.selection = null;
41009 * @event beforerowselect
41010 * Fires before a cell is selected.
41011 * @param {SelectionModel} this
41012 * @param {Number} rowIndex The selected row index
41013 * @param {Number} colIndex The selected cell index
41015 "beforecellselect" : true,
41017 * @event cellselect
41018 * Fires when a cell is selected.
41019 * @param {SelectionModel} this
41020 * @param {Number} rowIndex The selected row index
41021 * @param {Number} colIndex The selected cell index
41023 "cellselect" : true,
41025 * @event selectionchange
41026 * Fires when the active selection changes.
41027 * @param {SelectionModel} this
41028 * @param {Object} selection null for no selection or an object (o) with two properties
41030 <li>o.record: the record object for the row the selection is in</li>
41031 <li>o.cell: An array of [rowIndex, columnIndex]</li>
41034 "selectionchange" : true,
41037 * Fires when the tab (or enter) was pressed on the last editable cell
41038 * You can use this to trigger add new row.
41039 * @param {SelectionModel} this
41043 * @event beforeeditnext
41044 * Fires before the next editable sell is made active
41045 * You can use this to skip to another cell or fire the tabend
41046 * if you set cell to false
41047 * @param {Object} eventdata object : { cell : [ row, col ] }
41049 "beforeeditnext" : true
41051 Roo.grid.CellSelectionModel.superclass.constructor.call(this);
41054 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel, {
41056 enter_is_tab: false,
41059 initEvents : function(){
41060 this.grid.on("mousedown", this.handleMouseDown, this);
41061 this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
41062 var view = this.grid.view;
41063 view.on("refresh", this.onViewChange, this);
41064 view.on("rowupdated", this.onRowUpdated, this);
41065 view.on("beforerowremoved", this.clearSelections, this);
41066 view.on("beforerowsinserted", this.clearSelections, this);
41067 if(this.grid.isEditor){
41068 this.grid.on("beforeedit", this.beforeEdit, this);
41073 beforeEdit : function(e){
41074 this.select(e.row, e.column, false, true, e.record);
41078 onRowUpdated : function(v, index, r){
41079 if(this.selection && this.selection.record == r){
41080 v.onCellSelect(index, this.selection.cell[1]);
41085 onViewChange : function(){
41086 this.clearSelections(true);
41090 * Returns the currently selected cell,.
41091 * @return {Array} The selected cell (row, column) or null if none selected.
41093 getSelectedCell : function(){
41094 return this.selection ? this.selection.cell : null;
41098 * Clears all selections.
41099 * @param {Boolean} true to prevent the gridview from being notified about the change.
41101 clearSelections : function(preventNotify){
41102 var s = this.selection;
41104 if(preventNotify !== true){
41105 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
41107 this.selection = null;
41108 this.fireEvent("selectionchange", this, null);
41113 * Returns true if there is a selection.
41114 * @return {Boolean}
41116 hasSelection : function(){
41117 return this.selection ? true : false;
41121 handleMouseDown : function(e, t){
41122 var v = this.grid.getView();
41123 if(this.isLocked()){
41126 var row = v.findRowIndex(t);
41127 var cell = v.findCellIndex(t);
41128 if(row !== false && cell !== false){
41129 this.select(row, cell);
41135 * @param {Number} rowIndex
41136 * @param {Number} collIndex
41138 select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
41139 if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
41140 this.clearSelections();
41141 r = r || this.grid.dataSource.getAt(rowIndex);
41144 cell : [rowIndex, colIndex]
41146 if(!preventViewNotify){
41147 var v = this.grid.getView();
41148 v.onCellSelect(rowIndex, colIndex);
41149 if(preventFocus !== true){
41150 v.focusCell(rowIndex, colIndex);
41153 this.fireEvent("cellselect", this, rowIndex, colIndex);
41154 this.fireEvent("selectionchange", this, this.selection);
41159 isSelectable : function(rowIndex, colIndex, cm){
41160 return !cm.isHidden(colIndex);
41164 handleKeyDown : function(e){
41165 //Roo.log('Cell Sel Model handleKeyDown');
41166 if(!e.isNavKeyPress()){
41169 var g = this.grid, s = this.selection;
41172 var cell = g.walkCells(0, 0, 1, this.isSelectable, this);
41174 this.select(cell[0], cell[1]);
41179 var walk = function(row, col, step){
41180 return g.walkCells(row, col, step, sm.isSelectable, sm);
41182 var k = e.getKey(), r = s.cell[0], c = s.cell[1];
41189 // handled by onEditorKey
41190 if (g.isEditor && g.editing) {
41194 newCell = walk(r, c-1, -1);
41196 newCell = walk(r, c+1, 1);
41201 newCell = walk(r+1, c, 1);
41205 newCell = walk(r-1, c, -1);
41209 newCell = walk(r, c+1, 1);
41213 newCell = walk(r, c-1, -1);
41218 if(g.isEditor && !g.editing){
41219 g.startEditing(r, c);
41228 this.select(newCell[0], newCell[1]);
41234 acceptsNav : function(row, col, cm){
41235 return !cm.isHidden(col) && cm.isCellEditable(col, row);
41239 * @param {Number} field (not used) - as it's normally used as a listener
41240 * @param {Number} e - event - fake it by using
41242 * var e = Roo.EventObjectImpl.prototype;
41243 * e.keyCode = e.TAB
41247 onEditorKey : function(field, e){
41249 var k = e.getKey(),
41252 ed = g.activeEditor,
41254 ///Roo.log('onEditorKey' + k);
41257 if (this.enter_is_tab && k == e.ENTER) {
41263 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
41265 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41271 } else if(k == e.ENTER && !e.ctrlKey){
41274 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
41276 } else if(k == e.ESC){
41281 var ecall = { cell : newCell, forward : forward };
41282 this.fireEvent('beforeeditnext', ecall );
41283 newCell = ecall.cell;
41284 forward = ecall.forward;
41288 //Roo.log('next cell after edit');
41289 g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
41290 } else if (forward) {
41291 // tabbed past last
41292 this.fireEvent.defer(100, this, ['tabend',this]);
41297 * Ext JS Library 1.1.1
41298 * Copyright(c) 2006-2007, Ext JS, LLC.
41300 * Originally Released Under LGPL - original licence link has changed is not relivant.
41303 * <script type="text/javascript">
41307 * @class Roo.grid.EditorGrid
41308 * @extends Roo.grid.Grid
41309 * Class for creating and editable grid.
41310 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41311 * The container MUST have some type of size defined for the grid to fill. The container will be
41312 * automatically set to position relative if it isn't already.
41313 * @param {Object} dataSource The data model to bind to
41314 * @param {Object} colModel The column model with info about this grid's columns
41316 Roo.grid.EditorGrid = function(container, config){
41317 Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
41318 this.getGridEl().addClass("xedit-grid");
41320 if(!this.selModel){
41321 this.selModel = new Roo.grid.CellSelectionModel();
41324 this.activeEditor = null;
41328 * @event beforeedit
41329 * Fires before cell editing is triggered. The edit event object has the following properties <br />
41330 * <ul style="padding:5px;padding-left:16px;">
41331 * <li>grid - This grid</li>
41332 * <li>record - The record being edited</li>
41333 * <li>field - The field name being edited</li>
41334 * <li>value - The value for the field being edited.</li>
41335 * <li>row - The grid row index</li>
41336 * <li>column - The grid column index</li>
41337 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41339 * @param {Object} e An edit event (see above for description)
41341 "beforeedit" : true,
41344 * Fires after a cell is edited. <br />
41345 * <ul style="padding:5px;padding-left:16px;">
41346 * <li>grid - This grid</li>
41347 * <li>record - The record being edited</li>
41348 * <li>field - The field name being edited</li>
41349 * <li>value - The value being set</li>
41350 * <li>originalValue - The original value for the field, before the edit.</li>
41351 * <li>row - The grid row index</li>
41352 * <li>column - The grid column index</li>
41354 * @param {Object} e An edit event (see above for description)
41356 "afteredit" : true,
41358 * @event validateedit
41359 * Fires after a cell is edited, but before the value is set in the record.
41360 * You can use this to modify the value being set in the field, Return false
41361 * to cancel the change. The edit event object has the following properties <br />
41362 * <ul style="padding:5px;padding-left:16px;">
41363 * <li>editor - This editor</li>
41364 * <li>grid - This grid</li>
41365 * <li>record - The record being edited</li>
41366 * <li>field - The field name being edited</li>
41367 * <li>value - The value being set</li>
41368 * <li>originalValue - The original value for the field, before the edit.</li>
41369 * <li>row - The grid row index</li>
41370 * <li>column - The grid column index</li>
41371 * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
41373 * @param {Object} e An edit event (see above for description)
41375 "validateedit" : true
41377 this.on("bodyscroll", this.stopEditing, this);
41378 this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick, this);
41381 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
41383 * @cfg {Number} clicksToEdit
41384 * The number of clicks on a cell required to display the cell's editor (defaults to 2)
41391 trackMouseOver: false, // causes very odd FF errors
41393 onCellDblClick : function(g, row, col){
41394 this.startEditing(row, col);
41397 onEditComplete : function(ed, value, startValue){
41398 this.editing = false;
41399 this.activeEditor = null;
41400 ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
41402 var field = this.colModel.getDataIndex(ed.col);
41407 originalValue: startValue,
41414 var cell = Roo.get(this.view.getCell(ed.row,ed.col));
41417 if(String(value) !== String(startValue)){
41419 if(this.fireEvent("validateedit", e) !== false && !e.cancel){
41420 r.set(field, e.value);
41421 // if we are dealing with a combo box..
41422 // then we also set the 'name' colum to be the displayField
41423 if (ed.field.displayField && ed.field.name) {
41424 r.set(ed.field.name, ed.field.el.dom.value);
41427 delete e.cancel; //?? why!!!
41428 this.fireEvent("afteredit", e);
41431 this.fireEvent("afteredit", e); // always fire it!
41433 this.view.focusCell(ed.row, ed.col);
41437 * Starts editing the specified for the specified row/column
41438 * @param {Number} rowIndex
41439 * @param {Number} colIndex
41441 startEditing : function(row, col){
41442 this.stopEditing();
41443 if(this.colModel.isCellEditable(col, row)){
41444 this.view.ensureVisible(row, col, true);
41446 var r = this.dataSource.getAt(row);
41447 var field = this.colModel.getDataIndex(col);
41448 var cell = Roo.get(this.view.getCell(row,col));
41453 value: r.data[field],
41458 if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
41459 this.editing = true;
41460 var ed = this.colModel.getCellEditor(col, row);
41466 ed.render(ed.parentEl || document.body);
41472 (function(){ // complex but required for focus issues in safari, ie and opera
41476 ed.on("complete", this.onEditComplete, this, {single: true});
41477 ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
41478 this.activeEditor = ed;
41479 var v = r.data[field];
41480 ed.startEdit(this.view.getCell(row, col), v);
41481 // combo's with 'displayField and name set
41482 if (ed.field.displayField && ed.field.name) {
41483 ed.field.el.dom.value = r.data[ed.field.name];
41487 }).defer(50, this);
41493 * Stops any active editing
41495 stopEditing : function(){
41496 if(this.activeEditor){
41497 this.activeEditor.completeEdit();
41499 this.activeEditor = null;
41503 * Called to get grid's drag proxy text, by default returns this.ddText.
41506 getDragDropText : function(){
41507 var count = this.selModel.getSelectedCell() ? 1 : 0;
41508 return String.format(this.ddText, count, count == 1 ? '' : 's');
41513 * Ext JS Library 1.1.1
41514 * Copyright(c) 2006-2007, Ext JS, LLC.
41516 * Originally Released Under LGPL - original licence link has changed is not relivant.
41519 * <script type="text/javascript">
41522 // private - not really -- you end up using it !
41523 // This is a support class used internally by the Grid components
41526 * @class Roo.grid.GridEditor
41527 * @extends Roo.Editor
41528 * Class for creating and editable grid elements.
41529 * @param {Object} config any settings (must include field)
41531 Roo.grid.GridEditor = function(field, config){
41532 if (!config && field.field) {
41534 field = Roo.factory(config.field, Roo.form);
41536 Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
41537 field.monitorTab = false;
41540 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
41543 * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
41546 alignment: "tl-tl",
41549 cls: "x-small-editor x-grid-editor",
41554 * Ext JS Library 1.1.1
41555 * Copyright(c) 2006-2007, Ext JS, LLC.
41557 * Originally Released Under LGPL - original licence link has changed is not relivant.
41560 * <script type="text/javascript">
41565 Roo.grid.PropertyRecord = Roo.data.Record.create([
41566 {name:'name',type:'string'}, 'value'
41570 Roo.grid.PropertyStore = function(grid, source){
41572 this.store = new Roo.data.Store({
41573 recordType : Roo.grid.PropertyRecord
41575 this.store.on('update', this.onUpdate, this);
41577 this.setSource(source);
41579 Roo.grid.PropertyStore.superclass.constructor.call(this);
41584 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
41585 setSource : function(o){
41587 this.store.removeAll();
41590 if(this.isEditableValue(o[k])){
41591 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
41594 this.store.loadRecords({records: data}, {}, true);
41597 onUpdate : function(ds, record, type){
41598 if(type == Roo.data.Record.EDIT){
41599 var v = record.data['value'];
41600 var oldValue = record.modified['value'];
41601 if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
41602 this.source[record.id] = v;
41604 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
41611 getProperty : function(row){
41612 return this.store.getAt(row);
41615 isEditableValue: function(val){
41616 if(val && val instanceof Date){
41618 }else if(typeof val == 'object' || typeof val == 'function'){
41624 setValue : function(prop, value){
41625 this.source[prop] = value;
41626 this.store.getById(prop).set('value', value);
41629 getSource : function(){
41630 return this.source;
41634 Roo.grid.PropertyColumnModel = function(grid, store){
41637 g.PropertyColumnModel.superclass.constructor.call(this, [
41638 {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
41639 {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
41641 this.store = store;
41642 this.bselect = Roo.DomHelper.append(document.body, {
41643 tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
41644 {tag: 'option', value: 'true', html: 'true'},
41645 {tag: 'option', value: 'false', html: 'false'}
41648 Roo.id(this.bselect);
41651 'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
41652 'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
41653 'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
41654 'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
41655 'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
41657 this.renderCellDelegate = this.renderCell.createDelegate(this);
41658 this.renderPropDelegate = this.renderProp.createDelegate(this);
41661 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
41665 valueText : 'Value',
41667 dateFormat : 'm/j/Y',
41670 renderDate : function(dateVal){
41671 return dateVal.dateFormat(this.dateFormat);
41674 renderBool : function(bVal){
41675 return bVal ? 'true' : 'false';
41678 isCellEditable : function(colIndex, rowIndex){
41679 return colIndex == 1;
41682 getRenderer : function(col){
41684 this.renderCellDelegate : this.renderPropDelegate;
41687 renderProp : function(v){
41688 return this.getPropertyName(v);
41691 renderCell : function(val){
41693 if(val instanceof Date){
41694 rv = this.renderDate(val);
41695 }else if(typeof val == 'boolean'){
41696 rv = this.renderBool(val);
41698 return Roo.util.Format.htmlEncode(rv);
41701 getPropertyName : function(name){
41702 var pn = this.grid.propertyNames;
41703 return pn && pn[name] ? pn[name] : name;
41706 getCellEditor : function(colIndex, rowIndex){
41707 var p = this.store.getProperty(rowIndex);
41708 var n = p.data['name'], val = p.data['value'];
41710 if(typeof(this.grid.customEditors[n]) == 'string'){
41711 return this.editors[this.grid.customEditors[n]];
41713 if(typeof(this.grid.customEditors[n]) != 'undefined'){
41714 return this.grid.customEditors[n];
41716 if(val instanceof Date){
41717 return this.editors['date'];
41718 }else if(typeof val == 'number'){
41719 return this.editors['number'];
41720 }else if(typeof val == 'boolean'){
41721 return this.editors['boolean'];
41723 return this.editors['string'];
41729 * @class Roo.grid.PropertyGrid
41730 * @extends Roo.grid.EditorGrid
41731 * This class represents the interface of a component based property grid control.
41732 * <br><br>Usage:<pre><code>
41733 var grid = new Roo.grid.PropertyGrid("my-container-id", {
41741 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41742 * The container MUST have some type of size defined for the grid to fill. The container will be
41743 * automatically set to position relative if it isn't already.
41744 * @param {Object} config A config object that sets properties on this grid.
41746 Roo.grid.PropertyGrid = function(container, config){
41747 config = config || {};
41748 var store = new Roo.grid.PropertyStore(this);
41749 this.store = store;
41750 var cm = new Roo.grid.PropertyColumnModel(this, store);
41751 store.store.sort('name', 'ASC');
41752 Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
41755 enableColLock:false,
41756 enableColumnMove:false,
41758 trackMouseOver: false,
41761 this.getGridEl().addClass('x-props-grid');
41762 this.lastEditRow = null;
41763 this.on('columnresize', this.onColumnResize, this);
41766 * @event beforepropertychange
41767 * Fires before a property changes (return false to stop?)
41768 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41769 * @param {String} id Record Id
41770 * @param {String} newval New Value
41771 * @param {String} oldval Old Value
41773 "beforepropertychange": true,
41775 * @event propertychange
41776 * Fires after a property changes
41777 * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
41778 * @param {String} id Record Id
41779 * @param {String} newval New Value
41780 * @param {String} oldval Old Value
41782 "propertychange": true
41784 this.customEditors = this.customEditors || {};
41786 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
41789 * @cfg {Object} customEditors map of colnames=> custom editors.
41790 * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
41791 * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
41792 * false disables editing of the field.
41796 * @cfg {Object} propertyNames map of property Names to their displayed value
41799 render : function(){
41800 Roo.grid.PropertyGrid.superclass.render.call(this);
41801 this.autoSize.defer(100, this);
41804 autoSize : function(){
41805 Roo.grid.PropertyGrid.superclass.autoSize.call(this);
41807 this.view.fitColumns();
41811 onColumnResize : function(){
41812 this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
41816 * Sets the data for the Grid
41817 * accepts a Key => Value object of all the elements avaiable.
41818 * @param {Object} data to appear in grid.
41820 setSource : function(source){
41821 this.store.setSource(source);
41825 * Gets all the data from the grid.
41826 * @return {Object} data data stored in grid
41828 getSource : function(){
41829 return this.store.getSource();
41838 * @class Roo.grid.Calendar
41839 * @extends Roo.grid.Grid
41840 * This class extends the Grid to provide a calendar widget
41841 * <br><br>Usage:<pre><code>
41842 var grid = new Roo.grid.Calendar("my-container-id", {
41845 selModel: mySelectionModel,
41846 autoSizeColumns: true,
41847 monitorWindowResize: false,
41848 trackMouseOver: true
41849 eventstore : real data store..
41855 * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
41856 * The container MUST have some type of size defined for the grid to fill. The container will be
41857 * automatically set to position relative if it isn't already.
41858 * @param {Object} config A config object that sets properties on this grid.
41860 Roo.grid.Calendar = function(container, config){
41861 // initialize the container
41862 this.container = Roo.get(container);
41863 this.container.update("");
41864 this.container.setStyle("overflow", "hidden");
41865 this.container.addClass('x-grid-container');
41867 this.id = this.container.id;
41869 Roo.apply(this, config);
41870 // check and correct shorthanded configs
41874 for (var r = 0;r < 6;r++) {
41877 for (var c =0;c < 7;c++) {
41881 if (this.eventStore) {
41882 this.eventStore= Roo.factory(this.eventStore, Roo.data);
41883 this.eventStore.on('load',this.onLoad, this);
41884 this.eventStore.on('beforeload',this.clearEvents, this);
41888 this.dataSource = new Roo.data.Store({
41889 proxy: new Roo.data.MemoryProxy(rows),
41890 reader: new Roo.data.ArrayReader({}, [
41891 'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
41894 this.dataSource.load();
41895 this.ds = this.dataSource;
41896 this.ds.xmodule = this.xmodule || false;
41899 var cellRender = function(v,x,r)
41901 return String.format(
41902 '<div class="fc-day fc-widget-content"><div>' +
41903 '<div class="fc-event-container"></div>' +
41904 '<div class="fc-day-number">{0}</div>'+
41906 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
41907 '</div></div>', v);
41912 this.colModel = new Roo.grid.ColumnModel( [
41914 xtype: 'ColumnModel',
41916 dataIndex : 'weekday0',
41918 renderer : cellRender
41921 xtype: 'ColumnModel',
41923 dataIndex : 'weekday1',
41925 renderer : cellRender
41928 xtype: 'ColumnModel',
41930 dataIndex : 'weekday2',
41931 header : 'Tuesday',
41932 renderer : cellRender
41935 xtype: 'ColumnModel',
41937 dataIndex : 'weekday3',
41938 header : 'Wednesday',
41939 renderer : cellRender
41942 xtype: 'ColumnModel',
41944 dataIndex : 'weekday4',
41945 header : 'Thursday',
41946 renderer : cellRender
41949 xtype: 'ColumnModel',
41951 dataIndex : 'weekday5',
41953 renderer : cellRender
41956 xtype: 'ColumnModel',
41958 dataIndex : 'weekday6',
41959 header : 'Saturday',
41960 renderer : cellRender
41963 this.cm = this.colModel;
41964 this.cm.xmodule = this.xmodule || false;
41968 //this.selModel = new Roo.grid.CellSelectionModel();
41969 //this.sm = this.selModel;
41970 //this.selModel.init(this);
41974 this.container.setWidth(this.width);
41978 this.container.setHeight(this.height);
41985 * The raw click event for the entire grid.
41986 * @param {Roo.EventObject} e
41991 * The raw dblclick event for the entire grid.
41992 * @param {Roo.EventObject} e
41996 * @event contextmenu
41997 * The raw contextmenu event for the entire grid.
41998 * @param {Roo.EventObject} e
42000 "contextmenu" : true,
42003 * The raw mousedown event for the entire grid.
42004 * @param {Roo.EventObject} e
42006 "mousedown" : true,
42009 * The raw mouseup event for the entire grid.
42010 * @param {Roo.EventObject} e
42015 * The raw mouseover event for the entire grid.
42016 * @param {Roo.EventObject} e
42018 "mouseover" : true,
42021 * The raw mouseout event for the entire grid.
42022 * @param {Roo.EventObject} e
42027 * The raw keypress event for the entire grid.
42028 * @param {Roo.EventObject} e
42033 * The raw keydown event for the entire grid.
42034 * @param {Roo.EventObject} e
42042 * Fires when a cell is clicked
42043 * @param {Grid} this
42044 * @param {Number} rowIndex
42045 * @param {Number} columnIndex
42046 * @param {Roo.EventObject} e
42048 "cellclick" : true,
42050 * @event celldblclick
42051 * Fires when a cell is double clicked
42052 * @param {Grid} this
42053 * @param {Number} rowIndex
42054 * @param {Number} columnIndex
42055 * @param {Roo.EventObject} e
42057 "celldblclick" : true,
42060 * Fires when a row is clicked
42061 * @param {Grid} this
42062 * @param {Number} rowIndex
42063 * @param {Roo.EventObject} e
42067 * @event rowdblclick
42068 * Fires when a row is double clicked
42069 * @param {Grid} this
42070 * @param {Number} rowIndex
42071 * @param {Roo.EventObject} e
42073 "rowdblclick" : true,
42075 * @event headerclick
42076 * Fires when a header is clicked
42077 * @param {Grid} this
42078 * @param {Number} columnIndex
42079 * @param {Roo.EventObject} e
42081 "headerclick" : true,
42083 * @event headerdblclick
42084 * Fires when a header cell is double clicked
42085 * @param {Grid} this
42086 * @param {Number} columnIndex
42087 * @param {Roo.EventObject} e
42089 "headerdblclick" : true,
42091 * @event rowcontextmenu
42092 * Fires when a row is right clicked
42093 * @param {Grid} this
42094 * @param {Number} rowIndex
42095 * @param {Roo.EventObject} e
42097 "rowcontextmenu" : true,
42099 * @event cellcontextmenu
42100 * Fires when a cell is right clicked
42101 * @param {Grid} this
42102 * @param {Number} rowIndex
42103 * @param {Number} cellIndex
42104 * @param {Roo.EventObject} e
42106 "cellcontextmenu" : true,
42108 * @event headercontextmenu
42109 * Fires when a header is right clicked
42110 * @param {Grid} this
42111 * @param {Number} columnIndex
42112 * @param {Roo.EventObject} e
42114 "headercontextmenu" : true,
42116 * @event bodyscroll
42117 * Fires when the body element is scrolled
42118 * @param {Number} scrollLeft
42119 * @param {Number} scrollTop
42121 "bodyscroll" : true,
42123 * @event columnresize
42124 * Fires when the user resizes a column
42125 * @param {Number} columnIndex
42126 * @param {Number} newSize
42128 "columnresize" : true,
42130 * @event columnmove
42131 * Fires when the user moves a column
42132 * @param {Number} oldIndex
42133 * @param {Number} newIndex
42135 "columnmove" : true,
42138 * Fires when row(s) start being dragged
42139 * @param {Grid} this
42140 * @param {Roo.GridDD} dd The drag drop object
42141 * @param {event} e The raw browser event
42143 "startdrag" : true,
42146 * Fires when a drag operation is complete
42147 * @param {Grid} this
42148 * @param {Roo.GridDD} dd The drag drop object
42149 * @param {event} e The raw browser event
42154 * Fires when dragged row(s) are dropped on a valid DD target
42155 * @param {Grid} this
42156 * @param {Roo.GridDD} dd The drag drop object
42157 * @param {String} targetId The target drag drop object
42158 * @param {event} e The raw browser event
42163 * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
42164 * @param {Grid} this
42165 * @param {Roo.GridDD} dd The drag drop object
42166 * @param {String} targetId The target drag drop object
42167 * @param {event} e The raw browser event
42172 * Fires when the dragged row(s) first cross another DD target while being dragged
42173 * @param {Grid} this
42174 * @param {Roo.GridDD} dd The drag drop object
42175 * @param {String} targetId The target drag drop object
42176 * @param {event} e The raw browser event
42178 "dragenter" : true,
42181 * Fires when the dragged row(s) leave another DD target while being dragged
42182 * @param {Grid} this
42183 * @param {Roo.GridDD} dd The drag drop object
42184 * @param {String} targetId The target drag drop object
42185 * @param {event} e The raw browser event
42190 * Fires when a row is rendered, so you can change add a style to it.
42191 * @param {GridView} gridview The grid view
42192 * @param {Object} rowcfg contains record rowIndex and rowClass - set rowClass to add a style.
42198 * Fires when the grid is rendered
42199 * @param {Grid} grid
42204 * Fires when a date is selected
42205 * @param {DatePicker} this
42206 * @param {Date} date The selected date
42210 * @event monthchange
42211 * Fires when the displayed month changes
42212 * @param {DatePicker} this
42213 * @param {Date} date The selected month
42215 'monthchange': true,
42217 * @event evententer
42218 * Fires when mouse over an event
42219 * @param {Calendar} this
42220 * @param {event} Event
42222 'evententer': true,
42224 * @event eventleave
42225 * Fires when the mouse leaves an
42226 * @param {Calendar} this
42229 'eventleave': true,
42231 * @event eventclick
42232 * Fires when the mouse click an
42233 * @param {Calendar} this
42236 'eventclick': true,
42238 * @event eventrender
42239 * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
42240 * @param {Calendar} this
42241 * @param {data} data to be modified
42243 'eventrender': true
42247 Roo.grid.Grid.superclass.constructor.call(this);
42248 this.on('render', function() {
42249 this.view.el.addClass('x-grid-cal');
42251 (function() { this.setDate(new Date()); }).defer(100,this); //default today..
42255 if (!Roo.grid.Calendar.style) {
42256 Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
42259 '.x-grid-cal .x-grid-col' : {
42260 height: 'auto !important',
42261 'vertical-align': 'top'
42263 '.x-grid-cal .fc-event-hori' : {
42274 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
42276 * @cfg {Store} eventStore The store that loads events.
42281 activeDate : false,
42284 monitorWindowResize : false,
42287 resizeColumns : function() {
42288 var col = (this.view.el.getWidth() / 7) - 3;
42289 // loop through cols, and setWidth
42290 for(var i =0 ; i < 7 ; i++){
42291 this.cm.setColumnWidth(i, col);
42294 setDate :function(date) {
42296 Roo.log('setDate?');
42298 this.resizeColumns();
42299 var vd = this.activeDate;
42300 this.activeDate = date;
42301 // if(vd && this.el){
42302 // var t = date.getTime();
42303 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
42304 // Roo.log('using add remove');
42306 // this.fireEvent('monthchange', this, date);
42308 // this.cells.removeClass("fc-state-highlight");
42309 // this.cells.each(function(c){
42310 // if(c.dateValue == t){
42311 // c.addClass("fc-state-highlight");
42312 // setTimeout(function(){
42313 // try{c.dom.firstChild.focus();}catch(e){}
42323 var days = date.getDaysInMonth();
42325 var firstOfMonth = date.getFirstDateOfMonth();
42326 var startingPos = firstOfMonth.getDay()-this.startDay;
42328 if(startingPos < this.startDay){
42332 var pm = date.add(Date.MONTH, -1);
42333 var prevStart = pm.getDaysInMonth()-startingPos;
42337 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42339 this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
42340 //this.cells.addClassOnOver('fc-state-hover');
42342 var cells = this.cells.elements;
42343 var textEls = this.textNodes;
42345 //Roo.each(cells, function(cell){
42346 // cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
42349 days += startingPos;
42351 // convert everything to numbers so it's fast
42352 var day = 86400000;
42353 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
42356 //Roo.log(prevStart);
42358 var today = new Date().clearTime().getTime();
42359 var sel = date.clearTime().getTime();
42360 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
42361 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
42362 var ddMatch = this.disabledDatesRE;
42363 var ddText = this.disabledDatesText;
42364 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
42365 var ddaysText = this.disabledDaysText;
42366 var format = this.format;
42368 var setCellClass = function(cal, cell){
42370 //Roo.log('set Cell Class');
42372 var t = d.getTime();
42377 cell.dateValue = t;
42379 cell.className += " fc-today";
42380 cell.className += " fc-state-highlight";
42381 cell.title = cal.todayText;
42384 // disable highlight in other month..
42385 cell.className += " fc-state-highlight";
42390 //cell.className = " fc-state-disabled";
42391 cell.title = cal.minText;
42395 //cell.className = " fc-state-disabled";
42396 cell.title = cal.maxText;
42400 if(ddays.indexOf(d.getDay()) != -1){
42401 // cell.title = ddaysText;
42402 // cell.className = " fc-state-disabled";
42405 if(ddMatch && format){
42406 var fvalue = d.dateFormat(format);
42407 if(ddMatch.test(fvalue)){
42408 cell.title = ddText.replace("%0", fvalue);
42409 cell.className = " fc-state-disabled";
42413 if (!cell.initialClassName) {
42414 cell.initialClassName = cell.dom.className;
42417 cell.dom.className = cell.initialClassName + ' ' + cell.className;
42422 for(; i < startingPos; i++) {
42423 cells[i].dayName = (++prevStart);
42424 Roo.log(textEls[i]);
42425 d.setDate(d.getDate()+1);
42427 //cells[i].className = "fc-past fc-other-month";
42428 setCellClass(this, cells[i]);
42433 for(; i < days; i++){
42434 intDay = i - startingPos + 1;
42435 cells[i].dayName = (intDay);
42436 d.setDate(d.getDate()+1);
42438 cells[i].className = ''; // "x-date-active";
42439 setCellClass(this, cells[i]);
42443 for(; i < 42; i++) {
42444 //textEls[i].innerHTML = (++extraDays);
42446 d.setDate(d.getDate()+1);
42447 cells[i].dayName = (++extraDays);
42448 cells[i].className = "fc-future fc-other-month";
42449 setCellClass(this, cells[i]);
42452 //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
42454 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
42456 // this will cause all the cells to mis
42459 for (var r = 0;r < 6;r++) {
42460 for (var c =0;c < 7;c++) {
42461 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
42465 this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
42466 for(i=0;i<cells.length;i++) {
42468 this.cells.elements[i].dayName = cells[i].dayName ;
42469 this.cells.elements[i].className = cells[i].className;
42470 this.cells.elements[i].initialClassName = cells[i].initialClassName ;
42471 this.cells.elements[i].title = cells[i].title ;
42472 this.cells.elements[i].dateValue = cells[i].dateValue ;
42478 //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
42479 //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
42481 ////if(totalRows != 6){
42482 //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
42483 // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
42486 this.fireEvent('monthchange', this, date);
42491 * Returns the grid's SelectionModel.
42492 * @return {SelectionModel}
42494 getSelectionModel : function(){
42495 if(!this.selModel){
42496 this.selModel = new Roo.grid.CellSelectionModel();
42498 return this.selModel;
42502 this.eventStore.load()
42508 findCell : function(dt) {
42509 dt = dt.clearTime().getTime();
42511 this.cells.each(function(c){
42512 //Roo.log("check " +c.dateValue + '?=' + dt);
42513 if(c.dateValue == dt){
42523 findCells : function(rec) {
42524 var s = rec.data.start_dt.clone().clearTime().getTime();
42526 var e= rec.data.end_dt.clone().clearTime().getTime();
42529 this.cells.each(function(c){
42530 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
42532 if(c.dateValue > e){
42535 if(c.dateValue < s){
42544 findBestRow: function(cells)
42548 for (var i =0 ; i < cells.length;i++) {
42549 ret = Math.max(cells[i].rows || 0,ret);
42556 addItem : function(rec)
42558 // look for vertical location slot in
42559 var cells = this.findCells(rec);
42561 rec.row = this.findBestRow(cells);
42563 // work out the location.
42567 for(var i =0; i < cells.length; i++) {
42575 if (crow.start.getY() == cells[i].getY()) {
42577 crow.end = cells[i];
42593 for (var i = 0; i < cells.length;i++) {
42594 cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
42601 clearEvents: function() {
42603 if (!this.eventStore.getCount()) {
42606 // reset number of rows in cells.
42607 Roo.each(this.cells.elements, function(c){
42611 this.eventStore.each(function(e) {
42612 this.clearEvent(e);
42617 clearEvent : function(ev)
42620 Roo.each(ev.els, function(el) {
42621 el.un('mouseenter' ,this.onEventEnter, this);
42622 el.un('mouseleave' ,this.onEventLeave, this);
42630 renderEvent : function(ev,ctr) {
42632 ctr = this.view.el.select('.fc-event-container',true).first();
42636 this.clearEvent(ev);
42642 var cells = ev.cells;
42643 var rows = ev.rows;
42644 this.fireEvent('eventrender', this, ev);
42646 for(var i =0; i < rows.length; i++) {
42650 cls += ' fc-event-start';
42652 if ((i+1) == rows.length) {
42653 cls += ' fc-event-end';
42656 //Roo.log(ev.data);
42657 // how many rows should it span..
42658 var cg = this.eventTmpl.append(ctr,Roo.apply({
42661 }, ev.data) , true);
42664 cg.on('mouseenter' ,this.onEventEnter, this, ev);
42665 cg.on('mouseleave' ,this.onEventLeave, this, ev);
42666 cg.on('click', this.onEventClick, this, ev);
42670 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
42671 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
42674 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
42675 cg.setWidth(ebox.right - sbox.x -2);
42679 renderEvents: function()
42681 // first make sure there is enough space..
42683 if (!this.eventTmpl) {
42684 this.eventTmpl = new Roo.Template(
42685 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}" style="position: absolute" unselectable="on">' +
42686 '<div class="fc-event-inner">' +
42687 '<span class="fc-event-time">{time}</span>' +
42688 '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
42690 '<div class="ui-resizable-heandle ui-resizable-e"> </div>' +
42698 this.cells.each(function(c) {
42699 //Roo.log(c.select('.fc-day-content div',true).first());
42700 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
42703 var ctr = this.view.el.select('.fc-event-container',true).first();
42706 this.eventStore.each(function(ev){
42708 this.renderEvent(ev);
42712 this.view.layout();
42716 onEventEnter: function (e, el,event,d) {
42717 this.fireEvent('evententer', this, el, event);
42720 onEventLeave: function (e, el,event,d) {
42721 this.fireEvent('eventleave', this, el, event);
42724 onEventClick: function (e, el,event,d) {
42725 this.fireEvent('eventclick', this, el, event);
42728 onMonthChange: function () {
42732 onLoad: function () {
42734 //Roo.log('calendar onload');
42736 if(this.eventStore.getCount() > 0){
42740 this.eventStore.each(function(d){
42745 if (typeof(add.end_dt) == 'undefined') {
42746 Roo.log("Missing End time in calendar data: ");
42750 if (typeof(add.start_dt) == 'undefined') {
42751 Roo.log("Missing Start time in calendar data: ");
42755 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
42756 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
42757 add.id = add.id || d.id;
42758 add.title = add.title || '??';
42766 this.renderEvents();
42776 render : function ()
42780 if (!this.view.el.hasClass('course-timesheet')) {
42781 this.view.el.addClass('course-timesheet');
42783 if (this.tsStyle) {
42788 Roo.log(_this.grid.view.el.getWidth());
42791 this.tsStyle = Roo.util.CSS.createStyleSheet({
42792 '.course-timesheet .x-grid-row' : {
42795 '.x-grid-row td' : {
42796 'vertical-align' : 0
42798 '.course-edit-link' : {
42800 'text-overflow' : 'ellipsis',
42801 'overflow' : 'hidden',
42802 'white-space' : 'nowrap',
42803 'cursor' : 'pointer'
42808 '.de-act-sup-link' : {
42809 'color' : 'purple',
42810 'text-decoration' : 'line-through'
42814 'text-decoration' : 'line-through'
42816 '.course-timesheet .course-highlight' : {
42817 'border-top-style': 'dashed !important',
42818 'border-bottom-bottom': 'dashed !important'
42820 '.course-timesheet .course-item' : {
42821 'font-family' : 'tahoma, arial, helvetica',
42822 'font-size' : '11px',
42823 'overflow' : 'hidden',
42824 'padding-left' : '10px',
42825 'padding-right' : '10px',
42826 'padding-top' : '10px'
42834 monitorWindowResize : false,
42835 cellrenderer : function(v,x,r)
42840 xtype: 'CellSelectionModel',
42847 beforeload : function (_self, options)
42849 options.params = options.params || {};
42850 options.params._month = _this.monthField.getValue();
42851 options.params.limit = 9999;
42852 options.params['sort'] = 'when_dt';
42853 options.params['dir'] = 'ASC';
42854 this.proxy.loadResponse = this.loadResponse;
42856 //this.addColumns();
42858 load : function (_self, records, options)
42860 _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
42861 // if you click on the translation.. you can edit it...
42862 var el = Roo.get(this);
42863 var id = el.dom.getAttribute('data-id');
42864 var d = el.dom.getAttribute('data-date');
42865 var t = el.dom.getAttribute('data-time');
42866 //var id = this.child('span').dom.textContent;
42869 Pman.Dialog.CourseCalendar.show({
42873 productitem_active : id ? 1 : 0
42875 _this.grid.ds.load({});
42880 _this.panel.fireEvent('resize', [ '', '' ]);
42883 loadResponse : function(o, success, response){
42884 // this is overridden on before load..
42886 Roo.log("our code?");
42887 //Roo.log(success);
42888 //Roo.log(response)
42889 delete this.activeRequest;
42891 this.fireEvent("loadexception", this, o, response);
42892 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42897 result = o.reader.read(response);
42899 Roo.log("load exception?");
42900 this.fireEvent("loadexception", this, o, response, e);
42901 o.request.callback.call(o.request.scope, null, o.request.arg, false);
42904 Roo.log("ready...");
42905 // loop through result.records;
42906 // and set this.tdate[date] = [] << array of records..
42908 Roo.each(result.records, function(r){
42910 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
42911 _this.tdata[r.data.when_dt.format('j')] = [];
42913 _this.tdata[r.data.when_dt.format('j')].push(r.data);
42916 //Roo.log(_this.tdata);
42918 result.records = [];
42919 result.totalRecords = 6;
42921 // let's generate some duumy records for the rows.
42922 //var st = _this.dateField.getValue();
42924 // work out monday..
42925 //st = st.add(Date.DAY, -1 * st.format('w'));
42927 var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
42929 var firstOfMonth = date.getFirstDayOfMonth();
42930 var days = date.getDaysInMonth();
42932 var firstAdded = false;
42933 for (var i = 0; i < result.totalRecords ; i++) {
42934 //var d= st.add(Date.DAY, i);
42937 for(var w = 0 ; w < 7 ; w++){
42938 if(!firstAdded && firstOfMonth != w){
42945 var dd = (d > 0 && d < 10) ? "0"+d : d;
42946 row['weekday'+w] = String.format(
42947 '<span style="font-size: 16px;"><b>{0}</b></span>'+
42948 '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
42950 date.format('Y-m-')+dd
42953 if(typeof(_this.tdata[d]) != 'undefined'){
42954 Roo.each(_this.tdata[d], function(r){
42958 var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
42959 if(r.parent_id*1>0){
42960 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
42963 if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
42964 deactive = 'de-act-link';
42967 row['weekday'+w] += String.format(
42968 '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
42970 r.product_id_name, //1
42971 r.when_dt.format('h:ia'), //2
42981 // only do this if something added..
42983 result.records.push(_this.grid.dataSource.reader.newRow(row));
42987 // push it twice. (second one with an hour..
42991 this.fireEvent("load", this, o, o.request.arg);
42992 o.request.callback.call(o.request.scope, result, o.request.arg, true);
42994 sortInfo : {field: 'when_dt', direction : 'ASC' },
42996 xtype: 'HttpProxy',
42999 url : baseURL + '/Roo/Shop_course.php'
43002 xtype: 'JsonReader',
43019 'name': 'parent_id',
43023 'name': 'product_id',
43027 'name': 'productitem_id',
43045 click : function (_self, e)
43047 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
43048 sd.setMonth(sd.getMonth()-1);
43049 _this.monthField.setValue(sd.format('Y-m-d'));
43050 _this.grid.ds.load({});
43056 xtype: 'Separator',
43060 xtype: 'MonthField',
43063 render : function (_self)
43065 _this.monthField = _self;
43066 // _this.monthField.set today
43068 select : function (combo, date)
43070 _this.grid.ds.load({});
43073 value : (function() { return new Date(); })()
43076 xtype: 'Separator',
43082 text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
43092 click : function (_self, e)
43094 var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
43095 sd.setMonth(sd.getMonth()+1);
43096 _this.monthField.setValue(sd.format('Y-m-d'));
43097 _this.grid.ds.load({});
43110 * Ext JS Library 1.1.1
43111 * Copyright(c) 2006-2007, Ext JS, LLC.
43113 * Originally Released Under LGPL - original licence link has changed is not relivant.
43116 * <script type="text/javascript">
43120 * @class Roo.LoadMask
43121 * A simple utility class for generically masking elements while loading data. If the element being masked has
43122 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
43123 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
43124 * element's UpdateManager load indicator and will be destroyed after the initial load.
43126 * Create a new LoadMask
43127 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
43128 * @param {Object} config The config object
43130 Roo.LoadMask = function(el, config){
43131 this.el = Roo.get(el);
43132 Roo.apply(this, config);
43134 this.store.on('beforeload', this.onBeforeLoad, this);
43135 this.store.on('load', this.onLoad, this);
43136 this.store.on('loadexception', this.onLoadException, this);
43137 this.removeMask = false;
43139 var um = this.el.getUpdateManager();
43140 um.showLoadIndicator = false; // disable the default indicator
43141 um.on('beforeupdate', this.onBeforeLoad, this);
43142 um.on('update', this.onLoad, this);
43143 um.on('failure', this.onLoad, this);
43144 this.removeMask = true;
43148 Roo.LoadMask.prototype = {
43150 * @cfg {Boolean} removeMask
43151 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
43152 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
43154 removeMask : false,
43156 * @cfg {String} msg
43157 * The text to display in a centered loading message box (defaults to 'Loading...')
43159 msg : 'Loading...',
43161 * @cfg {String} msgCls
43162 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
43164 msgCls : 'x-mask-loading',
43167 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
43173 * Disables the mask to prevent it from being displayed
43175 disable : function(){
43176 this.disabled = true;
43180 * Enables the mask so that it can be displayed
43182 enable : function(){
43183 this.disabled = false;
43186 onLoadException : function()
43188 Roo.log(arguments);
43190 if (typeof(arguments[3]) != 'undefined') {
43191 Roo.MessageBox.alert("Error loading",arguments[3]);
43195 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43196 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43203 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
43206 onLoad : function()
43208 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
43212 onBeforeLoad : function(){
43213 if(!this.disabled){
43214 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
43219 destroy : function(){
43221 this.store.un('beforeload', this.onBeforeLoad, this);
43222 this.store.un('load', this.onLoad, this);
43223 this.store.un('loadexception', this.onLoadException, this);
43225 var um = this.el.getUpdateManager();
43226 um.un('beforeupdate', this.onBeforeLoad, this);
43227 um.un('update', this.onLoad, this);
43228 um.un('failure', this.onLoad, this);
43233 * Ext JS Library 1.1.1
43234 * Copyright(c) 2006-2007, Ext JS, LLC.
43236 * Originally Released Under LGPL - original licence link has changed is not relivant.
43239 * <script type="text/javascript">
43244 * @class Roo.XTemplate
43245 * @extends Roo.Template
43246 * Provides a template that can have nested templates for loops or conditionals. The syntax is:
43248 var t = new Roo.XTemplate(
43249 '<select name="{name}">',
43250 '<tpl for="options"><option value="{value:trim}">{text:ellipsis(10)}</option></tpl>',
43254 // then append, applying the master template values
43257 * Supported features:
43262 {a_variable} - output encoded.
43263 {a_variable.format:("Y-m-d")} - call a method on the variable
43264 {a_variable:raw} - unencoded output
43265 {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
43266 {a_variable:this.method_on_template(...)} - call a method on the template object.
43271 <tpl for="a_variable or condition.."></tpl>
43272 <tpl if="a_variable or condition"></tpl>
43273 <tpl exec="some javascript"></tpl>
43274 <tpl name="named_template"></tpl> (experimental)
43276 <tpl for="."></tpl> - just iterate the property..
43277 <tpl for=".."></tpl> - iterates with the parent (probably the template)
43281 Roo.XTemplate = function()
43283 Roo.XTemplate.superclass.constructor.apply(this, arguments);
43290 Roo.extend(Roo.XTemplate, Roo.Template, {
43293 * The various sub templates
43298 * basic tag replacing syntax
43301 * // you can fake an object call by doing this
43305 re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
43308 * compile the template
43310 * This is not recursive, so I'm not sure how nested templates are really going to be handled..
43313 compile: function()
43317 s = ['<tpl>', s, '</tpl>'].join('');
43319 var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
43320 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
43321 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
43322 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
43323 namedRe = /^<tpl\b[^>]*?name="(\w+)"/, // named templates..
43328 while(true == !!(m = s.match(re))){
43329 var forMatch = m[0].match(nameRe),
43330 ifMatch = m[0].match(ifRe),
43331 execMatch = m[0].match(execRe),
43332 namedMatch = m[0].match(namedRe),
43337 name = forMatch && forMatch[1] ? forMatch[1] : '';
43340 // if - puts fn into test..
43341 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
43343 fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
43348 // exec - calls a function... returns empty if true is returned.
43349 exp = execMatch && execMatch[1] ? execMatch[1] : null;
43351 exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
43359 case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
43360 case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
43361 default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
43364 var uid = namedMatch ? namedMatch[1] : id;
43368 id: namedMatch ? namedMatch[1] : id,
43375 s = s.replace(m[0], '');
43377 s = s.replace(m[0], '{xtpl'+ id + '}');
43382 for(var i = tpls.length-1; i >= 0; --i){
43383 this.compileTpl(tpls[i]);
43384 this.tpls[tpls[i].id] = tpls[i];
43386 this.master = tpls[tpls.length-1];
43390 * same as applyTemplate, except it's done to one of the subTemplates
43391 * when using named templates, you can do:
43393 * var str = pl.applySubTemplate('your-name', values);
43396 * @param {Number} id of the template
43397 * @param {Object} values to apply to template
43398 * @param {Object} parent (normaly the instance of this object)
43400 applySubTemplate : function(id, values, parent)
43404 var t = this.tpls[id];
43408 if(t.test && !t.test.call(this, values, parent)){
43412 Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
43413 Roo.log(e.toString());
43419 if(t.exec && t.exec.call(this, values, parent)){
43423 Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
43424 Roo.log(e.toString());
43429 var vs = t.target ? t.target.call(this, values, parent) : values;
43430 parent = t.target ? values : parent;
43431 if(t.target && vs instanceof Array){
43433 for(var i = 0, len = vs.length; i < len; i++){
43434 buf[buf.length] = t.compiled.call(this, vs[i], parent);
43436 return buf.join('');
43438 return t.compiled.call(this, vs, parent);
43440 Roo.log("Xtemplate.applySubTemplate : Exception thrown");
43441 Roo.log(e.toString());
43442 Roo.log(t.compiled);
43447 compileTpl : function(tpl)
43449 var fm = Roo.util.Format;
43450 var useF = this.disableFormats !== true;
43451 var sep = Roo.isGecko ? "+" : ",";
43452 var undef = function(str) {
43453 Roo.log("Property not found :" + str);
43457 var fn = function(m, name, format, args)
43459 //Roo.log(arguments);
43460 args = args ? args.replace(/\\'/g,"'") : args;
43461 //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
43462 if (typeof(format) == 'undefined') {
43463 format= 'htmlEncode';
43465 if (format == 'raw' ) {
43469 if(name.substr(0, 4) == 'xtpl'){
43470 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
43473 // build an array of options to determine if value is undefined..
43475 // basically get 'xxxx.yyyy' then do
43476 // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
43477 // (function () { Roo.log("Property not found"); return ''; })() :
43482 Roo.each(name.split('.'), function(st) {
43483 lookfor += (lookfor.length ? '.': '') + st;
43484 udef_ar.push( "(typeof(" + lookfor + ") == 'undefined')" );
43487 var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
43490 if(format && useF){
43492 args = args ? ',' + args : "";
43494 if(format.substr(0, 5) != "this."){
43495 format = "fm." + format + '(';
43497 format = 'this.call("'+ format.substr(5) + '", ';
43501 return "'"+ sep + udef_st + format + name + args + "))"+sep+"'";
43505 // called with xxyx.yuu:(test,test)
43507 return "'"+ sep + udef_st + name + '(' + args + "))"+sep+"'";
43509 // raw.. - :raw modifier..
43510 return "'"+ sep + udef_st + name + ")"+sep+"'";
43514 // branched to use + in gecko and [].join() in others
43516 body = "tpl.compiled = function(values, parent){ with(values) { return '" +
43517 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
43520 body = ["tpl.compiled = function(values, parent){ with (values) { return ['"];
43521 body.push(tpl.body.replace(/(\r\n|\n)/g,
43522 '\\n').replace(/'/g, "\\'").replace(this.re, fn));
43523 body.push("'].join('');};};");
43524 body = body.join('');
43527 Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
43529 /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef */
43535 applyTemplate : function(values){
43536 return this.master.compiled.call(this, values, {});
43537 //var s = this.subs;
43540 apply : function(){
43541 return this.applyTemplate.apply(this, arguments);
43546 Roo.XTemplate.from = function(el){
43547 el = Roo.getDom(el);
43548 return new Roo.XTemplate(el.value || el.innerHTML);