<commit_before>/**
    Spreadsheet.js
    by <NAME>
    
    A simple javascript library to easily create
    and utilize tables that act as worksheets.

    https://github.com/ChiefOfGxBxL/Spreadsheet.js
*/

function Spreadsheet(ctx, row, col) {
    const DEBUG = false;
    
    // Public
    this.name = ctx.getAttribute('name');
    this.table = document.createElement('table'); // defined below by Initialization
    this.table.name = 'Tablejs-' + this.name;
    
    
    // Private variables
    var _rowCount = row,
        _colCount = col,
        _rowCounter = 0,
        _alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
        _this = this, // set to this object; allows functions to bypass functional scope and access the Spreadsheet
        oldCellValue; // used to track value for event handler onCellValueChanged
    
    
    // Private functions
    function tdDblClick(e) {
        // e.target.contentEditable = true;
        // e.target.focus();
        
        _this.onCellClick(e.target);
    }
    
    function tdBlur(e) {
        e.target.contentEditable = false;
        e.target.className = e.target.className.replace(/cellFocus/, '').trim();
        
        // Call off event handler for onCellValueChanged
        var newCellValue = e.target.textContent;
        if(newCellValue !== oldCellValue) {
            _this.onCellValueChanged(e.target, oldCellValue, newCellValue); // only called on change
        }
        oldCellValue = null;
    }
    
    function tdClick(e) {
        // remove cellFocus class from all other cells that may have this class
        _this.unfocusCells();
        _this.focusCell(e.target);
        
        _this.onCellClick(e.target);
    }
    
    function tdKeyPress(e) {
        if(DEBUG) {
            console.log(e);
        }
        
        if(e.key === 'Enter' || e.keyCode === 13) {
            e.preventDefault();
            e.target.blur();
            deselectAllText();
        }
        else if(e.key === 'Tab' || e.keyCode === 9) {
            /* select the cell to the right of this one
            else if end of line, go to the next line */
            e.preventDefault();
            e.target.blur();
            _this.unfocusCells();
            
            // move on to next cell
            var colOfTable = e.target.cellIndex,
                rowOfTable = e.target.parentElement.rowIndex,
                rowCount = _this.getRowCount(),
                colCount = _this.getColCount(),
                nextCell;
            
            /* if we tab from the top-left most content cell,
            we are at [1, 1], so now move to [1, 2] if possible */
            if(rowOfTable === rowCount && colOfTable === colCount) {
                // We are on the last cell, and thus cannot tab to a next cell
                return;
            }
            else {
                if(colOfTable < colCount) {
                    // can select cell in next column
                    nextCell = _this.selectCell(rowOfTable - 1, colOfTable);
                    _this.focusCell(nextCell);
                }
                else {
                    // overflow on column, go to next row starting at the 1st column
                    nextCell = _this.selectCell(rowOfTable, 0);
                    _this.focusCell(nextCell);
                }
            }
        }
    }
    
    function deselectAllText() {
        if(document.body.createTextRange) {
            // TODO: ms
        }
        else if(window.getSelection) {
            var selection = window.getSelection();
            selection.removeAllRanges();
        }
    }
    
    function selectText(element) {
        /*    Special thanks to <NAME> for this cross-browser snippet
            Reference: https://stackoverflow.com/questions/11128130/select-text-in-javascript
        */
        var range,
            selection;
        
        if (document.body.createTextRange) { // ms
            range = document.body.createTextRange();
            range.moveToElementText(element);
            range.select();
        }
        else if (window.getSelection) { // moz, opera, webkit
            selection = window.getSelection();
            range = document.createRange();
            range.selectNodeContents(element);
            selection.removeAllRanges();
            selection.addRange(range);
        }
    }
    
    
    // Methods
    this.addRow = function() {
        var tr = document.createElement('tr'),
            tdRowCount = document.createElement('td'),
            colC,
            td;
        
        tdRowCount.className = 'Tablejs-gray';
        tdRowCount.innerHTML = ++_rowCounter;
        tr.appendChild(tdRowCount);
        
        for(colC = 0; colC < _colCount; colC++) {
            td = document.createElement('td');
            td.innerHTML = Math.floor(Math.random()*10);
            
            // event handlers
            td.ondblclick = tdDblClick;
            td.onblur = tdBlur;
            td.onclick = tdClick;
            td.onkeypress = tdKeyPress;
            
            tr.appendChild(td);
        }
        
        this.table.tBodies[0].appendChild(tr);
        _rowCount += 1;
        
        return;
    };
    
    this.addCol = function() {
        if(_colCount === 702) {
            return; // Spreadsheet.js can go up to column ZZ = 26^2 + 26
        }
        
        var newTh = document.createElement('th'),
            newTd,
            i;
        
        newTh.innerHTML = numToLetterBase(++_colCount);
        this.table.children[0].appendChild(newTh);
        
        // iterate through each row and add a td as necessary
        for(i = 1; i <= this.getRowCount(); i++) {
            newTd = document.createElement('td');
            newTd.innerHTML = Math.floor(Math.random()*10);
            
            // event handlers
            newTd.ondblclick = tdDblClick;
            newTd.onblur = tdBlur;
            newTd.onclick = tdClick;
            newTd.onkeypress = tdKeyPress;
            
            this.table.children[i].appendChild(newTd);
        }
        
        this.onNewCol();
        return;
        
    };
    
    this.selectCell = function(row, col) {
        // selectCell(0, 0) -> table.children[1].children[1]
        return this.table.children[row+1].children[col+1];
    };
    
    this.cellContent = function(row, col) {
        return this.table.children[row+1].children[col+1].textContent;
    };
    
    this._ = function(cellname) {
        var cell = parseCellname(cellname),
            letterPart,
            digitPart;
        
        if(cell[1].length > 2) {
            return; // Spreadsheet.js only supports cellnames up to ZZ (which is 702 columns!)
        }
        
        // ex. [A, 4] or [ZZ, 210]
        letterPart = letterBaseToNum(cell[0]);
        digitPart = cell[1];
        
        return this.cellContent(digitPart-1, letterPart-1);<commit_msg>Closes #3. Fixing issue where addition of the <tbody> and <thead> elements caused errors with tabbing, adding columns, etc.<commit_after>/**
    Spreadsheet.js
    by <NAME>
    
    A simple javascript library to easily create
    and utilize tables that act as worksheets.

    https://github.com/ChiefOfGxBxL/Spreadsheet.js
*/

function Spreadsheet(ctx, row, col) {
    const DEBUG = false;
    
    // Public
    this.name = ctx.getAttribute('name');
    this.table = document.createElement('table'); // defined below by Initialization
    this.table.name = 'Tablejs-' + this.name;
    
    
    // Private variables
    var _rowCount = row,
        _colCount = col,
        _rowCounter = 0,
        _alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
        _this = this, // set to this object; allows functions to bypass functional scope and access the Spreadsheet
        oldCellValue; // used to track value for event handler onCellValueChanged
    
    
    // Private functions
    function tdDblClick(e) {
        // e.target.contentEditable = true;
        // e.target.focus();
        
        _this.onCellClick(e.target);
    }
    
    function tdBlur(e) {
        e.target.contentEditable = false;
        e.target.className = e.target.className.replace(/cellFocus/, '').trim();
        
        // Call off event handler for onCellValueChanged
        var newCellValue = e.target.textContent;
        if(newCellValue !== oldCellValue) {
            _this.onCellValueChanged(e.target, oldCellValue, newCellValue); // only called on change
        }
        oldCellValue = null;
    }
    
    function tdClick(e) {
        // remove cellFocus class from all other cells that may have this class
        _this.unfocusCells();
        _this.focusCell(e.target);
        
        _this.onCellClick(e.target);
    }
    
    function tdKeyPress(e) {
        if(DEBUG) {
            console.log(e);
        }
        
        if(e.key === 'Enter' || e.keyCode === 13) {
            e.preventDefault();
            e.target.blur();
            deselectAllText();
        }
        else if(e.key === 'Tab' || e.keyCode === 9) {
            /* select the cell to the right of this one
            else if end of line, go to the next line */
            e.preventDefault();
            e.target.blur();
            _this.unfocusCells();
            
            // move on to next cell
            var colOfTable = e.target.cellIndex,
                rowOfTable = e.target.parentElement.rowIndex,
                rowCount = _this.getRowCount(),
                colCount = _this.getColCount(),
                nextCell;
            
            /* if we tab from the top-left most content cell,
            we are at [1, 1], so now move to [1, 2] if possible */
            if(rowOfTable === rowCount && colOfTable === colCount) {
                // We are on the last cell, and thus cannot tab to a next cell
                return;
            }
            else {
                if(colOfTable < colCount) {
                    // can select cell in next column
                    nextCell = _this.selectCell(rowOfTable - 1, colOfTable);
                    _this.focusCell(nextCell);
                }
                else {
                    // overflow on column, go to next row starting at the 1st column
                    nextCell = _this.selectCell(rowOfTable, 0);
                    _this.focusCell(nextCell);
                }
            }
        }
    }
    
    function deselectAllText() {
        if(document.body.createTextRange) {
            // TODO: ms
        }
        else if(window.getSelection) {
            var selection = window.getSelection();
            selection.removeAllRanges();
        }
    }
    
    function selectText(element) {
        /*    Special thanks to <NAME> for this cross-browser snippet
            Reference: https://stackoverflow.com/questions/11128130/select-text-in-javascript
        */
        var range,
            selection;
        
        if (document.body.createTextRange) { // ms
            range = document.body.createTextRange();
            range.moveToElementText(element);
            range.select();
        }
        else if (window.getSelection) { // moz, opera, webkit
            selection = window.getSelection();
            range = document.createRange();
            range.selectNodeContents(element);
            selection.removeAllRanges();
            selection.addRange(range);
        }
    }
    
    
    // Methods
    this.addRow = function() {
        var tr = document.createElement('tr'),
            tdRowCount = document.createElement('td'),
            colC,
            td;
        
        tdRowCount.className = 'Tablejs-gray';
        tdRowCount.innerHTML = ++_rowCounter;
        tr.appendChild(tdRowCount);
        
        for(colC = 0; colC < _colCount; colC++) {
            td = document.createElement('td');
            td.innerHTML = Math.floor(Math.random()*10);
            
            // event handlers
            td.ondblclick = tdDblClick;
            td.onblur = tdBlur;
            td.onclick = tdClick;
            td.onkeypress = tdKeyPress;
            
            tr.appendChild(td);
        }
        
        this.table.tBodies[0].appendChild(tr);
        _rowCount += 1;
        
        return;
    };
    
    this.addCol = function() {
        if(_colCount === 702) {
            return; // Spreadsheet.js can go up to column ZZ = 26^2 + 26
        }
        
        var newTh = document.createElement('th'),
            newTd,
            i;
        
        newTh.innerHTML = numToLetterBase(++_colCount);
        this.table.tHead.children[0].appendChild(newTh);
        
        // iterate through each row and add a td as necessary
        for(i = 0; i < this.getRowCount(); i++) {
            newTd = document.createElement('td');
            newTd.innerHTML = Math.floor(Math.random()*10);
            
            // event handlers
            newTd.ondblclick = tdDblClick;
            newTd.onblur = tdBlur;
            newTd.onclick = tdClick;
            newTd.onkeypress = tdKeyPress;
            
            this.table.tBodies[0].children[i].appendChild(newTd);
        }
        
        this.onNewCol();
        return;
        
    };
    
    this.selectCell = function(row, col) {
        // selectCell(0, 0) -> table.children[1].children[1]
        return this.table.tBodies[0].children[row].children[col+1];
    };
    
    this.cellContent = function(row, col) {
        return this.table.children[row+1].children[col+1].textContent;
    };
    
    this._ = function(cellname) {
        var cell = parseCellname(cellname),
            letterPart,
            digitPart;
        
        if(cell[1].length > 2) {
            return; // Spreadsheet.js only supports cellnames up to ZZ (which is 702 columns!)
        }
        
        // ex. [A, 4] or [ZZ, 210]
        letterPart = letterBaseToNum(cell[0]);
        digitPart = cell[1];
        
        return this.cellContent(digitPart-1, letterPart-1);
{'fraction_non_alphanumeric': 0.10622632068053471, 'fraction_numerical': 0.008292229608978484, 'mean_word_length': 1.5417877906976745, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 13, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2802550', 'n_tokens_mistral': 4022, 'n_tokens_neox': 3868, 'n_words': 893}