Here's a simple example of how the Extends mutator can be used:
var firstClass = new Class({
aMethod: function(){
console.log('firstClass method');
}
});
var secondClass = new Class({
Extends:firstClass,
aMethod: function(){
this.parent();
console.log('secondClass method');
},
initialize: function(){
this.aMethod();
}
});
var instance = new secondClass();
//Output:
// firstClass method
// secondClass method
I would like to achieve the same behavior, but with class constructors like this:
var firstClass = new Class({
methods: {
aMethod: function(){
console.log('firstClass method');
}
}
});
var secondClass = new Class({
Extends:firstClass,
methods: {
aMethod: function(){
this.parent();
console.log('secondClass method');
}
},
initialize: function(){
this.methods.aMethod.call(this);
}
});
var instance = new secondClass();
//Output:
// Error: The method "initialize" has no parent.
instance.methods.aMethod.call(this);
//Output:
// TypeError: this.parent is not a function
My Mootools Fu is unfortunately not very strong, so I'm kind of at a loss. Is this possible with the way Extends currently works? If it's not then I'd like to work on implementing a solution to do this, however I'd need some pointers to get me started in the right direction (make a custom Mutator? modify the Core source? something else?). Any suggestions or ideas on how to proceed would be appreciated.
Update
I came up with a way to do this using pattern based mutators (Github). Mark's code needs to be included for the following to work:
(function(){
/* Utility functions */
Object.extend({
containsMethods:function(obj){
if(typeOf(obj) != 'object') return false;
return Object.getLength(Object.filter(obj, function(item){return typeOf(item) == 'function';})) > 0;
},
containsObjects:function(obj){
if(typeOf(obj) != 'object') return false;
return Object.getLength(Object.filter(obj, function(item){return typeOf(item) == 'object';})) > 0;
}
});
var getInheritance = function(obj, key){
var temp = {};
if(obj){
if(obj.inheritance !== undefined){
temp[key] = obj.inheritance;
}
delete obj.inheritance;
}
return temp;
}
/* Recursive function to turn specially keyed object literals in the constructor into class instances with inheritance */
var classify = function(obj, inherit, key, imap){
if(typeOf(obj) != 'object'){return obj;}
if(key == undefined){
key = '';
}
var inheritance = 'extends';
if(imap != null){
imap = Object.append(imap, getInheritance(obj, key));
imap = Object.append(imap, getInheritance(inherit, key));
if(imap[key] != undefined){
inheritance = imap[key];
}
}
var i = 'extends';
for(var k in inherit){
if(!inherit.hasOwnProperty(k)) {continue;}
i = 'extends';
if(imap != null){
if(typeOf(inherit[k]) == 'object'){
imap = Object.append(imap, getInheritance(inherit[k], k));
}
if(typeOf(obj[k]) == 'object'){
imap = Object.append(imap, getInheritance(obj[k], k));
}
if(imap[k] != undefined){
i = imap[k];
}
}
/* Needed to clean up properties when inheritance == false */
if(inherit[k] === undefined){
delete inherit[k];
}
if(typeOf(inherit[k]) == 'object' && i === false && !Object.keys(obj).contains(k)){
obj[k] = undefined;
continue;
}
if(obj[k] && typeOf(obj[k]) == 'object' && i !== false){
obj[k] = classify(obj[k], inherit[k], k, imap);
}
}
if(Object.containsMethods(obj)){
var constructor = {};
if(inherit != undefined && inheritance !== false){
if(inheritance == 'implements'){
constructor.Implements = (inherit.$constructor ? inherit.$constructor : new Class(inherit));
}else {
constructor.Extends = (inherit.$constructor ? inherit.$constructor : new Class(inherit));
}
}
obj = new (new Class(Object.append(constructor, obj)));
}else {
if(!Object.containsMethods(inherit)){
obj = Object.append({}, inherit, obj);
}
}
return obj;
};
/* Mutator */
Class.defineMutator(/^_(\w+)_/, function(params, key){
var old_key = key;
var key = key.replace(/_/g, "");
var c = null;
var imap = null;
if(this.$constructor){
imap = this.$constructor.imap = (this.$constructor.imap || {});
}
if(this.prototype[key] != undefined){
c = classify.call(this, params, this.prototype[key], '', imap);
}else {
c = classify.call(this, params, undefined, '', imap);
}
this.prototype[key] = c;
delete this[old_key];
});
})();
The mutator pattern is simply a key name surrounded with single underscores (ex: _methods_). The nested objects in the class constructor can contain any combination of properties and methods or other nested objects. Each individual object can have a property with the key 'inheritance' that takes the following values: 'implements', 'extends' or false. Implements and extends correspond to the behavior of the Implements and Extends Mootools class mutators. Extends is the default, and is the method used when nothing is supplied for the inheritance property. A value of false will keep the given object (and any objects nested within it) from being inherited altogether.
The entire code including examples can be found here
Related
My model properties definition is coming from a json file so using reflection to write the classes to be shown under schema on resulting swagger page.
foreach (var model in Models)
{
if (!ModelTypes.ContainsKey(model.Key))
{
anyNonCompiledModel = true;
BuildModelCodeClass(modelComponentBuilder, model.Value);//Build model classes
}
}
BuildModelCodeEnd(modelComponentBuilder);
if (anyNonCompiledModel)
{
CSharpCompiler compiler = new CSharpCompiler();
compiler.AddReference(typeof(object));
compiler.AddReference(typeof(ResourceFactory));
compiler.AddReference(typeof(System.Runtime.Serialization.DataContractResolver));
compiler.AddReference(typeof(System.Runtime.Serialization.DataContractAttribute));
var types = compiler.Compiler(modelComponentBuilder.ToString()); //write model classes
foreach (var type in types)
{
ModelTypes.Add(type.Name, type);
}
}
public void BuildModelCodeClass(StringBuilder modelComponentBuilder, MetadataModelEntity model)
{
modelComponentBuilder.AppendLine($"public class {model.Name} {{");
foreach (var p in model.Data.Properties)
{
if (p.Obsoleted) continue;
if (p.Type.Type == "array")
{
modelComponentBuilder.AppendLine($" public {p.Type.ArrayType.ObjectName}[] {p.Name} {{get;set;}}");
}
else
{
//primitive types
modelComponentBuilder.AppendLine($" public {p.Type.ObjectName} {p.Name} {{get;set;}}");
}
}
modelComponentBuilder.AppendLine(
#"}
");
}
If i provide the description and example like following (in BuildModelCodeClass, inside the loop) then the example and description displays for me.
if (!string.IsNullOrWhiteSpace((string)p.Example))
{
modelComponentBuilder.AppendLine($" ///<example>{p.Example}</example>");
}
if (!string.IsNullOrWhiteSpace((string)p.Description))
{
modelComponentBuilder.AppendLine($" ///<description>{p.Description}</description>");
}
However, i dont want to do above.
I want to write my models via the open api and not via the C# Compiler, is it possible?
I want to show example and description via schema (may be under paths some where). How can i do this? Context has my models info available that i can interact with here.
public class SwaggerDocumentFilter : IDocumentFilter
{
SwaggerDocument _swaggerDocument;
public SwaggerDocumentFilter(object apiConfigure)
{
_swaggerDocument = ((ApiGatewayConfiguration)apiConfigure).SwaggerDocument;
}
public void Apply(OpenApiDocument document, DocumentFilterContext context)
{
if (document.Info.Extensions == null || !document.Info.Extensions.ContainsKey(SwaggerEndpoint.ExtensionDocName)) return;
var openIdString = document.Info.Extensions[SwaggerEndpoint.ExtensionDocName] as OpenApiString;
if (openIdString == null) return;
var docName = openIdString.Value;
SwaggerEndpoint endpoint = _swaggerDocument.SwaggerEndpoints.SingleOrDefault(x => x.Name == docName);
if (endpoint == null) return;
//Add server objects
document.Servers = endpoint.ServerObjects;
//Add Tags objects
document.Tags = endpoint.Tags;
//Set swagger paths objects
var pathsObjects = _swaggerDocument.GetPathsObject(docName, context);
if (pathsObjects.IsValid())
{
pathsObjects.ToList().ForEach(
item => document.Paths.Add(item.Key, item.Value)
);
}
//Add Schema components
//Add Example/Examples
}
}
Following helped
https://github.com/domaindrivendev/Swashbuckle.WebApi/issues/162
AddSchemaExamples.cs
public class AddSchemaExamples : ISchemaFilter
{
public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
{
if (type == typeof(Product))
{
schema.example = new Product
{
Id = 123,
Type = ProductType.Book,
Description = "Treasure Island",
UnitPrice = 10.0M
};
}
}
}
SwaggerConfig.cs
httpConfig
.EnableSwagger(c =>
{
c.SchemaFilter<AddSchemaExamples>()
});
My implementation for the Apply since model is dynamic
if (model != null)
{
schema.Description = model.Description;
foreach (var p in schema.Properties)
{
var mp = model.Data.Properties.SingleOrDefault(x => x.Name == p.Key);
if (mp != null)
{
if (!string.IsNullOrWhiteSpace(mp.Description))
{
p.Value.Description = mp.Description;
}
if(!string.IsNullOrWhiteSpace(mp.Example))
{
p.Value.Example =
new Microsoft.OpenApi.Any.OpenApiString(mp.Example.ToString());
}
}
}
}
I use tinyMCE 5 in my web site to enter data stored in a database. Therefore I need to limit the entered size, including format information, to the size of the data field. How can I prohibit the user to enter more then the allowed number of bytes, say 2000?
Best of all if I could add some information like "42/2000" on the status bar.
We had a similar requirement in our project (difference: the output should be <entered_chars>/<chars_left> instead of <entered_chars>/<max_chars>), and it ended up being a custom plugin, based on the wordcount plugin. There is some hacks in there, which could make it fail whenever tinyMCE changes, since there is no API for the statusbar in version 5 at this point of time.
But maybe you will still find it useful:
(function () {
'use strict';
var global = tinymce.util.Tools.resolve('tinymce.PluginManager');
var maxChars = function (editor) {
return editor.getParam('number_max_chars', 3600);
};
var applyMaxChars = function (editor) {
return editor.getParam('restrict_to_max_chars', true);
};
var Settings = {
maxChars: maxChars,
applyMaxChars: applyMaxChars
};
var global$1 = tinymce.util.Tools.resolve('tinymce.dom.TreeWalker');
var getText = function (node, schema) {
var blockElements = schema.getBlockElements();
var shortEndedElements = schema.getShortEndedElements();
var isNewline = function (node) {
return blockElements[node.nodeName] || shortEndedElements[node.nodeName];
};
var textBlocks = [];
var txt = '';
var treeWalker = new global$1(node, node);
while (node = treeWalker.next()) {
if (node.nodeType === 3) {
txt += node.data;
} else if (isNewline(node) && txt.length) {
textBlocks.push(txt);
txt = '';
}
}
if (txt.length) {
textBlocks.push(txt);
}
return textBlocks;
};
var strLen = function (str) {
return str.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, '_').length;
};
var countCharacters = function (node, schema) {
var text = getText(node, schema).join('');
return strLen(text);
};
var createBodyCounter = function (editor, count) {
return function () {
return count(editor.getBody(), editor.schema);
};
};
var createMaxCount = function (editor) {
return function () {
return Settings.maxChars(editor);
}
}
var createRestrictToMaxCount = function (editor) {
return function () {
return Settings.applyMaxChars(editor);
}
}
var get = function (editor) {
return {
getCount: createBodyCounter(editor, countCharacters),
getMaxCount: createMaxCount(editor),
getRestrictToMaxCount: createRestrictToMaxCount(editor)
};
};
var global$2 = tinymce.util.Tools.resolve('tinymce.util.Delay');
function isAllowedKeycode(event) {
// allow arrow keys, backspace and delete
const key = event.keyCode;
return key === 37 || key === 38 || key === 39 || key === 40 || key === 8
|| key === 46;
}
var updateCount = function (editor, api) {
editor.getContainer().getElementsByClassName(
'tox-statusbar__text-container')[0].textContent = String(
api.getCount()) + " / " + String(
Settings.maxChars(editor) - api.getCount());
};
var setup = function (editor, api, delay) {
var debouncedUpdate = global$2.debounce(function () {
return updateCount(editor, api);
}, delay);
editor.on('init', function () {
updateCount(editor, api);
global$2.setEditorTimeout(editor, function () {
editor.on('SetContent BeforeAddUndo Undo Redo keyup', debouncedUpdate);
editor.on('SetContent BeforeAddUndo Undo Redo keydown', function (e) {
if (!isAllowedKeycode(e) && Settings.applyMaxChars(editor) &&
api.getCount() >= Settings.maxChars(editor)) {
e.preventDefault();
e.stopPropagation();
}
});
}, 0);
});
};
function Plugin(delay) {
if (delay === void 0) {
delay = 300;
}
global.add('charactercount', function (editor) {
var api = get(editor);
setup(editor, api, delay);
return api;
});
}
Plugin();
}());
Currently I'm working on a preprocessor for the paste plugin, so that the max_length effects also pasted text. That's why you see the charactercount API in the code.
I have the following problem.
In a function I have a promise as a return type. This function is in the class Hierarchy.
updateNodeValues(entity: String, data: {}): Promise<any>{
let jsonBody = JSON.stringify(data);
let url = environment.endpointCore + '/api/' + entity + '/' + data['id'];
return this.http.put(url, jsonBody, this.options)
.toPromise()
.then(response => {
return response;
})
.catch(this.handleError);
}
This function is in class node.
onSubmit(): void{
var currentForm = this.form.value;
var entityName = this.inflection.classify(this.node.type).toLowerCase();
var requiredData = {};
for(var i = 0; i < this.formItems.length; i++){
this.formItems[i].value = currentForm[Object.keys(currentForm)[i]];
}
for(var i=0; i<this.formItems.length; i++){
requiredData[this.globalService.camelize(this.formItems[i].label)] = this.formItems[i].value
}
Promise.resolve(this.hierarchyService.updateNodeValues(entityName, requiredData)).then(response => {
alert(response.ok);
if(response.ok){
this.globalService.showSuccessMessage('Values updated');
this.refreshGui(requiredData);
}
});
this.editMode = false;
}
The problem is that when i try to resolve promise and invoke this.refreshGui(requireddata) nothing is happening. I have read about how the fat arrow is preserving the 'context' of this, and I do not understand why invoking this method is not doing anything, while invoking successMessage produces expected outcome.
The method that I am invoking looks like this, and it is also in the class node.
private refreshGui(data: {}){
this._node.data = data;
this.objectProperties = new Array();
this.nodeChildren = new Array();
for (var property in data) {
var propertyValue = data[property];
if (propertyValue instanceof Array) {
this.nodeChildren.push({label: property, value: "total: ".concat(propertyValue.length.toString())});
} else {
this.objectProperties.push({label: property, value: propertyValue});
}
}
}
The solution that I found to be working was to implement custom event. The problem was that within the async callback resolution, the context of what this is would "get lost". The fat arrow enabled me to invoke class method with this, but the properties within the would be "lost". Because of this reason I have took the logic from the method, and put it in the callback part and set expected and needed results in some variable. This variable was passed to my custom event and set to class variable in the custom event handler appropriately.
I want to get the custom attributes, mentioned below, in breeze dataService (client side).
namespace Tam.Framework.Web.Models
{
[ViewAttribute("app/views/Employee.html")]//this custom class attribute
public class Employee : BaseEntity
{
protected override string OnGetDescriptor()
{
return "some description";
}
public string FirstName { get; set; }
[Display(Name = "LAST NAME")]//this custom property attribute
public string LastName { get; set; }
}
}
On the server, add logic to the Metadata controller action to supplement the standard metadata with the display attribute properties:
[HttpGet]
public virtual string Metadata()
{
// Extend metadata with extra attributes
var metadata = JObject.Parse(this.ContextProvider.Metadata());
var ns = metadata["schema"]["namespace"].ToString();
foreach (var breezeEntityType in metadata["schema"]["entityType"])
{
var typeName = ns + "." + breezeEntityType["name"].ToString();
var entityType = BuildManager.GetType(typeName, true);
foreach (var propertyInfo in entityType.GetProperties())
{
var attributes = propertyInfo.GetAllAttributes();
var breezePropertyInfo = breezeEntityType["property"].SingleOrDefault(p => p["name"].ToString() == propertyInfo.Name);
if (breezePropertyInfo == null)
continue;
// handle display attribute...
var displayAttribute = attributes.OfType<DisplayAttribute>().FirstOrDefault();
if (displayAttribute != null)
{
var displayName = displayAttribute.GetName();
if (displayName != null)
breezePropertyInfo["displayName"] = displayName;
var displayOrder = displayAttribute.GetOrder();
if (displayOrder != null)
breezePropertyInfo["displayOrder"] = displayOrder;
var autogenerateField = displayAttribute.GetAutoGenerateField();
if (autogenerateField != null)
breezePropertyInfo["autoGenerateField"] = autogenerateField;
}
// allowEmptyStrings.
if (propertyInfo.PropertyType == typeof(string))
{
breezePropertyInfo["allowEmptyStrings"] = true;
var requiredAttribute = attributes.OfType<RequiredAttribute>().FirstOrDefault();
if (requiredAttribute != null && !requiredAttribute.AllowEmptyStrings)
breezePropertyInfo["allowEmptyStrings"] = false;
}
// todo: handle other types of attributes...
}
}
return metadata.ToString();
}
On the client, fetch the metadata and supplement the breeze entity type with the custom metadata.
function initializeMetadataStore(metadataStore, metadata) {
var metadataType, metadataProperty, entityProperty, i, j;
for (i = 0; i < metadata.schema.entityType.length; i++) {
metadataType = metadata.schema.entityType[i];
var entityType = metadataStore.getEntityType(metadataType.name);
for (j = 0; j < metadataType.property.length; j++) {
metadataProperty = metadataType.property[j];
entityProperty = entityType.getProperty(metadataProperty.name);
if (entityProperty) {
if (typeof metadataProperty.displayName !== 'undefined') {
entityProperty.displayName = metadataProperty.displayName;
}
if (typeof metadataProperty.displayOrder !== 'undefined') {
entityProperty.displayOrder = metadataProperty.displayOrder;
}
if (typeof metadataProperty.autoGenerateField !== 'undefined') {
entityProperty.autoGenerateField = metadataProperty.autoGenerateField;
}
if (typeof metadataProperty.allowEmptyStrings !== 'undefined') {
entityProperty.allowEmptyStrings = metadataProperty.allowEmptyStrings;
}
}
}
}
}
var entityManager = ....something...;
entityManager.fetchMetadata(function (metadata) {
return initializeMetadataStore(entityManager.metadataStore, metadata);
});
now the additional metadata is available in the breeze entity type...
var propertyDisplayName = myEntity.entityType.getProperty('lastName').displayName;
var manager = configureBreezeManager();
function configureBreezeManager() {
breeze.NamingConvention.camelCase.setAsDefault();
var mgr = new breeze.EntityManager('api/breeze');
model.configureMetadataStore(mgr.metadataStore);
mgr.fetchMetadata(function (metadata) {
return initializeMetadataStore(mgr.metadataStore, metadata);
});
return mgr;
};
function initializeMetadataStore(metadataStore, metadata) {
breeze.NamingConvention.defaultInstance = breeze.NamingConvention.none;
var metadataType, metadataProperty, entityProperty, i, j;
for (i = 0; i < metadata.schema.entityType.length; i++) {
metadataType = metadata.schema.entityType[i];
var entityType = metadataStore.getEntityType(metadataType.name);
for (j = 0; j < metadataType.property.length; j++) {
metadataProperty = metadataType.property[j];
entityProperty = entityType.getProperty(metadataProperty.name);
if (entityProperty) {
if (typeof metadataProperty.displayName !== 'undefined') {
entityProperty.displayName = metadataProperty.displayName;
}
if (typeof metadataProperty.displayOrder !== 'undefined') {
entityProperty.displayOrder = metadataProperty.displayOrder;
}
if (typeof metadataProperty.autoGenerateField !== 'undefined') {
entityProperty.autoGenerateField = metadataProperty.autoGenerateField;
}
if (typeof metadataProperty.allowEmptyStrings !== 'undefined') {
entityProperty.allowEmptyStrings = metadataProperty.allowEmptyStrings;
}
}
}
}
}
var readData = function (entityName, observableResults, showLog) {
if (!entityName || !observableResults)
return null;
var query = new breeze.EntityQuery()
.from(entityName);
return manager.executeQuery(query)
.then(querySucceeded)
.fail(queryFailed);
}
function readEmployee() {
return breezeDataService.readData("Employees", employees, true).then(function () {
var propertyDisplayName = employees()[0].entityType.getProperty('lastName').displayName;//error displayName undefined
}
}
when I get list of entity by readData function that list (observableResults[0]) have not any displayName but I add displayName and checked it by initializeMetadataStore function is correct
FINALLY!!!!! I found it it because of breeze.NamingConvention.camelCase.setAsDefault();
I have an extjs form with fields. The user isn't required to enter data into each field so I do not want to submit the fields with no data. I want it to post only fields that actually have data. Is this possible?
I recommend using form's beforeaction event. While handling this event you can check all fields. If all values are empty just return false;. The following example works in ExtJS4 and has to work in ExtJS3:
myform.on('beforeaction', function(form, action) {
if (action.type == 'submit') {
var doSubmit = false, vals = form.getValues();
for (var i in vals)
if (vals[i] !== '') {
doSubmit = true;
break;
}
return doSubmit;
}
});
Actualy, the right way to not submit empty fields is to use action's submitEmptyText config. But it's not working in current version (ExtJS4.0.2a).
Another options is to override component's getSubmitValue() method and return null if this field is empty, this way it won't be included into submit fields.
{
xtype: 'combo',
getSubmitValue: function(){
var value = this.getValue();
if(Ext.isEmpty(value)) {
return null;
}
return value;
}
}
Instead of using form's submit, directly call Ext.Ajax.request(...) with the url, method type (GET/POST) and params (and any other options as explained in the call documentation).
To generate params, iterate over the form fields and check for null value before adding to params.
This bug is present in ExtJS 4.0.7 too.
As Molecule Man pointed:
Actualy, the right way to not submit empty fields is to use action's submitEmptyText config. But it's not working in current version (ExtJS4.0.2a).
A possible solution to fix this bug is by overriding 2 functions, getValues in "Ext.form.Basic" (where the bug is) and createForm (to create our basic form) in "Ext.form.Panel" by extension in the following way:
Ext.define("My.form.Basic", {
alias: "form.mybasic",
extend: "Ext.form.Basic",
getValues: function(asString, dirtyOnly, includeEmptyText, useDataValues) {
var values = {};
this.getFields().each(function(field) {
if (!dirtyOnly || field.isDirty()) {
var data = field[useDataValues ? "getModelData" : "getSubmitData"](includeEmptyText);
if (Ext.isObject(data)) {
var isArray = Ext.isArray;
Ext.iterate(data, function(name, val) {
if (includeEmptyText && val === "") {
val = field.emptyText || "";
}
if (includeEmptyText || ((!isArray(val) && val !== "") || (isArray(val) && val.length !== 0))) {
if (name in values) {
var bucket = values[name];
if (!isArray(bucket)) {
bucket = values[name] = [bucket];
}
if (isArray(val)) {
values[name] = bucket.concat(val);
}
else {
bucket.push(val);
}
}
else {
values[name] = val;
}
}
});
}
}
});
if (asString) {
values = Ext.Object.toQueryString(values);
}
return values;
}
});
Ext.define("My.form.Panel", {
alias: "form.mypanel",
extend: "Ext.form.Panel",
createForm: function() {
return Ext.create("My.form.Basic", this, Ext.applyIf({listeners: {}}, this.initialConfig));
}
});
The code is copied from the ext source code. The only change is inside the iteration of each field: introduced the following wrapping "if":
if (includeEmptyText || ((!isArray(val) && val !== "") || (isArray(val) && val.length !== 0)))
I am a bit late but, better later than never...