alunharford
21 Jan 2008, 2:45 AM
I got annoyed with a few aspects of how classes are generally written in Ext, so I wrote my own mechanism. I had to write a lot of code to check that the config provided when instantiating a class is valid, and I wanted a way to turn that off in release mode (for performance).
I wrote some code to do that. It can be used like this to create a simple class:
//Creates a class called Ext.ux.MyPanel
Ext.superExtend("Ext.ux.MyPanel", {
superclass: Ext.Panel, //superclass - if none is specified, Ext.superExtend automatically uses Ext.override instead of Ext.extend
checkParams:
{
items: true, //Adds a check: config MUST contain 'items'
}
};
Or like this to create something more complicated:
//Creates a class called Ext.ux.MyPanel
Ext.superExtend("Ext.ux.MyPanel", {
superclass: Ext.Panel, //superclass - if none is specified, Ext.superExtend automatically uses Ext.override instead of Ext.extend
checkParams:
{
items: true, //Adds a check: config MUST contain 'items'
layout: false //Adds a check: config MUST NOT contain 'layout'
},
defaults: function(config)
{
return {
layout: 'border' //does Ext.applyIf(config, {layout: 'border'});
};
},
type: function(config)
{
//some code that runs before the superclass' constructor is called
},
postConstructor: function(config)
{
//some code that runs after the superclass' constructor is called
},
overrides:
{
//public function (would 'normally' be specified with Ext.extend)
foo: function()
{
Ext.Msg.alert('foo!');
}
}
});
And here is the code for Ext.superExtend. If you find bugs or have comments, let me know. I expect I'll refactor this code soon (it really needs it!), so doing fixes shouldn't be too hard :-)
Ext.superExtend = function(name, config)
{
if(typeof(name) === 'object' && typeof(config) === 'undefined')
{
config = name;
name = null;
}
var allowedParams = [];
var constructParam = function(configParams, defaultValue)
{
var result = null;
for(var i = 0, l = configParams.length; !result && i < l; i++)
{
result = config[configParams[i]];
}
allowedParams = allowedParams.concat(configParams);
return result || defaultValue;
};
config = config || {};
var supertype = constructParam(["supertype", "st", "superclass", "sc"], null);
var type = constructParam(["type"], Ext.emptyFn);
var postConstructor = constructParam(["postConstructor", "pc"], Ext.emptyFn);
var hash = constructParam(["checkParams", "cp"], {});
var overrides = constructParam(["overrides", "ov"], {});
var defaults = constructParam(["defaults"], function(){return {};});
if(typeof(defaults) !== 'function')
{
throw new Error("defaults passed into Ext.superExtend must be specified with a function. Failed in " + name);
}
var checkParamSpelling = function()
{
var isConfigItemAllowed = function(item)
{
return (allowedParams.findAll(function(param){ return param === item; }).length !== 0);
};
for(configItem in config)
{
if(!isConfigItemAllowed(configItem))
{
throw new Error("Input to Ext.superExtend contained an unexpected parameter");
}
}
};
checkParamSpelling();
var buildChecks = function()
{
var addCheck = function(variable, checks)
{
//Variable capture
var _variable = variable;
var _checks = checks;
var _hash = hash;
if(_hash[_variable])
{
return function(config)
{
if(typeof(config[_variable]) === 'undefined')
{
throw new Error(_variable + " cannot be null");
}
_checks(config);
};
} else {
return function(config)
{
if(typeof(config) !== 'undefined' && typeof(config[_variable]) !== 'undefined')
{
throw new Error(_variable + " cannot be set in the config");
}
_checks(config);
};
}
};
var configCheck = function(config)
{
if(!config)
{
throw new Error("config cannot be null");
}
};
var configCheckFn;
var checks = function(config){};
for(varname in hash)
{
if(hash[varname])
{
configCheckFn = configCheckFn || configCheck;
}
checks = addCheck(varname, checks);
}
return configCheckFn ? function(config)
{
configCheckFn(config);
checks(config);
} : checks;
};
var assignToVariableName = function(name, variable)
{
if(name)
{
Ext.namespace(name);
var parts = name.split(".");
var v = self;
for(var i = 0, l = parts.length - 1; i < l; i++)
{
v = v[parts[i]];
}
v[parts[parts.length - 1]] = variable;
}
};
var res;
var checks = buildChecks();
if(supertype)
{
res = function(config)
{
config = config || {};
checks.call(this, config);
Ext.applyIf(config, defaults(config));
type.call(this, config);
res.superclass.constructor.call(this, config);
postConstructor.call(this, config);
};
Ext.extend(res, supertype, overrides);
}
else
{
res = function(config)
{
config = config || {};
checks.call(this, config);
Ext.applyIf(config, defaults(config));
type.call(this, config);
postConstructor.call(this, config);
};
Ext.override(res, overrides);
}
assignToVariableName(name, res);
return res;
};
I wrote some code to do that. It can be used like this to create a simple class:
//Creates a class called Ext.ux.MyPanel
Ext.superExtend("Ext.ux.MyPanel", {
superclass: Ext.Panel, //superclass - if none is specified, Ext.superExtend automatically uses Ext.override instead of Ext.extend
checkParams:
{
items: true, //Adds a check: config MUST contain 'items'
}
};
Or like this to create something more complicated:
//Creates a class called Ext.ux.MyPanel
Ext.superExtend("Ext.ux.MyPanel", {
superclass: Ext.Panel, //superclass - if none is specified, Ext.superExtend automatically uses Ext.override instead of Ext.extend
checkParams:
{
items: true, //Adds a check: config MUST contain 'items'
layout: false //Adds a check: config MUST NOT contain 'layout'
},
defaults: function(config)
{
return {
layout: 'border' //does Ext.applyIf(config, {layout: 'border'});
};
},
type: function(config)
{
//some code that runs before the superclass' constructor is called
},
postConstructor: function(config)
{
//some code that runs after the superclass' constructor is called
},
overrides:
{
//public function (would 'normally' be specified with Ext.extend)
foo: function()
{
Ext.Msg.alert('foo!');
}
}
});
And here is the code for Ext.superExtend. If you find bugs or have comments, let me know. I expect I'll refactor this code soon (it really needs it!), so doing fixes shouldn't be too hard :-)
Ext.superExtend = function(name, config)
{
if(typeof(name) === 'object' && typeof(config) === 'undefined')
{
config = name;
name = null;
}
var allowedParams = [];
var constructParam = function(configParams, defaultValue)
{
var result = null;
for(var i = 0, l = configParams.length; !result && i < l; i++)
{
result = config[configParams[i]];
}
allowedParams = allowedParams.concat(configParams);
return result || defaultValue;
};
config = config || {};
var supertype = constructParam(["supertype", "st", "superclass", "sc"], null);
var type = constructParam(["type"], Ext.emptyFn);
var postConstructor = constructParam(["postConstructor", "pc"], Ext.emptyFn);
var hash = constructParam(["checkParams", "cp"], {});
var overrides = constructParam(["overrides", "ov"], {});
var defaults = constructParam(["defaults"], function(){return {};});
if(typeof(defaults) !== 'function')
{
throw new Error("defaults passed into Ext.superExtend must be specified with a function. Failed in " + name);
}
var checkParamSpelling = function()
{
var isConfigItemAllowed = function(item)
{
return (allowedParams.findAll(function(param){ return param === item; }).length !== 0);
};
for(configItem in config)
{
if(!isConfigItemAllowed(configItem))
{
throw new Error("Input to Ext.superExtend contained an unexpected parameter");
}
}
};
checkParamSpelling();
var buildChecks = function()
{
var addCheck = function(variable, checks)
{
//Variable capture
var _variable = variable;
var _checks = checks;
var _hash = hash;
if(_hash[_variable])
{
return function(config)
{
if(typeof(config[_variable]) === 'undefined')
{
throw new Error(_variable + " cannot be null");
}
_checks(config);
};
} else {
return function(config)
{
if(typeof(config) !== 'undefined' && typeof(config[_variable]) !== 'undefined')
{
throw new Error(_variable + " cannot be set in the config");
}
_checks(config);
};
}
};
var configCheck = function(config)
{
if(!config)
{
throw new Error("config cannot be null");
}
};
var configCheckFn;
var checks = function(config){};
for(varname in hash)
{
if(hash[varname])
{
configCheckFn = configCheckFn || configCheck;
}
checks = addCheck(varname, checks);
}
return configCheckFn ? function(config)
{
configCheckFn(config);
checks(config);
} : checks;
};
var assignToVariableName = function(name, variable)
{
if(name)
{
Ext.namespace(name);
var parts = name.split(".");
var v = self;
for(var i = 0, l = parts.length - 1; i < l; i++)
{
v = v[parts[i]];
}
v[parts[parts.length - 1]] = variable;
}
};
var res;
var checks = buildChecks();
if(supertype)
{
res = function(config)
{
config = config || {};
checks.call(this, config);
Ext.applyIf(config, defaults(config));
type.call(this, config);
res.superclass.constructor.call(this, config);
postConstructor.call(this, config);
};
Ext.extend(res, supertype, overrides);
}
else
{
res = function(config)
{
config = config || {};
checks.call(this, config);
Ext.applyIf(config, defaults(config));
type.call(this, config);
postConstructor.call(this, config);
};
Ext.override(res, overrides);
}
assignToVariableName(name, res);
return res;
};