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