UNPKG

@reyadeyat/haseb

Version:

Kateb - Reyadeyat Mathematics Book in HTML and vanilla Javascript

690 lines (639 loc) 24 kB
/* * Copyright (C) 2023-2024 Reyadeyat * * Reyadeyat/Haseb is licensed under the * BSD 3-Clause "New" or "Revised" License * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://reyadeyat.net/LICENSE/HASEB.LICENSE * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ "use strict"; "use strict"; import { StringI18n} from "../data-structure/string-i18n.js"; export class ComparisonOperator { constructor(operator, name) { this.operator = operator; this.name = name; } static fromJSON(comparison_operator_json) { return new ComparisonOperator(comparison_operator_json.operator, StringI18n.fromJSON(comparison_operator_json.name)); } static fromJSONList(comparison_operator_json_list) { let comparison_operator_list = []; comparison_operator_json_list.forEach(comparison_operator_json => comparison_operator_list.push(ComparisonOperator.fromJSON(comparison_operator_json))); return comparison_operator_list; } } export class DataAttributeHeader { constructor(name, icon) { this.name = name; this.icon = icon; } static fromJSON(data_attribute_header_json) { return new DataAttributeHeader(StringI18n.fromJSON(data_attribute_header_json.name), data_attribute_header_json.icon); } static fromJSONList(data_attribute_header_json_list) { let data_attribute_header_list = []; data_attribute_header_json_list.forEach(data_attribute_header_json => data_attribute_header_list.push(DataAttributeHeader.fromJSON(data_attribute_header_json))); return data_attribute_header_list; } } export class DataField { constructor(label, type, name) { this.label = label; this.type = type; this.name = name; this.class_name = "DataField"; } static fromJSON(data_field_json) { return new DataField(data_field_json.label, new DataType(data_field_json.type.type), data_field_json.name); } static fromJSONList(data_field_json_list) { let data_field_list = []; data_field_json_list.forEach(data_field_json => { if (data_field_json.class_name == "CompositDataField") { data_field_list.push(CompositDataField.fromJSON(data_field_json)); } else { data_field_list.push(DataField.fromJSON(data_field_json)); } }); return data_field_list; } } export class CompositDataField extends DataField { constructor(label, type, name, composit_data_field_list) { super(label, type, name); this.composit_data_field_list = composit_data_field_list; this.class_name = "CompositDataField"; } composeValue(record, field_map) { let composit = {}; this.composit_data_field_list.forEach(data_field => { let label = data_field.label.split('_').pop(); Object.defineProperty(composit, label, { value: record[field_map.get(data_field.label)], writable: true, enumerable: true, configurable: true }); }); return composit; } static fromJSON(composit_data_field_json) { return new CompositDataField(composit_data_field_json.label, new DataType(composit_data_field_json.type.type), composit_data_field_json.name, composit_data_field_json.composit_data_field_list); } static fromJSONList(composit_data_field_json_list) { let composit_data_field_list = []; composit_data_field_json_list.forEach(composit_data_field_json => composit_data_field_list.push(CompositDataField.fromJSON(composit_data_field_json))); return composit_data_field_list; } } export class DataType { constructor(type) { if (type !== "boolean" && type !== "string" && type !== "string18" && type !== "icon" && type !== "label" && type !== "number" && type !== "date" && type !== "time" && type !== "timestamp" && type !== "timestamp_with_timezone" && type !== "time_with_timezone" && type !== "composit" ) { throw new Error("Undefined DataType '" + type + "'"); } this.type = type; this.html_type = this.type === "string" || this.type === "string18" || this.type === "composit" ? "text" : this.type == "timestamp" || this.type == "timestamp_with_timezone" || this.type == "time_with_timezone" ? "time" : this.type; } isLabel() { return this.type == "label"; } isPrimitive() { return this.type != "string18" && this.type != "icon"; } value(object, key) { if (this.type === "string18" || this.type === "icon") { return object[key]; } return object; } icon(value) { if (this.type === "icon") { return value["icon"]["icon_file_url"]; } throw new Error("DataType is not icon => '" + this.type + "'"); } equals(type) { return this.type === type; } static fromJSON(data_type_json) { return new DataType(data_type_json.type); } static fromJSONList(data_type_json_list) { let data_type_list = []; data_type_json_list.forEach(data_type_json => data_type_list.push(DataType.fromJSON(data_type_json))); return data_type_list; } } export class DataAttribute { constructor(field, width_ratio, header, show_on_screen, show_on_mobile, selected_color, deselected_color, assets, style) { this.field = field; this.width_ratio = width_ratio; this.header = header; this.show_on_screen = show_on_screen; this.show_on_mobile = show_on_mobile; this.selected_color = selected_color this.deselected_color = deselected_color; this.assets = assets; this.edit = false; this.style = style; this.class_name = "DataAttribute"; } key() { return this.field.label; } setIndexInRecord(index_in_record) { this.index_in_record = index_in_record; } getIndexInRecord() { return this.index_in_record; } setColumnIndexOnScreen(column_index_on_screen) { this.column_index_on_screen = column_index_on_screen; } getColumnIndexOnScreen() { return this.column_index_on_screen; } static fromJSON(data_attribute) { return new DataAttribute(DataField.fromJSON(data_attribute.field), data_attribute.width_ratio, DataAttributeHeader.fromJSON(data_attribute.header), data_attribute.show_on_screen, data_attribute.show_on_mobile, data_attribute.selected_color, data_attribute.deselected_color, data_attribute.assets, data_attribute.style); } static fromJSONList(data_attribute_json_list) { let data_attribute_list = []; data_attribute_json_list.forEach(data_attribute_json => { if (data_attribute_json.class_name == "CompositDataAttribute") { data_attribute_list.push(CompositDataAttribute.fromJSON(data_attribute_json)); } else { data_attribute_list.push(DataAttribute.fromJSON(data_attribute_json)); } }); return data_attribute_list; } } export class CompositDataAttribute extends DataAttribute { constructor(field, width_ratio, header, show_on_screen, show_on_mobile, selected_color, deselected_color, assets, style, composit_data_field_list) { super(field, width_ratio, header, show_on_screen, show_on_mobile, selected_color, deselected_color, assets, style); this.composit_data_field_list = composit_data_field_list; this.class_name = "CompositDataAttribute"; } static fromJSON(composit_data_data_attribute) { return new CompositDataAttribute(CompositDataField.fromJSON(composit_data_data_attribute.field), composit_data_data_attribute.width_ratio, DataAttributeHeader.fromJSON(composit_data_data_attribute.header), composit_data_data_attribute.show_on_screen, composit_data_data_attribute.show_on_mobile, composit_data_data_attribute.selected_color, composit_data_data_attribute.deselected_color, composit_data_data_attribute.assets, composit_data_data_attribute.style, composit_data_data_attribute.composit_data_field_list); } static fromJSONList(composit_data_attribute_json_list) { let composit_data_attribute_list = []; composit_data_attribute_json_list.forEach(composit_data_attribute_json => composit_data_attribute_list.push(CompositDataAttribute.fromJSON(composit_data_attribute_json))); return composit_data_attribute_list; } } export class AttributeProcess { label; operation; aggregation; constructor(label, operation, aggregation) { this.label = label; this.operation = operation; this.aggregation = aggregation; } } export class Sort { attribute; asccending; constructor(attribute, asccending) { this.attribute = attribute; this.asccending = asccending; } } export class Aggregate { attribute; static minimum = 0x2; static maximum = 0x4; static summation = 0x8; static average = 0x10; static count = 0x20; aggregation; minimum_value; maximum_value; summation_value; average_value; count_value; last_requested_aggregate; constructor(attribute) { this.attribute = attribute; this.minimum_value = null; this.maximum_value = null; this.summation_value = null; this.average_value = null; this.count_value = null; this.aggregation = 0; } addAggregation(aggregation) { this.aggregation = this.aggregation | aggregation; if ((this.aggregation & Aggregate.average) == Aggregate.average) { this.aggregation = this.aggregation | Aggregate.summation; this.aggregation = this.aggregation | Aggregate.count; } this.last_requested_aggregate = aggregation; } init(value, lang) { if (this.aggregation & Aggregate.minimum) { this.minimum_value = value; } if (this.aggregation & Aggregate.maximum) { this.maximum_value = value; } if (this.attribute.field.type.type === "number") { if (this.aggregation & Aggregate.average) { this.summation_value = 0; this.count_value = 0; this.average_value = null; } else { if (this.aggregation & Aggregate.summation) { this.summation_value = 0; } if (this.aggregation & Aggregate.count) { this.count_value = 0; } } } } aggregate(value, lang) { if (this.attribute.field.type.type === "boolean") { if (this.aggregation & Aggregate.minimum) { this.minimum_value = this.minimum_value < value ? this.minimum_value : value; } if (this.aggregation & Aggregate.maximum) { this.maximum_value = this.maximum_value < value ? this.maximum_value : value; } if (this.aggregation & Aggregate.count) { this.count_value++; } } else if (this.attribute.field.type.type === "string") { if (this.aggregation & Aggregate.minimum) { this.minimum_value = this.minimum_value < value ? this.minimum_value : value; } if (this.aggregation & Aggregate.maximum) { this.maximum_value = this.maximum_value < value ? this.maximum_value : value; } if (this.aggregation & Aggregate.count) { this.count_value++; } } else if (this.attribute.field.type.type === "string18") { if (this.aggregation & Aggregate.minimum) { this.minimum_value = this.minimum_value < value[lang] ? this.minimum_value : value[lang]; } if (this.aggregation & Aggregate.maximum) { this.maximum_value = this.maximum_value < value[lang] ? this.maximum_value : value[lang]; } if (this.aggregation & Aggregate.count) { this.count_value++; } } else if (this.attribute.field.type.type === "number") { if (this.aggregation & Aggregate.minimum) { this.minimum_value = Math.min(value, this.minimum_value); } if (this.aggregation & Aggregate.maximum) { this.maximum_value = Math.max(value, this.maximum_value); } if (this.aggregation & Aggregate.average) { this.summation_value += value; this.count_value++; this.average_value = this.summation_value / this.count_value; } else { if (this.aggregation & Aggregate.summation) { this.summation_value += value; } if (this.aggregation & Aggregate.count) { this.count_value++; } } } else if (this.attribute.field.type.type === "date") { if (this.aggregation & Aggregate.minimum) { this.minimum_value = this.minimum_value < value ? this.minimum_value : value; } if (this.aggregation & Aggregate.maximum) { this.maximum_value = this.maximum_value < value ? this.maximum_value : value; } if (this.aggregation & Aggregate.count) { this.count_value++; } } } clone() { let clone = new Aggregate(this.attribute); clone.aggregation = this.aggregation; clone.last_requested_aggregate = this.last_requested_aggregate; return clone; } getMinimum() { if ((this.aggregation & Aggregate.minimum) == Aggregate.minimum) { return this.minimum_value; } return null; } getMaximum() { if ((this.aggregation & Aggregate.maximum) == Aggregate.maximum) { return this.maximum_value; } return null; } getSummation() { if ((this.aggregation & Aggregate.summation) == Aggregate.summation) { return this.summation_value; } return null; } getAverage() { if ((this.aggregation & Aggregate.average) == Aggregate.average) { return this.average_value; } return null; } getCount() { if ((this.aggregation & Aggregate.count) == Aggregate.count) { return this.count_value; } return null; } getAggregate() { if (this.last_requested_aggregate == Aggregate.minimum) { return this.minimum_value; } else if (this.last_requested_aggregate == Aggregate.maximum) { return this.maximum_value; } else if (this.last_requested_aggregate == Aggregate.summation) { return this.summation_value; } else if (this.last_requested_aggregate == Aggregate.average) { return this.average_value; } else if (this.last_requested_aggregate == Aggregate.count) { return this.count_value; } return null; } hasMinimum() { return (this.aggregation & Aggregate.minimum) == Aggregate.minimum; } hasMaximum() { return (this.aggregation & Aggregate.maximum) == Aggregate.maximum; } hasSummation() { return (this.aggregation & Aggregate.summation) == Aggregate.summation; } hasAverage() { return (this.aggregation & Aggregate.average) == Aggregate.average; } hasCount() { return (this.aggregation & Aggregate.count) == Aggregate.count; } toJSON() { return { attribute: this.attribute, minimum: this.minimum_value, maximum: this.maximum_value, summation: this.summation_value, average: this.average_value, count: this.count_value } } } export class GroupPlacement { static header = 0x2; static footer = 0x4; group; placement; constructor(group, placement) { this.group = group; this.placement = placement; } isHeaderPlacement() { return (this.placement & GroupPlacement.header) == GroupPlacement.header; } isFooterPlacement() { return (this.placement & GroupPlacement.footer) == GroupPlacement.footer; } } export class Group { className = "Group"; key; value; group_level; sort_list; aggregate_list; list_open_index; list_close_index; spliced_open_index; spliced_close_index; sub_group; sub_group_list; group_placement_list; lang; isOpen; isHidden; hiddenRows; spliced_list; current; constructor(sort, aggregate_list, group_level, lang, spliced_list) { this.sort = sort; this.group_level = group_level; try { this.key = this.sort[this.group_level].attribute; } catch (err) { console.log("this.sort => " + this.sort); console.log("this.group_level => " + this.group_level); console.log("this.sort[this.group_level].attribute => " + this.sort[this.group_level].attribute); console.error(err); } this.lang = lang; this.spliced_list = spliced_list; this.isOpen = true; this.isHidden = false; this.hiddenRows = 0; this.group_placement_list = []; this.aggregate_list = []; aggregate_list.forEach((aggregate) => { this.aggregate_list.push(aggregate.clone()); }); } openInfo(list, index) { this.list_open_index = index; let tuple = list[this.list_open_index]; let thisis = this; this.aggregate_list.forEach(aggregate => { aggregate.init(tuple[aggregate.attribute.key()], thisis.lang); aggregate.aggregate(tuple[aggregate.attribute.key()], thisis.lang); }); this.value = tuple[this.sort[this.group_level].attribute.key()]; this.current = this.sort[this.group_level].attribute.field.type.value(tuple[this.sort[this.group_level].attribute.key()], this.lang); if (this.group_level + 1 < this.sort.length) { this.sub_group_list = []; this.sub_group = new Group(this.sort, this.aggregate_list, this.group_level + 1, this.lang, this.spliced_list); this.sub_group.openInfo(list, this.list_open_index); } } openSplice(list, spliced_list, index) { this.list_open_index = index; let tuple = list[this.list_open_index]; let thisis = this; this.aggregate_list.forEach(aggregate => { aggregate.init(tuple[aggregate.attribute.key()], thisis.lang); aggregate.aggregate(tuple[aggregate.attribute.key()], thisis.lang); }); this.value = tuple[this.sort[this.group_level].attribute.key()]; this.current = this.sort[this.group_level].attribute.field.type.value(tuple[this.sort[this.group_level].attribute.key()], this.lang); this.spliced_open_index = spliced_list.length; let groupPlacement = new GroupPlacement(this, GroupPlacement.header); spliced_list.push(groupPlacement); if (this.group_level + 1 < this.sort.length) { this.sub_group_list = []; this.sub_group = new Group(this.sort, this.aggregate_list, this.group_level + 1, this.lang, this.spliced_list); this.sub_group.openSplice(list, spliced_list, this.list_open_index); } return 1; } breakInfo(list, index) { let tuple = list[index]; let value = this.sort[this.group_level].attribute.field.type.value(tuple[this.sort[this.group_level].attribute.key()], this.lang); if (this.current !== value) { this.closeInfo(index - 1); this.current = value; this.sub_group = new Group(this.sort, this.aggregate_list, this.group_level, this.lang, this.spliced_list); return true; } else { if (this.sub_group_list != null && this.sub_group.breakInfo(list, index) == true) { this.sub_group_list.push(this.sub_group); this.sub_group = new Group(this.sort, this.aggregate_list, this.group_level + 1, this.lang, this.spliced_list); this.sub_group.openInfo(list, index); } let thisis = this; this.aggregate_list.forEach(aggregate => { aggregate.aggregate(tuple[aggregate.attribute.key()], thisis.lang); }); } return false; } breakSplice(list, spliced_list, index) { let tuple = list[index]; tuple['hidden'] = false; let value = this.sort[this.group_level].attribute.field.type.value(tuple[this.sort[this.group_level].attribute.key()], this.lang); if (this.current !== value) { this.closeSplice(spliced_list, index - 1); this.current = value; this.sub_group = new Group(this.sort, this.aggregate_list, this.group_level, this.lang, this.spliced_list); return true; } else { if (this.sub_group_list != null && this.sub_group.breakSplice(list, spliced_list, index) == true) { this.sub_group_list.push(this.sub_group); this.sub_group = new Group(this.sort, this.aggregate_list, this.group_level + 1, this.lang, this.spliced_list); this.sub_group.openSplice(list, spliced_list, index); } let thisis = this; this.aggregate_list.forEach(aggregate => { aggregate.aggregate(tuple[aggregate.attribute.key()], thisis.lang); }); } return false; } closeInfo(index) { this.list_close_index = index; if (this.sub_group_list != null) { this.sub_group.closeInfo(index); this.sub_group_list.push(this.sub_group); } } closeSplice(spliced_list, index) { this.list_close_index = index; if (this.sub_group_list != null) { this.sub_group.closeSplice(spliced_list, index); this.sub_group_list.push(this.sub_group); } this.spliced_close_index = spliced_list.length; this.hiddenRows = this.spliced_close_index - this.spliced_open_index; let groupPlacement = new GroupPlacement(this, GroupPlacement.footer); spliced_list.push(groupPlacement); } toJSON() { return { key: this.key.name, value: this.key.type.value(this.value, this.lang), group_level: this.group_level, sort: this.sort, list_open_index: this.list_open_index, list_close_index: this.list_close_index, spliced_open_index: this.spliced_open_index, spliced_close_index: this.spliced_close_index, sub_group_list: this.sub_group_list, aggregate_list: this.aggregate_list }; } } export class Manipulate { static sort(list, sort, lang) { list.sort((a, b) => { let result = 0; for (let i = 0; i < sort.length; i++) { let value_a = sort[i].attribute.field.type.value(a[sort[i].attribute.key()], lang); let value_b = sort[i].attribute.field.type.value(b[sort[i].attribute.key()], lang); if (value_a === value_b) { result = 0; } else if (value_a > value_b) { result = sort[i].asccending == true ? 1 : -1; break; } else if (value_a < value_b) { result = sort[i].asccending == true ? -1 : 1; break; } } return result; }); } static group(list, spliced_list, group_by, sort_by, aggregate_list, lang, splice) { Manipulate.sort(list, sort_by, lang); let group_list = []; if (splice == false) { let group = new Group(group_by, aggregate_list, 0, lang, splice); group.openInfo(list, 0); for (let i = 1; i < list.length; i++) { if (group.breakInfo(list, i) == true) { group_list.push(group); group = new Group(group_by, aggregate_list, 0, lang, splice); group.openInfo(list, i); } } group.closeInfo(list.length - 1); group_list.push(group); } else { let group = new Group(group_by, aggregate_list, 0, lang, splice); group.openSplice(list, spliced_list, 0); spliced_list.push(list[0]); for (let i = 1; i < list.length; i++) { if (group.breakSplice(list, spliced_list, i) == true) { group_list.push(group); group = new Group(group_by, aggregate_list, 0, lang, splice); group.openSplice(list, spliced_list, i); } spliced_list.push(list[i]); } group.closeSplice(spliced_list, list.length - 1); group_list.push(group); } return group_list; } }