diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js index 8fccf247..78289fa3 100644 --- a/kfet/static/kfet/js/kfet.api.js +++ b/kfet/static/kfet/js/kfet.api.js @@ -450,22 +450,28 @@ class ArticleCategory extends ModelObject { /** * Properties associated to a category - * @default ['id', 'name'] + * @default ['id', 'name', 'has_addcost', 'article'] * @see {@link Models.ModelObject.props|ModelObject.props} */ static get props() { - return ['id', 'name', 'has_addcost']; + return ['id', 'name', 'has_addcost', 'articles']; } /** * Default values for ArticleCategory model instances. - * @default { 'id': 0, 'name': '' } + * @default { 'id': 0, 'name': '', 'has_addcost': true, 'articles': [] } * @see {@link Models.ModelObject.default_data|ModelObject.default_data} */ static get default_data() { - return {'id': 0, 'name': '', 'has_addcost': true}; + return {'id': 0, 'name': '', 'has_addcost': true, 'articles': []}; } + /** + * Verbose name for ArticleCategory model + * @default 'category' + */ + static get verbose_name() { return 'category'; } + /** * @default {@link Formatters.ArticleCategoryFormatter} */ @@ -490,7 +496,7 @@ class ArticleCategory extends ModelObject { class Article extends ModelObject { /** * Properties associated to an article - * @default ['id', 'name'] + * @default ['id', 'name', 'price', 'stock', 'category'] * @see {@link Models.ModelObject.props|ModelObject.props} */ static get props() { @@ -510,6 +516,12 @@ class Article extends ModelObject { }; } + /** + * Verbose name for Article model + * @default 'article' + */ + static get verbose_name() { return 'article'; } + /** * @default {@link Formatters.ArticleFormatter} */ @@ -535,22 +547,6 @@ class Article extends ModelObject { } } - -/** - * Node for ModelForest object - * @memberof Models - */ -class TreeNode { - - constructor(type, content) { - this.modelname = type; - this.content = content; - this.parent = null; - this.children = []; - } -} - - /** * Simple {@link Models.ModelObject} forest. * @memberof Models @@ -558,24 +554,11 @@ class TreeNode { class ModelForest { /** - * Dictionary associating types to classes + * Abstract structure of the forest * @abstract * @type {Object} */ - static get models() { return {}; } - - /** - * Comparison function for nodes - * @abstract - * @param {class} model Model to use for comparison - * @param {Models.TreeNode} a - * @param {Models.TreeNode} b - * @see {@link Models.ModelObject.compare|ModelObject.compare} - */ - static compare(model, a, b) { - return model.compare(a.content, b.content); - } - + static get structure() { return {}; } /** * Creates empty instance and populates it with data if given @@ -585,6 +568,19 @@ class ModelForest { this.from(datalist || []); } + /** + * Shortcut functions to get parent and children of a given node + */ + get_parent(node) { + var parent_name = this.constructor.structure[node.constructor.verbose_name].parent; + return node[parent_name]; + } + + get_children(node) { + var child_name = this.constructor.structure[node.constructor.verbose_name].children; + return node[child_name]; + } + /** * Fetches an object from the instance data, or creates it if * it does not exist yet.
@@ -594,34 +590,38 @@ class ModelForest { * @param {number} direction */ get_or_create(data, direction) { - var model = this.constructor.models[data.modelname]; + var struct_data = this.constructor.structure[data.modelname]; + var model = struct_data.model; - var existing = this.find_node(data.modelname, data.content.id); + var existing = this.find(data.modelname, data.content.id); if (existing) { return existing; } - var content = new this.constructor.models[data.modelname](data.content); - var node = new TreeNode(data.modelname, content); - - if (data.child_sort) - node.child_sort = data.child_sort + var node = new model(data.content); if (direction <= 0) { - if (data.parent) { - var parent = this.get_or_create(data.parent, -1); - node.parent = parent; - parent.children.push(node); + var parent_name = struct_data.parent; + var parent_data = data.parent; + var parent_struct = this.constructor.structure[parent_name]; + if (parent_data) { + var parent_node = this.get_or_create(parent_data, -1); + node[parent_name] = parent_node; + parent_node[parent_struct.children].push(node); } else { this.roots.push(node); } } - if (direction >= 0 && data.children) { - for (let child_data of data.children) { - var child = this.get_or_create(child_data, 1); - child.parent = node; - node.children.push(child); + if (direction >= 0) { + var child_name = struct_data.children; + var child_struct = this.constructor.structure[child_name]; + if (data.children && data.children.length) { + for (let child_data of data.children) { + var child = this.get_or_create(child_data, 1); + child[child_struct.parent] = node; + node[child_name].push(child); + } } } @@ -654,33 +654,43 @@ class ModelForest { * @param {Object} [options] Options for element render method */ render_element(node, templates, options) { - var template = templates[node.modelname]; + var modelname = node.constructor.verbose_name; + var struct_data = this.constructor.structure[modelname]; + + var template = templates[modelname]; var options = options || {} ; var $container = $('
'); - $container.attr('id', node.modelname+'-'+node.content.id); + $container.attr('id', modelname+'-'+node.id); - var $rendered = node.content.display($(template), options); + var $rendered = node.display($(template), options); $container.append($rendered); - //dirty - node.children.sort(this.constructor.compare.bind(null, this.constructor.models[node.child_sort])); + var children = this.get_children(node); - for (let child of node.children) { - var $child = this.render_element(child, templates, options); - $container.append($child); + if (children && children.length) { + if (struct_data.child_sort) + children.sort(struct_data.child_sort); + else + children.sort(children[0].constructor.compare); + + for (let child of children) { + var $child = this.render_element(child, templates, options); + $container.append($child); + } } return $container; } add_to_container($container, node, templates, options) { - var existing = node.parent ; + var struct = this.constructor.structure; + var existing = this.get_parent(node) ; var first_missing = node; while (existing && !($container.find('#'+existing.modelname+'-'+existing.id))) { - first_missing = existing ; - existing = existing.parent; + first_missing = existing; + existing = this.get_parent(existing); } var $to_insert = render_element(first_missing, templates, options); @@ -696,7 +706,11 @@ class ModelForest { * @param {Object} [options] Options for element render method */ display($container, templates, options) { - this.roots.sort(this.constructor.compare.bind(null, this.root_sort)); + if (this.constructor.root_sort) + this.roots.sort(this.constructor.root_sort); + else + this.roots.sort(this.roots[0].constructor.compare); + for (let root of this.roots) { $container.append(this.render_element(root, templates, options)); } @@ -704,27 +718,6 @@ class ModelForest { return $container; } - /** - * Find if node already exists in given tree - * @param {Models.TreeNode} - */ - find_node(modelname, id) { - var result = null; - - function recurse(node) { - if (node.modelname === modelname && node.content.id === id) - result = node; - - for (let child of node.children) - recurse(child); - } - - for (let root of this.roots) - recurse(root); - - return result; - } - /** * Performs for each node (in a DFS order) the callback function * on node.content and node.parent.content, if node has given modelname. @@ -732,19 +725,27 @@ class ModelForest { * @param {function} callback */ traverse(modelname, callback) { + var that = this; function recurse(node) { - if (node.modelname === modelname) { - var parent = node.parent && node.parent.content || null; - var children = node.children.map( (child) => child.content); - callback(node.content, children, parent); + if (node.constructor.verbose_name === modelname) { + if (callback(node)) { + return true; + } } - for (let child of node.children) - recurse(child); + var children = that.get_children(node); + if (children) { + for (let child of children) + if (recurse(child)) + return true; + } + + return false; } for (let root of this.roots) - recurse(root); + if (recurse(root)) + return ; } /** @@ -755,9 +756,11 @@ class ModelForest { find(modelname, id) { var result = null; - function callback(content) { - if (content.id == id) - result = content ; + function callback(node) { + if (node.id == id) { + result = node ; + return true; + } } this.traverse(modelname, callback); @@ -807,12 +810,19 @@ class ArticleList extends APIModelForest { /** * Default structure for ArticleList instances * @abstract - * @default {'article': Article, - 'category': ArticleCategory} */ - static get models() { - return {'article': Article, - 'category': ArticleCategory}; + static get structure() { + return { + 'article': { + 'model': Article, + 'parent': 'category', + }, + 'category': { + 'model': ArticleCategory, + 'children': 'articles', + }, + }; + } /** @@ -824,15 +834,6 @@ class ArticleList extends APIModelForest { static get url_model() { return Urls['kfet.kpsul.articles_data'](); } - - /** - * Provides model to sort root objects - * {@see Models.ModelForest.constructor|ModelForest.constructor} - */ - constructor() { - super(); - this.root_sort = ArticleCategory; - } } diff --git a/kfet/static/kfet/js/kpsul.js b/kfet/static/kfet/js/kpsul.js index e1bb2b55..e4623e44 100644 --- a/kfet/static/kfet/js/kpsul.js +++ b/kfet/static/kfet/js/kpsul.js @@ -599,9 +599,9 @@ class ArticleAutocomplete { updateDisplay() { var that = this; - this.manager.list.traverse('category', function(category, articles) { + this.manager.list.traverse('category', function(category) { var is_active = false; - for (let article of articles) { + for (let article of category.articles) { if (that.matching.indexOf(article) != -1) { is_active = true; that._$container.find('#article-'+article.id).show(); diff --git a/kfet/views.py b/kfet/views.py index 310d06cf..3b3189d0 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1481,7 +1481,6 @@ def kpsul_articles_data(request): 'name': article.category.name, 'has_addcost': article.category.has_addcost, }, - 'child_sort': 'article', } }) return JsonResponse(articlelist, safe=False)