function __MakeTableVisualizer()

in src/scripts/mdExts.js [922:1100]


function __MakeTableVisualizer(type)
{
    class __TableVisualizer
    {
        get Database()
        {
            return this.m_database;
        }

        get Size()
        {
            return this.m_row_count;
        }

        getDimensionality()
        {
            return 1;
        }

        getValueAt(index)
        {
            if (type != "undefined")
            {
                return eval("new " + type + "(this, index)");
            }

            // For an untyped table, return values as arrays
            var result = new Array();
            var ptr = this.m_data.add(index * this.m_row_size);
            for (var i = 0; i < this.m_columns.Count(); ++i)
            {
                var dataSize = this.m_columns[index].size;
                var dataPtr = ptr.add(this.m_columns[i].offset);
                var value = host.memory.readMemoryValues(dataPtr.address, 1, dataSize)[0];
                result.push(value);
            }

            return result;
        }

        *[Symbol.iterator]()
        {
            var ptr = this.m_data;
            for (var i = 0; i < this.m_row_count; ++i)
            {
                yield new host.indexedValue(this.getValueAt(i), [i]);
            }
        }

        getValue(row, col)
        {
            if (row >= this.m_row_count)
            {
                throw new RangeError("Row index out of range: " + row);
            }
            else if (col >= this.m_columns.Count())
            {
                throw new RangeError("Column index out of range: " + col);
            }

            var dataSize = this.m_columns[col].size;
            var ptr = this.m_data.add(row * this.m_row_size + this.m_columns[col].offset);
            return host.memory.readMemoryValues(ptr.address, 1, dataSize)[0];
        }

        __lowerBound(col, value, min, max)
        {
            // First value that is not less than value
            min = min || 0;
            max = max || this.Size;

            while (min < max)
            {
                var mid = Math.floor((min + max) / 2);
                var testValue = this.getValue(mid, col);
                if (testValue < value)
                {
                    min = mid + 1;
                }
                else
                {
                    max = mid;
                }
            }

            return min;
        }

        __upperBound(col, value, min, max)
        {
            // First value that is greater than value
            min = min || 0;
            max = max || this.Size;

            while (min < max)
            {
                var mid = Math.floor((min + max) / 2);
                var testValue = this.getValue(mid, col);
                if (testValue <= value)
                {
                    min = mid + 1;
                }
                else
                {
                    max = mid;
                }
            }

            return min;
        }

        __equalRange(col, value, min, max)
        {
            var begin = this.__lowerBound(col, value, min, max);
            var end = this.__upperBound(col, value, begin, max);
            return new __TableRange(this, begin, end);
        }

        __fromCodedIndex(codedIndex, codedEnum)
        {
            var typeIndex = __codedValueType(codedIndex, codedEnum);
            if (typeIndex >= codedEnum.Values.length)
            {
                throw new RangeError("Invalid " + codedEnum.Name + " coded index: " + typeIndex);
            }

            var target = codedEnum.Values[typeIndex];
            if (target === null)
            {
                throw new RangeError("Invalid " + codedEnum.Name + " coded index: " + typeIndex);
            }

            var table = this.Database[target];
            var row = __codedValueIndex(codedIndex, codedEnum);
            if (row == -1)
            {
                return null;
            }

            return eval("new __" + target + "Visualizer(table, row)");
        }

        __getCodedIndex(row, col, codedEnum)
        {
            return this.__fromCodedIndex(this.getValue(row, col), codedEnum);
        }

        __getList(tableName, row, col)
        {
            // Lists are stored as a single index in the current row. This marks the beginning of the list. The next row in
            // the current table is the index of one past our end
            var table = this.Database[tableName];
            var begin = this.getValue(row, col);
            var end = table.Size;
            if (row + 1 < this.Size)
            {
                end = this.getValue(row + 1, col);
            }

            return new __TableRange(table, begin - 1, end - 1);
        }

        __getTargetRow(tableName, row, col)
        {
            return this.Database[tableName][this.getValue(row, col) - 1];
        }

        __getParentRow(tableName, row, col)
        {
            // The parent references the first row in this table that belongs to its list. Thus, we need to look for the
            // first value in the parent's table that references a later value in this table and then choose the previous
            var table = this.Database[tableName];
            var index = table.__upperBound(col, row + 1) - 1;
            return table[index];
        }
    }

    return __TableVisualizer;
}