diff --git a/kfet/static/kfet/css/kpsul.css b/kfet/static/kfet/css/kpsul.css
index 3d8c63f4..371331c0 100644
--- a/kfet/static/kfet/css/kpsul.css
+++ b/kfet/static/kfet/css/kpsul.css
@@ -296,30 +296,44 @@ input[type=number]::-webkit-outer-spin-button {
#articles_data {
overflow:auto;
max-height:500px;
-}
-
-#articles_data table {
width: 100%;
}
-#articles_data table tr.article {
+#articles_data div.article {
height:25px;
font-size:14px;
}
-#articles_data table tr.article>td:first-child {
- padding-left:10px;
+#articles_data span {
+ height:25px;
+ line-height:25px;
+ display: inline-block;
}
-#articles_data table tr.category {
+#articles_data span.name {
+ padding-left:10px;
+ width:78%;
+}
+
+#articles_data span.price {
+ width:8%;
+}
+
+#articles_data span.stock {
+ width:14%;
+}
+
+
+#articles_data div.category {
height:35px;
+ line-height:35px;
background-color:#c8102e;
font-size:16px;
color:#FFF;
font-weight:bold;
}
-#articles_data table tr.category>td:first-child {
+#articles_data div.category>span:first-child {
padding-left:20px;
}
diff --git a/kfet/static/kfet/js/kfet.api.js b/kfet/static/kfet/js/kfet.api.js
index 5748c3ee..4bbc5c6a 100644
--- a/kfet/static/kfet/js/kfet.api.js
+++ b/kfet/static/kfet/js/kfet.api.js
@@ -71,8 +71,10 @@ class Config {
* A model subclasses {@link Models.ModelObject}.
* A model whose instances can be got from API subclasses
* {@link Models.APIModelObject}.
- * A model to manage ModelObject lists
- * {@link Models.ModelList}.
+ * A model to manage ModelObject forests
+ * {@link Models.ModelForest}.
+ * A ModelObject that can be fetched through API
+ * {@link Models.APIModelForest}.
* These classes should not be used directly.
*
*
@@ -81,7 +83,9 @@ class Config {
* {@link Models.Checkout} (partial).
*
* Models without API support:
- * {@link Models.Statement}.
+ * {@link Models.Statement},
+ * {@link Models.ArticleCategory},
+ * {@link Models.Article}.
*
* @namespace Models
*/
@@ -107,13 +111,6 @@ class ModelObject {
*/
static get default_data() { return {}; }
- /**
- * Verbose name so refer to this model
- * @abstract
- * @type {string}
- */
- static get verbose_name() { return ""; }
-
/**
* Create new instance from data or default values.
* @param {Object} [data={}] - data to store in instance
@@ -171,15 +168,12 @@ class ModelObject {
}
/**
- * Compare function between two instances of the model
- * @abstract
- * @param {a} Models.ModelObject
- * @param {b} Models.ModelObject
+ * Returns a string value for the model, to use in comparisons
+ * @see {@link Models.TreeNode.compare|TreeNode.compare}
*/
- static compare(a, b) {
- return a.id - b.id ;
+ comparevalue() {
+ return this.id.toString();
}
-
}
@@ -493,13 +487,6 @@ class ArticleCategory extends ModelObject {
return {'id': 0, 'name': ''};
}
- /**
- * Verbose name for ArticleCategory model.
- * @default 'article_category'
- * @see {@link Models.ModelObject.verbose_name[ModelObject.verbose_name}
- */
- static get verbose_name() { return 'category'; }
-
/**
* @default {@link Formatters.ArticleCategoryFormatter}
*/
@@ -511,8 +498,8 @@ class ArticleCategory extends ModelObject {
* Comparison function between ArticleCategory model instances.
* @see {@link Models.ModelObject.compare|ModelObject.compare}
*/
- static compare(a, b) {
- return a.name.localeCompare(b.name);
+ comparevalue() {
+ return this.name ;
}
}
@@ -544,13 +531,6 @@ class Article extends ModelObject {
};
}
- /**
- * Verbose name for Article model
- * @default 'article'
- * @see {@link Models.ModelObject.verbose_name|ModelObject.verbose_name}
- */
- static get verbose_name() { return 'article'; }
-
/**
* @default {@link Formatters.ArticleFormatter}
*/
@@ -562,8 +542,8 @@ class Article extends ModelObject {
* Comparison function between Article model instances.
* @see {@link Models.ModelObject.compare|ModelObject.compare}
*/
- static compare(a, b) {
- return a.name.localeCompare(b.name);
+ comparevalue() {
+ return this.name;
}
// Take care of 'price' type
@@ -757,136 +737,148 @@ class Operation extends ModelObject {
/**
- * Simple {@link Models.ModelObject} list.
+ * Node for ModelForest object
* @memberof Models
*/
-class ModelList {
-
- /**
- * Nested structure of the list
- * @abstract
- * @type {Models.ModelObject[]}
- */
- static get models() { return []; }
-
- /**
- * Verbose names for list models
- * @abstract
- * @type {string[]}
- */
- static get names() {
- return this.models.map(function(v) {
- return v.verbose_name;
- });
+class TreeNode {
+
+ constructor(type, content) {
+ this.type = type;
+ this.content = content;
+ this.parent = null;
+ this.children = [];
}
+ static compare(a, b) {
+ var a_serial = a.content.comparevalue();
+ var b_serial = b.content.comparevalue();
+
+ return a_serial.localeCompare(b_serial)
+ }
+}
+
+
+/**
+ * Simple {@link Models.ModelObject} forest.
+ * @memberof Models
+ */
+class ModelForest {
+
+ /**
+ * Dictionary associating types to classes
+ * @abstract
+ * @type {Object}
+ */
+ static get models() { return {}; }
+
+
/**
* Creates empty instance and populates it with data if given
* @param {Object[]} [datalist=[]]
*/
constructor(datalist) {
- this.data = {};
this.from(datalist || []);
}
/**
* Fetches an object from the instance data, or creates it if
* it does not exist yet.
- * Parent objects are created recursively if needed.
- * @param {number} depth depth on the nested structure of the list
+ * If direction >= 0, parent objects are created recursively.
+ * If direction <= 0, child objects are created recursively.
* @param {Object} data
+ * @param {number} direction
*/
- get_or_create(depth, data) {
- var model = this.constructor.models[depth];
- var name = model.verbose_name ;
-
- var existing = this.data[name].find(function (v){
- return v.id === data['id'] ;
- }) ;
+ get_or_create(data, direction) {
+ var model = this.constructor.models[data.type];
+ var existing = this.find(data.type, data.content.id);
if (existing) {
return existing;
}
- if (depth == this.constructor.models.length-1) {
- var created = new model(data) ;
- this.data[name].push(created);
- return created ;
- } else {
- var par_name = this.constructor.models[depth+1]
- .verbose_name;
+ var content = new this.constructor.models[data.type](data.content);
+ var node = new TreeNode(data.type, content);
- var par_data = data[par_name];
- var created = new model(data);
- var parnt = this.get_or_create(depth+1, par_data);
- created[par_name] = parnt;
-
- this.data[name].push(created);
- return created ;
+ if (direction <= 0) {
+ if (data.parent) {
+ var parent = this.get_or_create(data.parent, -1);
+ node.parent = parent;
+ parent.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);
+ }
+ }
+
+ return node ;
}
/**
- * Resets then populates the instance with the given data, starting from
- * the lowest level Models.ModelObject in {@link Models.ModelList#models|models}.
+ * Resets then populates the instance with the given data.
* @param {Object[]} datalist
*/
from(datalist) {
-
- for (let key of this.constructor.names) {
- this.data[key] = [];
- }
-
+ this.roots = [];
for (let data of datalist) {
- this.get_or_create(0, data);
+ this.get_or_create(data, 0);
}
}
/**
- * Removes all Models.ModelObject from the list.
+ * Removes all Models.TreeNode from the tree.
*/
clear() {
this.from([]);
}
/**
- * Renders an element (and all its offspring) and returns the
+ * Renders a node (and all its offspring) and returns the
* corresponding jQuery object.
- * @param {Models.ModelObject} elt
+ * @param {Models.TreeNode} node
* @param {Object} templates Templates to render each model
* @param {Object} [options] Options for element render method
*/
- render_element(elt, templates, options) {
- var name = elt.constructor.verbose_name;
- var depth = this.constructor.names.indexOf(name);
- // Allows for more granular template specification
- var template = templates[elt.type] || templates[name];
+ render_element(node, templates, options) {
+ var template = templates[node.type];
var options = options || {} ;
- if (depth == -1) {
- return $();
- } else if (depth == 0) {
- var $rendered = elt.display($(template), options);
- $rendered.attr('data-'+name+'-id', elt.id);
- return $rendered;
- } else {
- var child_model = this.constructor.models[depth-1];
- var children = this.data[child_model.verbose_name]
- .filter(v => v[name].id == elt.id) ;
- children.sort(child_model.compare);
+ var $container = $('