1 Ext.namespace('Ext.ux.grid');
  2 Ext.namespace('Ext.ux.form');
  3 Ext.namespace('Ext.ux.tree');
  4 
  5 Ext.ux.tree.CodeLoader = function(designer,config) {
  6    Ext.apply(this, config);
  7    this.designer = designer;
  8    Ext.tree.TreeLoader.superclass.constructor.call(this);
  9 };
 10 
 11 Ext.extend(Ext.ux.tree.CodeLoader, Ext.util.Observable, {
 12  jsonId : '__JSON__',
 13  
 14  load : function(node, callback){ 
 15      while(node.firstChild) node.removeChild(node.firstChild);
 16      if(this.doLoad(node,this.designer.getConfig())){
 17        if(typeof callback == "function") callback();
 18      }
 19   },
 20   
 21   elementToText : function(c) {
 22       var txt = [];
 23       c = c || {};
 24       if (c.xtype)      { txt.push(c.xtype); }
 25       if (c.fieldLabel) { txt.push('[' + c.fieldLabel + ']'); }
 26       if (c.boxLabel)   { txt.push('[' + c.boxLabel + ']'); }
 27       if (c.layout)     { txt.push('<i>' + c.layout + '</i>'); }
 28       if (c.title)      { txt.push('<b>' + c.title + '</b>'); }
 29       if (c.text)       { txt.push('<b>' + c.text + '</b>'); }
 30       if (c.region)     { txt.push('<i>(' + c.region + ')</i>'); }
 31       return (txt.length == 0 ? "Element" : txt.join(" "));
 32   },
 33   
 34   doLoad : function(node,data){
 35     if(data){
 36       node.beginUpdate();
 37       if (!this.designer.isEmptyObject(data)) {
 38         var cs = {
 39              text: this.elementToText(data),
 40              cls: data.items ? 'folder' : 'file' , 
 41              leaf : data.items ? false : true,
 42              jsonId : data[this.jsonId]
 43         };
 44         var cn = node.appendChild(new Ext.tree.TreeNode(cs));
 45         if (data.items) {
 46           for(var i = 0, len = data.items.length; i < len; i++){
 47             this.doLoad(cn,data.items[i]);
 48           }
 49         }
 50       }
 51       node.endUpdate();
 52       return true;
 53     }
 54     return false;
 55   }
 56 });
 57 
 58 /**
 59  * Used by designer when selecting a value from a ComponentDoc defined value property in the grid
 60  * @type component
 61  */  
 62 Ext.ux.form.SimpleCombo = Ext.extend(Ext.form.ComboBox, {
 63     // @private Data is loaded localy
 64     mode           : 'local',
 65     // @private We trigger on all
 66     triggerAction  : 'all',
 67     // @private We allow type ahead
 68     typeAhead      : true,
 69     // @private The value field bound to field called value    
 70     valueField     : 'value',
 71     // @private The display name is called name
 72     displayField   : 'name',
 73     // @private Forceselection is by default enabled
 74     forceSelection : true,
 75     // @private The Combobox is by default editable
 76     editable       : true,
 77     // @private No charachters are required
 78     minChars       : 0,
 79     /**
 80      * Are customProperties (values) allowed to be entered (defaults false)
 81      * @type {Boolean}
 82      @cfg */
 83     customProperties : false,
 84     /**
 85      * @private Override the init of ComboBox so that local data store is used
 86      */
 87     initComponent  : function(){
 88         Ext.ux.form.SimpleCombo.superclass.initComponent.call(this);
 89         if(!this.store && this.data){
 90             this.store = new Ext.data.SimpleStore({
 91                 fields: ['value','name','cls'],
 92                 data : this.data
 93             });
 94         }
 95         this.tpl = '<tpl for="."><div class="x-combo-list-item {cls}">{' + this.displayField + '}</div></tpl>';
 96     },
 97     
 98     setList : function(list){
 99       data = [];
100       if (list) {
101         for (var i=0;i<list.length;i++) {data.push([list[i],list[i],null])};
102       }
103       this.store.loadData(data,false);
104     },
105     
106     /**
107      * @private Override the getValue so that when customProperties is set
108      * the rawValues is returned
109      */
110     getValue : function (){
111       return Ext.ux.form.SimpleCombo.superclass.getValue.call(this) || 
112         (this.customProperties ? this.getRawValue() : '');
113     }
114 
115 });
116 Ext.reg('simplecombo', Ext.ux.form.SimpleCombo);
117 
118 /**
119  * Used by designer to edit javascript code. 
120  * When codepress is installed it will used as the editor otherwise textarea
121  * @type component
122  */  
123 Ext.ux.form.ScriptEditor = Ext.extend(Ext.BoxComponent, {
124   
125   /**
126    * The value used by the scripteditor (defaults null)
127    * @type {String}
128    */
129   value : undefined,
130   
131   /**
132    * Default language of scripteditor (defaults javascript)
133    * @type {String}
134    @cfg */
135   language : 'javascript',
136   
137   /**
138    * Should it use codePress as code editor (defaults true)
139    * @type {Boolean}
140    @cfg */
141   codePress : true, //codePress enabled
142   
143   /**
144    * @private overridde setValue so value property value is set
145    */
146   setValue : function(text){
147     this.value =  text;
148   },
149 
150   /**
151    * @private overridde getValue so value property value is read
152    */  
153   getValue : function(){
154     return this.value || "";
155   },
156   
157   /**
158    * @private The data is always valid
159    */
160   isValid : function(preventMark){
161      return true;
162   },
163    
164   /**
165    * We open the scripteditor window on this event
166    */
167   onTriggerClick : function() {
168     if(this.disabled){return;}
169     if (!this.editorWin) {
170       var tf = (this.codePress && Ext.ux.CodePress) 
171              ?  new Ext.ux.CodePress({language: this.language ,autoResize:true,trim : true})
172              :  new Ext.form.TextArea({resize:Ext.emptyFn});
173       this.editorWin = new Ext.Window({
174           title  : "ScriptEditor",
175           iconCls: 'icon-editEl',
176           closable:true,
177           width:600,
178           height:450,
179           plain:true,
180           modal: true,
181           maximizable : true,          
182           layout      : 'fit',
183           items       : tf,
184           closeAction : 'hide',
185           keys: [{
186               key: 27,
187               scope: this,
188               fn: function() {
189                 this.editorWin.hide();
190                 Ext.getCmp(this.editor).cancelEdit();
191               }}],
192           buttons: [{
193              text    : "Close",
194              scope   : this,
195              handler : function() {             
196                this.editorWin.hide(); 
197                Ext.getCmp(this.editor).cancelEdit();  
198              }
199             },{
200              text    : "Apply",
201              scope   : this,
202              handler : function() {       
203                this.setValue(tf.getValue());
204                this.editorWin.hide();
205                this.editorWin.el.unmask(); 
206                Ext.getCmp(this.editor).completeEdit();
207              }
208            }] 
209         });
210       this.editorWin.tf = tf;
211       this.editorWin.doLayout();
212       this.editorWin.on('resize',function () {tf.resize()});
213       }
214     this.editorWin.show();
215     this.editorWin.tf.setValue(this.value || this.defaultValue);
216   },
217   
218   /**
219    * @private During render we create the the 'Click to edit' box
220    * @param {Component} ct The component to render
221    * @param {Object} position A object containing the position of the component
222    */
223   onRender : function(ct, position){
224    this.editor = ct.id;
225    Ext.ux.form.ScriptEditor.superclass.onRender.call(this, ct, position);
226    this.el = ct.createChild({tag: "div",cls:'x-form-text'},position);
227    this.trigger = this.el.createChild({tag: "div"});
228    this.trigger.createChild({tag:"div", cls:"icon-scripteditor",html:" "});
229    this.trigger.createChild({tag:"div",cls:"text-scripteditor",html:"Click to edit"});
230    this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
231   }
232 
233 });
234 Ext.reg('scripteditor', Ext.ux.form.ScriptEditor);
235 
236 Ext.ux.grid.PropertyRecord = Ext.data.Record.create([
237     {name:'name',type:'string'}, 'value' , 'type'
238 ]);
239 
240 Ext.ux.grid.PropertyStore = function(grid, source){
241     Ext.ux.grid.PropertyStore.superclass.constructor.call(this,grid,source);
242     this.store = new Ext.data.Store({
243         recordType : Ext.ux.grid.PropertyRecord
244     });
245     this.store.on('update', this.onUpdate,  this);    
246 };
247 
248 Ext.ux.grid.PropertyStore = Ext.extend(Ext.grid.PropertyStore, {
249     jsonId : "__JSON__",
250 
251     getPropertyType : function (name) {
252       if (this.grid && this.grid.getPropertyType) return this.grid.getPropertyType(name);
253       return null;
254     },
255     
256     // protected - should only be called by the grid.  Use grid.setSource instead.
257     setSource : function(o){
258         this.source = o;
259         this.store.removeAll();
260         var data = [];
261         for(var k in o){
262             if(k.indexOf(this.jsonId)!=0 && ['items'].indexOf(k)==-1){
263                 if (typeof(o[k]) == 'function') {
264                   data.push(new Ext.grid.PropertyRecord({name: k, value: o[this.jsonId + k] || String(o[k]) , type : 'function'}, k));
265                 } else if (typeof(o[k]) == 'object') {
266                   data.push(new Ext.grid.PropertyRecord({name: k, value: o[this.jsonId + k] || String(Ext.ux.JSON.encode(o[k])), type : 'object'}, k));
267                 } 
268                   data.push(new Ext.grid.PropertyRecord({name: k, value: o[this.jsonId + k] || o[k], type : o[this.jsonId + k] ? 'function' : '' }, k));
269             }
270         }
271         this.store.loadRecords({records: data}, {}, true);
272     },
273     
274     // private
275     onUpdate : function(ds, record, type){
276         if(type == Ext.data.Record.EDIT){
277             var v = record.data['value'];
278             var oldValue = record.modified['value'];
279             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
280                 record.data.changeValue = this.updateSource(record.id,v,record.data.type);
281                 record.commit();
282                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
283             }else{
284                 record.reject();
285             }
286         }
287     },
288     
289     updateSource : function (prop,value,type) {
290       var propType = this.getPropertyType(prop);
291       if (!type && propType) type=propType.type;
292       if (typeof(this.source[this.jsonId + prop])!='undefined' ||
293        ['object','function','mixed'].indexOf(type)!=-1 || !propType) {
294          this.source[this.jsonId + prop] = value;
295          try {
296           //Set the jsonScope to be used during eval
297            var scope = (this.grid) ? this.grid.jsonScope : this.scope;
298            var o = eval("( { data :" + value + "})");
299            this.source[prop] = o.data;
300          } catch (e) {Ext.Msg.alert('Exception','Could not set ' + prop + ' to ' + value + '/n' + e);}
301       } else {
302         this.source[prop] = value;
303       }
304       return this.source[prop];
305     },
306 
307     setValue : function(prop, value){
308         this.store.getById(prop).set('value', value);
309         this.updateSource(prop,value);
310     }
311     
312 });
313 
314 
315 Ext.ux.grid.PropertyColumnModel = function(grid, store){
316     Ext.ux.grid.PropertyColumnModel.superclass.constructor.call(this,grid,store);
317     this.jsonId=grid.jsonId;
318     Ext.apply(this.editors,{
319         'regexp' : new Ext.grid.GridEditor(new Ext.ux.form.ScriptEditor({defaultValue:'new RegExp()'})),
320         'function':new Ext.grid.GridEditor(new Ext.ux.form.ScriptEditor({defaultValue:'function(){}'})),
321         'object':new Ext.grid.GridEditor(new Ext.ux.form.ScriptEditor({defaultValue:'{}'})),
322         'object/array':new Ext.grid.GridEditor(new Ext.ux.form.ScriptEditor({defaultValue:'[{}]'})),
323         'array': new Ext.grid.GridEditor(new Ext.ux.form.ScriptEditor({defaultValue:'[]'})),
324         'template': new Ext.grid.GridEditor(new Ext.ux.form.ScriptEditor({defaultValue:''})),
325         'mixed': new Ext.grid.GridEditor(new Ext.ux.form.ScriptEditor({defaultValue:''})),
326         'html' : new Ext.grid.GridEditor(new Ext.ux.form.ScriptEditor({defaultValue:'',language:'html'})),
327         'css' : new Ext.grid.GridEditor(new Ext.ux.form.ScriptEditor({defaultValue:'',language:'css'})),
328         'editlist' :new Ext.grid.GridEditor(new Ext.ux.form.SimpleCombo({forceSelection:false,data:[],editable:true,customProperties:true})),
329         'list':new Ext.grid.GridEditor(new Ext.ux.form.SimpleCombo({forceSelection:false,data:[],editable:true,customProperties:true}))      
330     });
331     this.valueRendererDelegate = this.valueRenderer.createDelegate(this);
332     this.propertyRendererDelegate = this.propertyRenderer.createDelegate(this);
333 };
334 
335 Ext.extend(Ext.ux.grid.PropertyColumnModel,Ext.grid.PropertyColumnModel, {
336     // private
337     
338     getPropertyType : function (name) {
339       if (this.grid && this.grid.getPropertyType) return this.grid.getPropertyType(name);
340       return null;
341     },
342     
343     getCellEditor : function(colIndex, rowIndex){
344         var p = this.store.getProperty(rowIndex);
345         var n = p.data['name'], val = p.data['value'], t = p.data['type'];
346         if(this.grid.customEditors[n]){
347             return this.grid.customEditors[n];
348         }
349         var prop = this.getPropertyType(n);
350         if (!t && prop) {
351           t=prop.type;
352           if (!t && prop.values) {
353            var editor =  prop.editable ? this.editors['editlist'] : this.editors['list'];
354            editor.field.setList(prop.values);
355            return editor;
356           }
357         }
358         if (t && this.editors[t]) {
359           return this.editors[t];
360         } else if(Ext.isDate(val)){
361             return this.editors['date'];
362         }else if(typeof val == 'number'){
363             return this.editors['number'];
364         }else if(typeof val == 'boolean'){
365             return this.editors['boolean'];
366         }
367         return this.defaultEditor || this.editors[prop ? 'string' : 'mixed'];
368     },
369     
370     valueRenderer  : function(value, p, r) {
371       if (typeof value == 'boolean') {
372         p.css = (value ? "typeBoolTrue" : "typeBoolFalse");
373         return (value ? "True" : "False");
374       } 
375       var propType = this.getPropertyType(r.id);
376       if (propType && ['object','array','object/array'].indexOf(propType.type)!=-1) {
377         p.css = "typeObject";
378       }
379       return value;
380     },
381     
382     propertyRenderer :  function(value, p) {
383       var propType = this.getPropertyType(value);
384       if (propType) {
385         qtip = propType.desc || '';
386         p.attr = 'qtip="' + qtip.replace(/"/g,'"') + '"'; //'
387       }
388       return value;
389     },
390     
391     getRenderer  : function(col){
392        return col == 0 ? this.propertyRendererDelegate : this.valueRendererDelegate;
393     }
394 });
395 
396     
397 Ext.ux.grid.PropertyGrid = Ext.extend(Ext.grid.EditorGridPanel, {
398     // private config overrides
399     enableColumnMove:false,
400     stripeRows:false,
401     trackMouseOver: false,
402     clicksToEdit:1,
403     enableHdMenu : false,
404     viewConfig : {
405         forceFit:true
406     },
407     jsonId : '__JSON__',
408 
409     getPropertyType : function (name) {
410       if (this.propertyTypes) {
411         var i = this.propertyTypes.find('name',name);
412         if (i!=-1) return this.propertyTypes.getAt(i).data;
413       }
414       return null;
415     },
416 
417     // private
418     initComponent : function(){
419         this.customEditors = this.customEditors || {};
420         this.lastEditRow = null;
421         var store = new Ext.ux.grid.PropertyStore(this);
422         store.jsonId=this.jsonId,
423         this.propStore = store;
424         var cm = new Ext.ux.grid.PropertyColumnModel(this, store);
425         store.store.sort('name', 'ASC');
426         this.addEvents(
427             'beforepropertychange',
428             'propertychange'
429         );
430         this.cm = cm;
431         this.ds = store.store;
432         Ext.ux.grid.PropertyGrid.superclass.initComponent.call(this);
433 
434         this.selModel.on('beforecellselect', function(sm, rowIndex, colIndex){
435             if(colIndex === 0){
436                 this.startEditing.defer(200, this, [rowIndex, 1]);
437                 return false;
438             }
439         }, this);
440     },
441 
442     onRender : function(){
443         Ext.ux.grid.PropertyGrid.superclass.onRender.apply(this, arguments);
444         this.getGridEl().addClass('x-props-grid');
445     },
446 
447     afterRender: function(){
448         Ext.ux.grid.PropertyGrid.superclass.afterRender.apply(this, arguments);
449         if(this.source){
450             this.setSource(this.source);
451         }
452     },
453 
454     setSource : function(source){
455         this.propStore.setSource(source);
456     },
457 
458     getSource : function(){
459         return this.propStore.getSource();
460     },
461     
462 // Fix problem for now that ds.indexOf can return -1, throwning exception
463     getView : function(){
464             if(!this.view){
465                 this.view = Ext.apply(new Ext.grid.GridView(this.viewConfig),{ 
466                    refreshRow : function(record){
467                        var ds = this.ds, index;
468                        if(typeof record == 'number'){
469                            index = record;
470                            record = ds.getAt(index);
471                        }else{
472                            index = ds.indexOf(record);
473                        }
474                        if (index!=-1) {
475                          var cls = [];
476                          this.insertRows(ds, index, index, true);
477                          this.getRow(index).rowIndex = index;
478                          this.onRemove(ds, record, index+1, true);
479                          this.fireEvent("rowupdated", this, index, record);
480                        }
481                    }
482                 }); 
483             }
484             return this.view;
485     },
486     
487         
488 });    
489 
490 //Is not registered but required by designer
491 Ext.reg('uxpropertygrid', Ext.ux.grid.PropertyGrid);
492