<commit_before>/**
 * Copyright 2015-present Tuan Le.
 *
 * Licensed under the MIT License.
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://opensource.org/licenses/mit-license.html
 *
 * 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.
 *
 *------------------------------------------------------------------------
 *
 * @module PGComposite
 * @description - A node postgres composite.
 *
 * @author Tuan Le (tuan.t.lei@gmail.com)
 */
/* @flow */
'use strict'; // eslint-disable-line

/* load CompositeElement */
import CompositeElement from '../../../core/elements/composite-element';

/* load CommonElement */
import CommonElement from '../../../core/elements/common-element';

/* create CommonElement as Hf object */
const Hf = CommonElement();

/**
* @description - Node postgres composite module.
*
* @module PGComposite
* @return {object}
*/
export default CompositeElement({
    enclosure: {
        PGComposite: function PGComposite () {
            /* ----- Private Variables ------------- */
            /* ----- Public Functions -------------- */
            /**
             * @description - Get service provider.
             *
             * @method getProvider
             * @return void
             */
            this.getProvider = function getProvider () {
                Hf.log(`error`, `PGComposite.getProvider - Method is not implemented by default.`);
            };
        }
    },
    template: {
        /**
         * @description - Initialized and check that service is valid for this composite.
         *
         * @method $initPGComposite
         * @return void
         */
        $initPGComposite: function $initPGComposite () {
            const service = this;
            if (Hf.DEVELOPMENT) {
                if (!Hf.isSchema({
                    dbServer: {
                        name: `string`
                    },
                    dbServerUrl: `string`
                }).of(service)) {
                    Hf.log(`error`, `PGComposite.$init - Service is invalid. Cannot apply composite.`);
                }
            }
        },
        /**
         * @description - Do query from database.
         *
         * @method query
         * @param {string} tableName
         * @return {object}
         */
        query: function query (tableName) {
            const service = this;
            if (!Hf.isString(tableName)) {
                Hf.log(`error`, `PGComposite.query - Input table name is invalid.`);
            } else {
                const {
                    dbServer,
                    dbServerUrl,
                    getProvider
                } = service;
                const dbName = dbServer.name;
                const {
                    pg,
                    sql
                } = getProvider();
                if (!Hf.isObject(pg) || !Hf.isObject(sql)) {
                    Hf.log(`error`, `PGComposite.query - Postgres driver or squel builder provider is not unsupported.`);
                } else {
                    const sqlPostgres = sql.useFlavour(`postgres`);
                    return {
                        /**
                         * @description - Do select query on data base.
                         *
                         * @param {function} sqlCreate
                         * @method query.select
                         */
                        select: function select (sqlCreate) {
                            if (!Hf.isFunction(sqlCreate)) {
                                Hf.log(`error`, `PGComposite.query.select - Input squel function is invalid.`);
                            } else {
                                return new Promise((resolve, reject) => {
                                    pg.connect(dbServerUrl, (error, client, done) => {
                                        if (error) {
                                            reject(new Error(`ERROR: Unable to connect to database:${dbName}, ${error.message}`));
                                        } else {
                                            const results = [];
                                            /* do sql query select */
                                            const statementSQL = sqlCreate(sqlPostgres.select({
                                                separator: `\n`,
                                                autoQuoteFieldNames: true,
                                                nameQuoteCharacter: `"`,
                                                tableAliasQuoteCharacter: `|`,
                                                fieldAliasQuoteCharacter: `~`
                                            }).from(tableName));
                                            if (!Hf.isObject(statementSQL)) {
                                                reject(new Error(`ERROR: SQL statement object is invalid.`));
                                            } else {
                                                const selectQuery = client.query(statementSQL.toParam());
                                                Hf.log(`info`, `Submitted query statement:${statementSQL.toString()}.`);
                                                /* stream results back one row at a time */
                                                selectQuery.on(`row`, (row) => {
                                                    results.push(row);
                                                    // if (row.hasOwnProperty(`id`)) {
                                                    //     results.push(row);
                                                    // } else {
                                                    //     reject(new Error(`ERROR: Unable to query table:${dbName}.${tableName}. Missing required Id field.`));
                                                    // }
                                                });

                                                /* after all data is returned, close connection and resolve results */
                                                selectQuery.on(`end`, () => {
                                                    // client.end();
                                                    resolve(results);
                                                    /* call `done()` to release the client back to the pool */
                                                    done();
                                                });

                                                selectQuery.on(`error`, (_error) => {
                                                    reject(new Error(`ERROR: Unable to query table:${dbName}.${tableName}, ${_error.message}`));
                                                });
                                            }
                                        }
                                    });
                                });
                            }
                        },
                        /**
                         * @description - Do insert query on data base.
                         *
                         * @param {function} sqlCreate
                         * @method query.update
                         */
                        update: function update (sqlCreate) {
                            if (!Hf.isFunction(sqlCreate)) {
                                Hf.log(`error`, `PGComposite.query.update - Input squel function is invalid.`);
                            } else {
                                return new Promise((resolve, reject) => {
                                    pg.connect(dbServerUrl, (error, client, done) => {
                                        if (error) {
                                            reject(new Error(`ERROR: Unable to connect to database:${dbName}, ${error.message}`));
                                        } else {
                                            // let id;
                                            let result;
                                            /* do sql query update */
                                            const statementSQL = sqlCreate(sqlPostgres.update({
                                                separator: `\n`,
                                                autoQuoteFieldNames: true,
                                                nameQuoteCharacter: `"`,
                                                tableAliasQuoteCharacter: `|`,
                                                fieldAliasQuoteCharacter: `~`
                                            }).table(tableName));
                                            if (!Hf.isObject(statementSQL)) {
                                                reject(new Error(`ERROR: SQL statement object is invalid.`));
                                            } else {
                                                const updateQuery = client.query(statementSQL.returning(`*`).toParam());
                                                Hf.log(`info`, `Submitted query statement:${statementSQL.toString()}.`);

                                                updateQuery.on(`row`, (row) => {
                                                    result = row;
                                                    // if (row.hasOwnProperty(`id`)) {
                                                    //     id = row.id;
                                                    // } else {
                                                    //     reject(new Error(`ERROR: Unable to query table:${dbName}.${tableName}. Missing required Id field.`));
                                                    // }
                                                });

                                                /* after data is updated, close connection and resolve the Id */
                                                updateQuery.on(`end`, () => {
                                                    // client.end();
                                                    resolve(result);
                                                    /* call `done()` to release the client back to the pool */
                                                    done();
                                                });

                                                updateQuery.on(`error`, (_error) => {
                                                    reject(new Error(`ERROR: Unable to query table:${dbName}.${tableName}, ${_error.message}`));
                                                });
                                            }
                                        }
                                    });
                                });
                            }
                        },
                        /**
                         * @description - Do insert query on data base.
                         *
                         * @param {function} sqlCreate
                         * @method query.insert
                         */
                        insert: function insert (sqlCreate) {
                            if (!Hf.isFunction(sqlCreate)) {
                                Hf.log(`error`, `PGComposite.query.insert - Input squel function is invalid.`);
                            } else {
                                return new Promise((resolve, reject) => {
                                    pg.connect(dbServerUrl, (error, client, done) => {
                                        if (error) {
                                            reject(new Error(`ERROR: Unable to connect to database:${dbName}, ${error.message}`));
                                        } else {
                                            // let id;
                                            let result;
                                            /* do sql query insert */
                                            const statementSQL = sqlCreate(sqlPostgres.insert({
                                                separator: `\n`,
                                                autoQuoteFieldNames: true,
                                                nameQuoteCharacter: `"`,
                                                tableAliasQuoteCharacter: `|`,
                                                fieldAliasQuoteCharacter: `~`
                                            }).into(tableName));
                                            if (!Hf.isObject(statementSQL)) {
                                                reject(new Error(`ERROR: SQL statement object is invalid.`));
                                            } else {
                                                const insertQuery = client.query(statementSQL.returning(`*`).toParam());
                                                Hf.log(`info`, `Submitted query statement:${statementSQL.toString()}.`);

                                                insertQuery.on(`row`, (row) => {
                                                    result = row;
                                                    // if (row.hasOwnProperty(`id`)) {
                                                    //     id = row.id;
                                                    // } else {
                                                    //     reject(new Error(`ERROR: Unable to query table:${dbName}.${tableName}. Missing required Id field.`));
                                                    // }
                                                });

                                                /* after data is inserted, close connection and resolve the Id */
                                                insertQuery.on(`end`, () => {
                                                    // client.end();
                                                    resolve(result);
                                                    /* call `done()` to release the client back to the pool */
                                                    done();
                                                });

                                                insertQuery.on(`error`, (_error) => {
                                                    reject(new Error(`ERROR: Unable to query table:${dbName}.${tableName}, ${_error.message}`));
                                                });
                                            }
                                        }
                                    });
                                });
                            }
                        }
                        /**
                         * @description - Do delete query on data base.
                         *
                         * @param {function} sqlCreate
                         * @method query.delete
                         */
                        // delete: function delete (sqlCreate) {
                        //     // TODO: Needs implementation.
                        // }
                    };
                }
            }
        }
    }
});
<commit_msg>Added delete query method. Fixed query results return for insert and update method.<commit_after>/**
 * Copyright 2015-present Tuan Le.
 *
 * Licensed under the MIT License.
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://opensource.org/licenses/mit-license.html
 *
 * 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.
 *
 *------------------------------------------------------------------------
 *
 * @module PGComposite
 * @description - A node postgres composite.
 *
 * @author Tuan Le (tuan.t.lei@gmail.com)
 */
/* @flow */
'use strict'; // eslint-disable-line

/* load CompositeElement */
import CompositeElement from '../../../core/elements/composite-element';

/* load CommonElement */
import CommonElement from '../../../core/elements/common-element';

/* create CommonElement as Hf object */
const Hf = CommonElement();

/**
* @description - Node postgres composite module.
*
* @module PGComposite
* @return {object}
*/
export default CompositeElement({
    enclosure: {
        PGComposite: function PGComposite () {
            /* ----- Private Variables ------------- */
            /* ----- Public Functions -------------- */
            /**
             * @description - Get service provider.
             *
             * @method getProvider
             * @return void
             */
            this.getProvider = function getProvider () {
                Hf.log(`error`, `PGComposite.getProvider - Method is not implemented by default.`);
            };
        }
    },
    template: {
        /**
         * @description - Initialized and check that service is valid for this composite.
         *
         * @method $initPGComposite
         * @return void
         */
        $initPGComposite: function $initPGComposite () {
            const service = this;
            if (Hf.DEVELOPMENT) {
                if (!Hf.isSchema({
                    dbServer: {
                        name: `string`
                    },
                    dbServerUrl: `string`
                }).of(service)) {
                    Hf.log(`error`, `PGComposite.$init - Service is invalid. Cannot apply composite.`);
                }
            }
        },
        /**
         * @description - Do query from database.
         *
         * @method query
         * @param {string} tableName
         * @return {object}
         */
        query: function query (tableName) {
            const service = this;
            if (!Hf.isString(tableName)) {
                Hf.log(`error`, `PGComposite.query - Input table name is invalid.`);
            } else {
                const {
                    dbServer,
                    dbServerUrl,
                    getProvider
                } = service;
                const dbName = dbServer.name;
                const {
                    pg,
                    sql
                } = getProvider();
                if (!Hf.isObject(pg) || !Hf.isObject(sql)) {
                    Hf.log(`error`, `PGComposite.query - Postgres driver or squel builder provider is not unsupported.`);
                } else {
                    const sqlPostgres = sql.useFlavour(`postgres`);
                    return {
                        /**
                         * @description - Do select query on data base.
                         *
                         * @param {function} sqlCreate
                         * @method query.select
                         */
                        select: function select (sqlCreate) {
                            if (!Hf.isFunction(sqlCreate)) {
                                Hf.log(`error`, `PGComposite.query.select - Input squel function is invalid.`);
                            } else {
                                return new Promise((resolve, reject) => {
                                    pg.connect(dbServerUrl, (error, client, done) => {
                                        if (error) {
                                            reject(new Error(`ERROR: Unable to connect to database:${dbName}, ${error.message}`));
                                        } else {
                                            const results = [];
                                            /* do sql query select */
                                            const statementSQL = sqlCreate(sqlPostgres.select({
                                                separator: `\n`,
                                                autoQuoteFieldNames: true,
                                                nameQuoteCharacter: `"`,
                                                tableAliasQuoteCharacter: `|`,
                                                fieldAliasQuoteCharacter: `~`
                                            }).from(tableName));
                                            if (!Hf.isObject(statementSQL)) {
                                                reject(new Error(`ERROR: SQL statement object is invalid.`));
                                            } else {
                                                const selectQuery = client.query(statementSQL.toParam());
                                                Hf.log(`info`, `Submitted query statement:${statementSQL.toString()}.`);
                                                /* stream results back one row at a time */
                                                selectQuery.on(`row`, (row) => results.push(row));

                                                /* after all data is returned, close connection and resolve results */
                                                selectQuery.on(`end`, () => {
                                                    // client.end();
                                                    resolve(results);
                                                    /* call `done()` to release the client back to the pool */
                                                    done();
                                                });

                                                selectQuery.on(`error`, (_error) => {
                                                    reject(new Error(`ERROR: Unable to query table:${dbName}.${tableName}, ${_error.message}`));
                                                });
                                            }
                                        }
                                    });
                                });
                            }
                        },
                        /**
                         * @description - Do insert query on data base.
                         *
                         * @param {function} sqlCreate
                         * @method query.update
                         */
                        update: function update (sqlCreate) {
                            if (!Hf.isFunction(sqlCreate)) {
                                Hf.log(`error`, `PGComposite.query.update - Input squel function is invalid.`);
                            } else {
                                return new Promise((resolve, reject) => {
                                    pg.connect(dbServerUrl, (error, client, done) => {
                                        if (error) {
                                            reject(new Error(`ERROR: Unable to connect to database:${dbName}, ${error.message}`));
                                        } else {
                                            const results = [];
                                            /* do sql query update */
                                            const statementSQL = sqlCreate(sqlPostgres.update({
                                                separator: `\n`,
                                                autoQuoteFieldNames: true,
                                                nameQuoteCharacter: `"`,
                                                tableAliasQuoteCharacter: `|`,
                                                fieldAliasQuoteCharacter: `~`
                                            }).table(tableName));
                                            if (!Hf.isObject(statementSQL)) {
                                                reject(new Error(`ERROR: SQL statement object is invalid.`));
                                            } else {
                                                const updateQuery = client.query(statementSQL.returning(`*`).toParam());
                                                Hf.log(`info`, `Submitted query statement:${statementSQL.toString()}.`);

                                                updateQuery.on(`row`, (row) => results.push(row));

                                                /* after data is updated, close connection and resolve the Id */
                                                updateQuery.on(`end`, () => {
                                                    // client.end();
                                                    resolve(results);
                                                    /* call `done()` to release the client back to the pool */
                                                    done();
                                                });

                                                updateQuery.on(`error`, (_error) => {
                                                    reject(new Error(`ERROR: Unable to query table:${dbName}.${tableName}, ${_error.message}`));
                                                });
                                            }
                                        }
                                    });
                                });
                            }
                        },
                        /**
                         * @description - Do insert query on data base.
                         *
                         * @param {function} sqlCreate
                         * @method query.insert
                         */
                        insert: function insert (sqlCreate) {
                            if (!Hf.isFunction(sqlCreate)) {
                                Hf.log(`error`, `PGComposite.query.insert - Input squel function is invalid.`);
                            } else {
                                return new Promise((resolve, reject) => {
                                    pg.connect(dbServerUrl, (error, client, done) => {
                                        if (error) {
                                            reject(new Error(`ERROR: Unable to connect to database:${dbName}, ${error.message}`));
                                        } else {
                                            const results = [];
                                            /* do sql query insert */
                                            const statementSQL = sqlCreate(sqlPostgres.insert({
                                                separator: `\n`,
                                                autoQuoteFieldNames: true,
                                                nameQuoteCharacter: `"`,
                                                tableAliasQuoteCharacter: `|`,
                                                fieldAliasQuoteCharacter: `~`
                                            }).into(tableName));
                                            if (!Hf.isObject(statementSQL)) {
                                                reject(new Error(`ERROR: SQL statement object is invalid.`));
                                            } else {
                                                const insertQuery = client.query(statementSQL.returning(`*`).toParam());
                                                Hf.log(`info`, `Submitted query statement:${statementSQL.toString()}.`);

                                                insertQuery.on(`row`, (row) => results.push(row));

                                                /* after data is inserted, close connection and resolve the Id */
                                                insertQuery.on(`end`, () => {
                                                    // client.end();
                                                    resolve(results);
                                                    /* call done to release the client back to the pool */
                                                    done();
                                                });

                                                insertQuery.on(`error`, (_error) => {
                                                    reject(new Error(`ERROR: Unable to query table:${dbName}.${tableName}, ${_error.message}`));
                                                });
                                            }
                                        }
                                    });
                                });
                            }
                        },
                        /**
                         * @description - Do delete query on data base.
                         *
                         * @param {function} sqlCreate
                         * @method query.delete
                         */
                        delete: function _delete (sqlCreate) {
                            if (!Hf.isFunction(sqlCreate)) {
                                Hf.log(`error`, `PGComposite.query.delete - Input squel function is invalid.`);
                            } else {
                                return new Promise((resolve, reject) => {
                                    pg.connect(dbServerUrl, (error, client, done) => {
                                        if (error) {
                                            reject(new Error(`ERROR: Unable to connect to database:${dbName}, ${error.message}`));
                                        } else {
                                            const results = [];
                                            /* do sql query delete */
                                            const statementSQL = sqlCreate(sqlPostgres.delete({
                                                separator: `\n`,
                                                autoQuoteFieldNames: true,
                                                nameQuoteCharacter: `"`,
                                                tableAliasQuoteCharacter: `|`,
                                                fieldAliasQuoteCharacter: `~`
                                            }).from(tableName));
                                            if (!Hf.isObject(statementSQL)) {
                                                reject(new Error(`ERROR: SQL statement object is invalid.`));
                                            } else {
                                                const deleteQuery = client.query(statementSQL.returning(`*`).toParam());
                                                Hf.log(`info`, `Submitted query statement:${statementSQL.toString()}.`);

                                                deleteQuery.on(`row`, (row) => results.push(row));

                                                /* after data is deleted, close connection and resolve the Id */
                                                deleteQuery.on(`end`, () => {
                                                    // client.end();
                                                    resolve(results);
                                                    /* call done to release the client back to the pool */
                                                    done();
                                                });

                                                deleteQuery.on(`error`, (_error) => {
                                                    reject(new Error(`ERROR: Unable to query table:${dbName}.${tableName}, ${_error.message}`));
                                                });
                                            }
                                        }
                                    });
                                });
                            }
                        }
                    };
                }
            }
        }
    }
});
<|endoftext|>


<commit_before>let range = n => [...Array(n)].map((_, i) => i)
let makeTuples = arr => arr.map(x => arr.map(y => [x, y])).reduce((sum, x) => [...sum, ...x], [])
let hasDuplicates = arr => arr.sort().some((x, i) => i > 0 && x != null && arr[i - 1] === x)
let random = n => Math.floor(Math.random() * n)
let swap = (arr, i, j) => ([arr[i], arr[j]] = [arr[j], arr[i]], arr)
let shuffle = arr => (arr.forEach((_, i) => swap(arr, arr.length - i - 1, random(arr.length - i))), arr)

let range9 = range(9)
let _grid = makeTuples(range9)
let _boxes = [
    // Rows
    ...range9.map(i => range9.map(j => [i, j])),
    // Columns
    ...range9.map(i => range9.map(j => [j, i])),
    // Squares
    ...makeTuples([0, 3, 6]).map(([tx, ty]) =>
        makeTuples(range(3)).map(([i, j]) => [i + ty, j + tx])
    )
]

class Sudoku {
    constructor(arrangement = null) {
        this.arrangement = []
        this.excluded = []
        this.solids = []

        for (let y = 0; y < 9; y++) {
            this.arrangement[y] = arrangement ? [...arrangement[y]] : Array(9).fill(null)
            this.excluded[y] = [...Array(9)].map(_ => [])
        }
    }

    set([x, y], number) {
        this.arrangement[y][x] = number
    }

    get([x, y]) {
        return this.arrangement[y][x]
    }

    clone() {
        let puzzle = new Sudoku(this.arrangement)
        puzzle.solids = this.solids
        return puzzle
    }

    hasContradictions(box) {
        if (box != null) return hasDuplicates(box.map(v => this.get(v)))
        return this.getBoxes().some(this.hasContradictions.bind(this))
    }

    getGrid() {
        return _grid
    }

    getContradiction() {
        for (let box of this.getBoxes()) {
            for (let v of box) {
                if (this.get(v) == null) continue

                for (let w of box) {
                    if (Sudoku.vertexEquals(v)(w) || this.get(v) !== this.get(w))
                        continue

                    return [v, w]
                }
            }
        }

        return []
    }

    getBoxes(vertex) {
        if (!vertex) return _boxes
        return _boxes.filter(box => box.some(Sudoku.vertexEquals(vertex)))
    }

    getTrivialMarkup() {
        let result = [...Array(9)].map(_ => Array(9).fill(null))

        for (let [x, y] of this.getGrid()) {
            result[y][x] = range9.map(i => i + 1)

            for (let box of this.getBoxes([x, y])) {
                for (let w of box) {
                    let index = result[y][x].indexOf(this.get(w))
                    if (index >= 0) result[y][x].splice(index, 1)
                }
            }

            if (this.get([x, y]) != null) {
                result[y][x].push(this.get([x, y]))
            }
        }

        return result
    }

    getCurrentMarkup() {
        let markup = this.getTrivialMarkup()

        for (let [x, y] of this.getGrid()) {
            markup[y][x] = markup[y][x].filter(i => !this.excluded[y][x].includes(i))
        }

        return markup
    }

    solve(options = {}) {
        let {timeout = Infinity, startTime = Date.now()} = options
        let noEmptyVertices = true

        for (let vertex of this.getGrid()) {
            if (this.get(vertex) != null) continue

            let neighbor = this.clone()
            noEmptyVertices = false

            for (let number of shuffle(range(9))) {
                neighbor.set(vertex, number + 1)
                if (neighbor.hasContradictions()) continue

                if (Date.now() - startTime > timeout)
                    throw new Error('Timeout')

                let result = neighbor.solve(options)
                if (result != null) return result

                neighbor.set(vertex, null)
            }

            return null
        }

        if (noEmptyVertices) return this
        return null
    }
}

Sudoku.vertexEquals = v => w => v[0] === w[0] && v[1] === w[1]

Sudoku.generatePuzzle = function(options = {}) {
    let {timeout = Infinity, givens = 0, onProgress = () => {}} = options
    let puzzle = new Sudoku().solve()
    let notEmpty = v => puzzle.get(v) != null
    let startTime = Date.now()
    let i = 0
    let n = 0

    for (let vertex of shuffle([...puzzle.getGrid()])) {
        let number = puzzle.get(vertex)
        let boxes = puzzle.getBoxes(vertex)
        let feasible = boxes.some(box => box.filter(notEmpty).length === 9)

        if (!feasible) {
            // Check for multiple solutions

            feasible = true

            for (let n of shuffle(range(9))) {
                if (n + 1 === number) continue
                puzzle.set(vertex, n + 1)

                try {
                    feasible = puzzle.solve({timeout: 1000}) == null
                } catch (err) {
                    feasible = false
                }

                if (!feasible || Date.now() - startTime > timeout) {
                    feasible = false
                    break
                }
            }
        }

        puzzle.set(vertex, feasible ? null : number)

        if (feasible) i++
        if (i >= 81 - givens) break

        onProgress(++n / puzzle.getGrid().length)
    }

    puzzle.solids = puzzle.getGrid().filter(v => puzzle.get(v) != null)

    return puzzle
}

module.exports = Sudoku
<commit_msg>Add Sudoku.parse()<commit_after>let range = n => [...Array(n)].map((_, i) => i)
let makeTuples = arr => arr.map(x => arr.map(y => [x, y])).reduce((sum, x) => [...sum, ...x], [])
let hasDuplicates = arr => arr.sort().some((x, i) => i > 0 && x != null && arr[i - 1] === x)
let random = n => Math.floor(Math.random() * n)
let swap = (arr, i, j) => ([arr[i], arr[j]] = [arr[j], arr[i]], arr)
let shuffle = arr => (arr.forEach((_, i) => swap(arr, arr.length - i - 1, random(arr.length - i))), arr)

let range9 = range(9)
let _grid = makeTuples(range9)
let _boxes = [
    // Rows
    ...range9.map(i => range9.map(j => [i, j])),
    // Columns
    ...range9.map(i => range9.map(j => [j, i])),
    // Squares
    ...makeTuples([0, 3, 6]).map(([tx, ty]) =>
        makeTuples(range(3)).map(([i, j]) => [i + ty, j + tx])
    )
]

class Sudoku {
    constructor(arrangement = null) {
        this.arrangement = []
        this.excluded = []
        this.solids = []

        for (let y = 0; y < 9; y++) {
            this.arrangement[y] = arrangement ? [...arrangement[y]] : Array(9).fill(null)
            this.excluded[y] = [...Array(9)].map(_ => [])
        }
    }

    set([x, y], number) {
        this.arrangement[y][x] = number
    }

    get([x, y]) {
        return this.arrangement[y][x]
    }

    clone() {
        let puzzle = new Sudoku(this.arrangement)
        puzzle.solids = this.solids
        return puzzle
    }

    hasContradictions(box) {
        if (box != null) return hasDuplicates(box.map(v => this.get(v)))
        return this.getBoxes().some(this.hasContradictions.bind(this))
    }

    getGrid() {
        return _grid
    }

    getContradiction() {
        for (let box of this.getBoxes()) {
            for (let v of box) {
                if (this.get(v) == null) continue

                for (let w of box) {
                    if (Sudoku.vertexEquals(v)(w) || this.get(v) !== this.get(w))
                        continue

                    return [v, w]
                }
            }
        }

        return []
    }

    getBoxes(vertex) {
        if (!vertex) return _boxes
        return _boxes.filter(box => box.some(Sudoku.vertexEquals(vertex)))
    }

    getTrivialMarkup() {
        let result = [...Array(9)].map(_ => Array(9).fill(null))

        for (let [x, y] of this.getGrid()) {
            result[y][x] = range9.map(i => i + 1)

            for (let box of this.getBoxes([x, y])) {
                for (let w of box) {
                    let index = result[y][x].indexOf(this.get(w))
                    if (index >= 0) result[y][x].splice(index, 1)
                }
            }

            if (this.get([x, y]) != null) {
                result[y][x].push(this.get([x, y]))
            }
        }

        return result
    }

    getCurrentMarkup() {
        let markup = this.getTrivialMarkup()

        for (let [x, y] of this.getGrid()) {
            markup[y][x] = markup[y][x].filter(i => !this.excluded[y][x].includes(i))
        }

        return markup
    }

    solve(options = {}) {
        let {timeout = Infinity, startTime = Date.now()} = options
        let noEmptyVertices = true

        for (let vertex of this.getGrid()) {
            if (this.get(vertex) != null) continue

            let neighbor = this.clone()
            noEmptyVertices = false

            for (let number of shuffle(range(9))) {
                neighbor.set(vertex, number + 1)
                if (neighbor.hasContradictions()) continue

                if (Date.now() - startTime > timeout)
                    throw new Error('Timeout')

                let result = neighbor.solve(options)
                if (result != null) return result

                neighbor.set(vertex, null)
            }

            return null
        }

        if (noEmptyVertices) return this
        return null
    }
}

Sudoku.vertexEquals = v => w => v[0] === w[0] && v[1] === w[1]

Sudoku.generatePuzzle = function(options = {}) {
    let {timeout = Infinity, givens = 0, onProgress = () => {}} = options
    let puzzle = new Sudoku().solve()
    let notEmpty = v => puzzle.get(v) != null
    let startTime = Date.now()
    let i = 0
    let n = 0

    for (let vertex of shuffle([...puzzle.getGrid()])) {
        let number = puzzle.get(vertex)
        let boxes = puzzle.getBoxes(vertex)
        let feasible = boxes.some(box => box.filter(notEmpty).length === 9)

        if (!feasible) {
            // Check for multiple solutions

            feasible = true

            for (let n of shuffle(range(9))) {
                if (n + 1 === number) continue
                puzzle.set(vertex, n + 1)

                try {
                    feasible = puzzle.solve({timeout: 1000}) == null
                } catch (err) {
                    feasible = false
                }

                if (!feasible || Date.now() - startTime > timeout) {
                    feasible = false
                    break
                }
            }
        }

        puzzle.set(vertex, feasible ? null : number)

        if (feasible) i++
        if (i >= 81 - givens) break

        onProgress(++n / puzzle.getGrid().length)
    }

    puzzle.solids = puzzle.getGrid().filter(v => puzzle.get(v) != null)

    return puzzle
}

Sudoku.parse = function(string) {
    let arr = [...string]
    let arrangement = [...Array(9)].map((_, i) =>
        arr.slice(i * 9, (i + 1) * 9).map(x => x === '.' ? null : +x)
    )

    let puzzle = new Sudoku()
    puzzle.arrangement = arrangement
    puzzle.solids = puzzle.getGrid().filter(v => puzzle.get(v) != null)

    return puzzle
}

module.exports = Sudoku
<|endoftext|>


<commit_before>/* eslint-disable no-console */
(function () {
    'use strict';

    const analytics = require('@waves/event-sender');
    const ds = require('data-service');
    const WAVES_GATEWAY_ASSETS_ID = [
        WavesApp.defaultAssets.WEST,
        WavesApp.defaultAssets.ERGO,
        WavesApp.defaultAssets.BNT,
        WavesApp.defaultAssets.ETH
    ];

    /**
     * @param {typeof ConfirmTxService} ConfirmTxService
     * @param {$rootScope.Scope} $scope
     */
    const controller = function (ConfirmTxService, $scope) {

        class ConfirmGatewayTransaction extends ConfirmTxService {

            /**
             * @type {string}
             */
            targetRecipient = '';
            /**
             * @type {IGatewayDetails}
             */
            gatewayDetails = null;

            constructor(props) {
                super(props);
                this.observe(['gatewayDetails', 'tx'], () => {
                    if (!this.gatewayDetails || !this.tx) {
                        return null;
                    }

                    const fee = this.tx.amount.cloneWithTokens(this.gatewayDetails.gatewayFee);
                    this.tx.amount = this.tx.amount.sub(fee);
                });
            }

            getEventName() {
                return 'Gateway';
            }

            sendTransaction() {
                const signableAmountAsset = this.signable.getTxData().amount.asset;
                if (!WAVES_GATEWAY_ASSETS_ID.includes(signableAmountAsset.id)) {
                    return super.sendTransaction();
                }

                return this.signable.getDataForApi()
                    .then(data => {
                        return ds.fetch(`${WavesApp.network.wavesGateway}/api/v1/external/send`, {
                            method: 'POST',
                            body: WavesApp.stringifyJSON({
                                ...data,
                                sender: ds.app.address
                            })
                        });
                    })
                    .then(data => {
                        analytics.send({ name: `${signableAmountAsset.name.toUpperCase()} Transaction Success` });
                        return data;
                    }, (error) => {
                        analytics.send({ name: `${signableAmountAsset.name.toUpperCase()} Transaction Error` });
                        return Promise.reject(error);
                    });
            }

        }

        return new ConfirmGatewayTransaction($scope);
    };

    controller.$inject = ['ConfirmTxService', '$scope'];

    angular.module('app.ui').component('wConfirmGatewayTransaction', {
        bindings: {
            signable: '<',
            gatewayDetails: '<',
            targetRecipient: '<',
            onClickBack: '&'
        },
        templateUrl: 'modules/ui/directives/confirmGatewayTransaction/confirmGatewayTransaction.html',
        scope: false,
        transclude: false,
        controller
    });
})();
<commit_msg>DEXW-2298: add in send list<commit_after>/* eslint-disable no-console */
(function () {
    'use strict';

    const analytics = require('@waves/event-sender');
    const ds = require('data-service');
    const WAVES_GATEWAY_ASSETS_ID = [
        WavesApp.defaultAssets.WEST,
        WavesApp.defaultAssets.ERGO,
        WavesApp.defaultAssets.BNT,
        WavesApp.defaultAssets.ETH,
        WavesApp.defaultAssets.BTC
    ];

    /**
     * @param {typeof ConfirmTxService} ConfirmTxService
     * @param {$rootScope.Scope} $scope
     */
    const controller = function (ConfirmTxService, $scope) {

        class ConfirmGatewayTransaction extends ConfirmTxService {

            /**
             * @type {string}
             */
            targetRecipient = '';
            /**
             * @type {IGatewayDetails}
             */
            gatewayDetails = null;

            constructor(props) {
                super(props);
                this.observe(['gatewayDetails', 'tx'], () => {
                    if (!this.gatewayDetails || !this.tx) {
                        return null;
                    }

                    const fee = this.tx.amount.cloneWithTokens(this.gatewayDetails.gatewayFee);
                    this.tx.amount = this.tx.amount.sub(fee);
                });
            }

            getEventName() {
                return 'Gateway';
            }

            sendTransaction() {
                const signableAmountAsset = this.signable.getTxData().amount.asset;
                if (!WAVES_GATEWAY_ASSETS_ID.includes(signableAmountAsset.id)) {
                    return super.sendTransaction();
                }

                return this.signable.getDataForApi()
                    .then(data => {
                        return ds.fetch(`${WavesApp.network.wavesGateway}/api/v1/external/send`, {
                            method: 'POST',
                            body: WavesApp.stringifyJSON({
                                ...data,
                                sender: ds.app.address
                            })
                        });
                    })
                    .then(data => {
                        analytics.send({ name: `${signableAmountAsset.name.toUpperCase()} Transaction Success` });
                        return data;
                    }, (error) => {
                        analytics.send({ name: `${signableAmountAsset.name.toUpperCase()} Transaction Error` });
                        return Promise.reject(error);
                    });
            }

        }

        return new ConfirmGatewayTransaction($scope);
    };

    controller.$inject = ['ConfirmTxService', '$scope'];

    angular.module('app.ui').component('wConfirmGatewayTransaction', {
        bindings: {
            signable: '<',
            gatewayDetails: '<',
            targetRecipient: '<',
            onClickBack: '&'
        },
        templateUrl: 'modules/ui/directives/confirmGatewayTransaction/confirmGatewayTransaction.html',
        scope: false,
        transclude: false,
        controller
    });
})();
<|endoftext|>


<commit_before>
var express = require('express')
var path = require('path')
var cors = require('cors')
var bodyparser = require('body-parser')
var uuid = require('uuid')
var compression = require('compression')
var bcrypt = require('bcryptjs')

var knex = require('knex')({
  client: 'pg',
  connection: {
    username : 'josephquested',
    password : '',
    host     : '127.0.0.1',
    database : 'tooth_and_pail'
  }
})

module.exports = function routes(app) {

  var urlencodedParser = bodyparser.urlencoded({ extended: false })
  app.use(bodyparser.json())

  // GET
  app.get('/', function(req, res) {
    res.send('hello world')
    // .catch(function(err){
    //   console.log("ERROR! ", err)
    // })
})

  app.get('/recipients', function(req, res) {
    knex('recipients')
    .select('*')
    .then(function(resp) {
      res.send(resp)
    })
    // .catch(function(err){
    //   console.log("ERROR! ", err)
    // })
})

  app.get('/recipients/:recipientID', function(req, res) {
    // console.log("in GET for user", req.params.recipientID)
    knex('recipients')
    .where('recipients.recipientID', req.params.recipientID)
    .then(function(resp) {
      // console.log ("in get with recipientid", resp)
      res.send(resp[0])
    })
    // .catch(function(err){
    //   console.log("ERROR! ", err)
    // })
  })

  app.get('/donations', function(req, res) {
    // console.log("in GET all donations")
    knex('donations')
    .select('*')
    .then(function(resp) {
      res.send(resp)
    })
  })

  app.get('/donations/:donationID', function(req, res) {
    // console.log("in GET donations for a single donation", req.params.donationID)
    knex('donations')
    .where('donations.donationID', req.params.donationID)
    .then(function(resp) {
      res.send(resp[0])
    })
  })

  app.get('/donations/donor/:donorID', function(req, res) {
    // console.log("in GET donations for a single donor", req.params.donorID)
    knex('donations')
    .where('donations.donorID', req.params.donorID)
    .select('*')
    .then(function(resp) {
      res.send(resp)
    })
  })

  app.get('/donations/recipient/:recipientID', function(req, res) {
    // console.log("in GET donations for a single recipient", req.params.recipientID)
    knex('donations')
    .where('donations.recipientID', req.params.recipientID)
    .select('*')
    .then(function(resp) {
      res.send(resp)
    })
  })

  app.get('/donors', function(req, res) {
    // console.log("in GET all donors")
    knex('donors')
    .select('*')
    .then(function(resp) {
      res.send(resp)
    })
  })

  app.get('/donors/:donorID', function(req, res) {
    // console.log("in GET donors by donorID", req.params.donorID)
    knex('donors')
    .where('donors.donorID', req.params.donorID)
    .then(function(resp) {
      res.send(resp[0])
    })
  })
  app.get('/donors/name/:donorName', function(req, res) {
    // console.log("in GET donors by donorName", req.params.donorName)
    knex('donors')
    .where('donors.donorName', req.params.donorName)
    .then(function(resp) {
      res.send(resp[0])
    })
  })
  app.get('/donors/email/:email', function(req, res) {
    // console.log("in GET donors by email", req.params.email)
    knex('donors')
    .where('donors.email', req.params.email)
    .then(function(resp) {
      res.send(resp[0])
    })
    // .catch(function(err){
    //   console.log("ERROR! ", err)
    // })
})

  app.get('/ratings/:recipientID', function(req, res) {
    console.log("in GET ratings for a recipient", req.params.recipientID)
    knex('ratings')
    .where('ratings.recipientID', req.params.recipientID)
    .then(function(resp) {
      res.send(resp)
    })
  })

  // app.get('/ratings/:recipientID/:donorID', function(req, res) {
  //   console.log("in GET ratings for a recipient by donorid", req.params.recipientID, req.params.donorID)
  //   knex('ratings')
  //   .where({
  //     ratings.recipientID: req.params.recipientID,
  //     ratings.donorID:  req.params.donorID
  //   })
  //   .select('*')
  //   .then(function(resp) {
  //     console.log(resp)
  //     res.send(resp)
  //   })
  // })

  app.get('/ratings/:recipientID', function(req, res) {
    console.log("in GET ratings for a recipient", req.params.recipientID, req.params.donorID)
    knex('ratings')
    .where('ratings.recipientID', req.params.recipientID)
    .then(function(resp) {
      console.log(resp)
      res.send(resp)
    })
  })

  // ENCRYPTION

  app.post('/encrypt', function(req, res) {
    bcrypt.genSalt(10, function(err, salt) {
      if (err) { console.log("ERROR GENERATING SALT: ", err); return }
      bcrypt.hash(req.body.password, salt, (err, hash) => {
        if (err) { console.log("ERROR ENCRYPTING: ", err); return }
        res.send(hash)
    })
  })
})

  app.post('/unencrypt', function(req, res) {
    bcrypt.compare(req.body.password, req.body.passwordHash, function(err, resp) {
    if (resp === true) {
      console.log("password returned match TRUE")
      res.send(true)
    } else {
      console.log("password returned match FALSE")
      res.send(false)
    }
  })
})

  // POST

  app.post('/donors', function(req, res) {
    console.log("in POST to donors", req.body)
    var newId = uuid.v4()
    knex('donors')
    .insert({
      donorID: newId ,
      donorName: req.body.donorName,
      passwordHash: req.body.passwordHash,
      email: req.body.email
    })
    .then(function(resp) {
      res.send(newId)
    })
  })

  app.post('/donations', function(req, res) {
    console.log("in POST to donations", req.body)
    var newId = uuid.v4()
    knex('donations')
    .insert({
      donationID: newId,
      donorID: req.body.donorID,
      recipientID: req.body.recipientID,
      amount: req.body.amount
    })
    .then(function(resp) {
      res.send(resp)
    })
  })

  app.post('/recipients', function(req, res) {
    var newId = uuid.v4()
    knex('recipients')
      .insert({
        recipientID: newId ,
        name: req.body.Name,
        imgURL: req.body.imgURL,
        received: req.body.received,
        target: req.body.target,
        sobStory: req.body.sobStory,
        donorID: req.body.donorID
      })
      .then(function(resp) {
          res.send(resp)
      })
    })
    app.post('/ratings', function(req, res) {
      var newId = uuid.v4()
      knex('ratings')
        .insert({
          ratingID: newId ,
          recipientID: req.body.recipientID,
          donorID: req.body.donorID,
          rating: req.body.rating
        })
        .then(function(resp) {
            res.send(resp)
        })
      })

// PUT
    app.put('/recipients/:recipientID', function(req, res) {
      console.log("in dbroutes PUT recp")
      knex('recipients')
        .where('recipients.recipientID', req.params.recipientID)
        .update({
          name: req.body.Name,
          imgURL: req.body.imgURL,
          received: req.body.received,
          target: req.body.target,
          sobStory: req.body.sobStory,
          rating: req.body.rating,
          donorID: req.body.donorID
        })
        .then(function(resp) {
            res.send(resp)
        })
      })

      app.post('/ratings/:recipientID/:donorID', function(req, res) {
        console.log('in put to ratings')
        knex('ratings')
          .where({
              recipientID: req.params.recipientID,
              donorID:  req.params.donorID
            })
          .update({
            recipientID: req.body.recipientID,
            donorID: req.body.donorID,
            rating: req.body.rating
          })
          .then(function(resp) {
              res.send(resp)
          })
        })

}
<commit_msg>Add knex setup fix<commit_after>
var express = require('express')
var path = require('path')
var cors = require('cors')
var bodyparser = require('body-parser')
var uuid = require('uuid')
var compression = require('compression')
var bcrypt = require('bcryptjs')

var knex = require('knex')({
  client: 'pg',
  connection: {
    host     : '127.0.0.1',
    database : 'tooth_and_pail'
  }
})

module.exports = function routes(app) {
  var urlencodedParser = bodyparser.urlencoded({ extended: false })
  app.use(bodyparser.json())

  // GET REQUESTS //

  app.get('/', function(req, res) {
    res.send('hello teeth')
  })

  app.get('/recipients', function(req, res) {
    knex('recipients')
    .select('*')
    .then(function(resp) {
      res.send(resp)
    })
  })

  app.get('/recipients/:recipientID', function(req, res) {
    knex('recipients')
    .where('recipients.recipientID', req.params.recipientID)
    .then(function(resp) {
      res.send(resp[0])
    })
  })

  app.get('/donations', function(req, res) {
    knex('donations')
    .select('*')
    .then(function(resp) {
      res.send(resp)
    })
  })

  app.get('/donations/:donationID', function(req, res) {
    knex('donations')
    .where('donations.donationID', req.params.donationID)
    .then(function(resp) {
      res.send(resp[0])
    })
  })

  app.get('/donations/donor/:donorID', function(req, res) {
    knex('donations')
    .where('donations.donorID', req.params.donorID)
    .select('*')
    .then(function(resp) {
      res.send(resp)
    })
  })

  app.get('/donations/recipient/:recipientID', function(req, res) {
    knex('donations')
    .where('donations.recipientID', req.params.recipientID)
    .select('*')
    .then(function(resp) {
      res.send(resp)
    })
  })

  app.get('/donors', function(req, res) {
    knex('donors')
    .select('*')
    .then(function(resp) {
      res.send(resp)
    })
  })

  app.get('/donors/:donorID', function(req, res) {
    knex('donors')
    .where('donors.donorID', req.params.donorID)
    .then(function(resp) {
      res.send(resp[0])
    })
  })

  app.get('/donors/name/:donorName', function(req, res) {
    knex('donors')
    .where('donors.donorName', req.params.donorName)
    .then(function(resp) {
      res.send(resp[0])
    })
  })

  app.get('/donors/email/:email', function(req, res) {
    knex('donors')
    .where('donors.email', req.params.email)
    .then(function(resp) {
      res.send(resp[0])
    })
    // .catch(function(err){
    //   console.log("ERROR! ", err)
    // })
})

  app.get('/ratings/:recipientID', function(req, res) {
    console.log("in GET ratings for a recipient", req.params.recipientID)
    knex('ratings')
    .where('ratings.recipientID', req.params.recipientID)
    .then(function(resp) {
      res.send(resp)
    })
  })

  // app.get('/ratings/:recipientID/:donorID', function(req, res) {
  //   console.log("in GET ratings for a recipient by donorid", req.params.recipientID, req.params.donorID)
  //   knex('ratings')
  //   .where({
  //     ratings.recipientID: req.params.recipientID,
  //     ratings.donorID:  req.params.donorID
  //   })
  //   .select('*')
  //   .then(function(resp) {
  //     console.log(resp)
  //     res.send(resp)
  //   })
  // })

  app.get('/ratings/:recipientID', function(req, res) {
    console.log("in GET ratings for a recipient", req.params.recipientID, req.params.donorID)
    knex('ratings')
    .where('ratings.recipientID', req.params.recipientID)
    .then(function(resp) {
      console.log(resp)
      res.send(resp)
    })
  })

  // ENCRYPTION

  app.post('/encrypt', function(req, res) {
    bcrypt.genSalt(10, function(err, salt) {
      if (err) { console.log("ERROR GENERATING SALT: ", err); return }
      bcrypt.hash(req.body.password, salt, (err, hash) => {
        if (err) { console.log("ERROR ENCRYPTING: ", err); return }
        res.send(hash)
    })
  })
})

  app.post('/unencrypt', function(req, res) {
    bcrypt.compare(req.body.password, req.body.passwordHash, function(err, resp) {
    if (resp === true) {
      console.log("password returned match TRUE")
      res.send(true)
    } else {
      console.log("password returned match FALSE")
      res.send(false)
    }
  })
})

  // POST

  app.post('/donors', function(req, res) {
    console.log("in POST to donors", req.body)
    var newId = uuid.v4()
    knex('donors')
    .insert({
      donorID: newId ,
      donorName: req.body.donorName,
      passwordHash: req.body.passwordHash,
      email: req.body.email
    })
    .then(function(resp) {
      res.send(newId)
    })
  })

  app.post('/donations', function(req, res) {
    console.log("in POST to donations", req.body)
    var newId = uuid.v4()
    knex('donations')
    .insert({
      donationID: newId,
      donorID: req.body.donorID,
      recipientID: req.body.recipientID,
      amount: req.body.amount
    })
    .then(function(resp) {
      res.send(resp)
    })
  })

  app.post('/recipients', function(req, res) {
    var newId = uuid.v4()
    knex('recipients')
      .insert({
        recipientID: newId ,
        name: req.body.Name,
        imgURL: req.body.imgURL,
        received: req.body.received,
        target: req.body.target,
        sobStory: req.body.sobStory,
        donorID: req.body.donorID
      })
      .then(function(resp) {
          res.send(resp)
      })
    })
    app.post('/ratings', function(req, res) {
      var newId = uuid.v4()
      knex('ratings')
        .insert({
          ratingID: newId ,
          recipientID: req.body.recipientID,
          donorID: req.body.donorID,
          rating: req.body.rating
        })
        .then(function(resp) {
            res.send(resp)
        })
      })

// PUT
    app.put('/recipients/:recipientID', function(req, res) {
      console.log("in dbroutes PUT recp")
      knex('recipients')
        .where('recipients.recipientID', req.params.recipientID)
        .update({
          name: req.body.Name,
          imgURL: req.body.imgURL,
          received: req.body.received,
          target: req.body.target,
          sobStory: req.body.sobStory,
          rating: req.body.rating,
          donorID: req.body.donorID
        })
        .then(function(resp) {
            res.send(resp)
        })
      })

      app.post('/ratings/:recipientID/:donorID', function(req, res) {
        console.log('in put to ratings')
        knex('ratings')
          .where({
              recipientID: req.params.recipientID,
              donorID:  req.params.donorID
            })
          .update({
            recipientID: req.body.recipientID,
            donorID: req.body.donorID,
            rating: req.body.rating
          })
          .then(function(resp) {
              res.send(resp)
          })
        })

}
<|endoftext|>


<commit_before>'use strict';
var jsonSource = "https://sgdq-backend.firebaseio.com/.json"

var svg, brush, games, x2;

function adjustBrush(left, right, duration, clear){
  duration = duration || 1000;
  clear    = clear || false;
  d3.selectAll(".brush").transition()
    .duration(duration)
    .call(brush.extent([left,right]))
    .call(brush.event);
  // Clear after duration if necessary
  if(clear) setTimeout(function() {
    d3.selectAll(".brush").call(brush.clear()).call(brush);
  }, duration + 50);
}

function drawGraph(container){

  // Setup objects for d3 to render
  var margin = {top: 20, right: 75, bottom: 30, left: 50},
    width = $(container).width() - margin.left - margin.right,
    height = $(container).height() - 75 - margin.top - margin.bottom,
    margin2 = {top: height + 50, right: 10, bottom: 35, left: 0},
    height2 = $(container).height() - margin2.top - margin2.bottom;

  var x = d3.time.scale()
      .range([0, width]);

  x2 = d3.time.scale()
      .range([0, width]);

  var y0 = d3.scale.linear()
      .range([height, 0]);

  var y1 = d3.scale.linear()
      .range([height, 0]);

  var y3 = d3.scale.linear()
      .range([height2, 0])

  var xAxis = d3.svg.axis()
      .scale(x)
      .orient("bottom");

  var xAxis2 = d3.svg.axis()
      .scale(x2)
      .orient("bottom");

  var yAxis = d3.svg.axis()
      .scale(y0)
      .orient("left");

  var donationAxis = d3.svg.axis()
      .scale(y1)
      .orient("right")
      .tickFormat(function(d) { return '$' + d3.format(",.0f")(d) });

  var viewerLine = d3.svg.line()
      .x(function(d) { return x(d.date); })
      .y(function(d) { return y0(d.viewers); })
      .defined(function(d) { return d.date > 0 && d.viewers > 0 })
      .interpolate("monotone");

  var donationLine = d3.svg.line()
      .x(function(d) { return x(d.date); })
      .y(function(d) { return y1(d.donations); })
      .defined(function(d) { return d.date > 0 && d.donations > 0 })
      .interpolate("basis");

  var brushLine = d3.svg.line()
      .x(function(d) { return x2(d.date); })
      .y(function(d) { return y3(d.viewers); })
      .defined(function(d) { return d.date > 0 && d.viewers > 0 })
      .interpolate("basis");

  svg = d3.select(container).append("div")
      .append("svg")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom + height2 + margin2.bottom + margin2.top)
    .append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  var context = svg.append("g")
      .attr("class", "context")
      .attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");

  // Actually pull down JSON data and do the graph render
  d3.json(jsonSource, function(error, data) {
    if (error) {
      throw error;
    }
    games = data.games;
    data = data.data;
    var data_copy = [];
    var data_val;
    // Condition data
    for(var key in data) {
      // Ignore null viewer counts
      if(data[key].v < 0){
        continue;
      }
      data_val = {
        viewers: data[key].v,
        donators: data[key].d,
        donations: data[key].m,
        date: key
      };
      if(data_val.donations == undefined) {
        data_val.donations = -1;
      }
      data_copy.push(data_val);
    }

    // Condition games
    var games_arr = [];
    for(var key in games) {
      games_arr.push(games[key])
    }
    games = games_arr;

    var raw_data = data_copy;
    data = raw_data;

    function inDomainX(d) {
      return d.date < x.domain()[1].getTime() && d.date > x.domain()[0].getTime()
    }

    function numPointsInDomain(){
      var loBound = binarySearch(raw_data, {date: x.domain()[0]}, function(a, b){ return a.date - b.date });
      var hiBound = binarySearch(raw_data, {date: x.domain()[1]}, function(a, b){ return a.date - b.date });
      return [hiBound - loBound, loBound, hiBound];
    }

    function resample(){
      var res = numPointsInDomain()
      var dataPerPixel = res[0]/width;
      var lo = Math.max([res[1] - 5, 0]);
      return raw_data.slice(lo, res[2] + 5).filter(
        function(d, i) { return i % Math.ceil(dataPerPixel) == 0 && inDomainX(d); }
      );
    }

    function brushed() {
      x.domain(brush.empty() ? x2.domain() : brush.extent());
      data = resample();
      y0.domain(d3.extent(data, function(d) { return d.viewers; }));
      y1.domain(d3.extent(data, function(d) { return d.donations; }));
      svg.select(".line.viewerLine").datum(data).attr("d", viewerLine);
      svg.select(".line.donationLine").datum(data).attr("d", donationLine)
      svg.select(".x.axis.top").call(xAxis);
      svg.select(".y.axis.leftAxis").call(yAxis);
      svg.select(".y.axis.rightAxis").call(donationAxis);
      lineGroup.selectAll('.line').data(games)
        .attr("x1", function(d) { return x(d.start_time)} )
        .attr("x2", function(d) { return x(d.start_time)} )
    }

    brush = d3.svg.brush()
      .x(x2)
      .on("brush", brushed);

    x.domain(d3.extent(data, function(d) { return d.date; }));
    x2.domain(x.domain());
    y0.domain(d3.extent(data, function(d) { return d.viewers; }));
    y1.domain(d3.extent(data, function(d) { return d.donations; }));
    y3.domain(y0.domain());

    data = resample();

    svg.append("g")
        .attr("class", "x axis top")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);

    svg.append("g")
        .attr("class", "y axis leftAxis")
        .call(yAxis)
      .append("text")
        .attr("transform", "rotate(-90)")
        .attr("y", 6)
        .attr("dy", ".71em")
        .style("text-anchor", "end")
        .text("Viewers");

    svg.append("path")
        .datum(data)
        .attr("class", "line viewerLine")
        .attr("d", viewerLine);

    svg.append("path")
        .datum(data)
        .attr("class", "line donationLine")
        .attr("d", donationLine);

    svg.append("g")
        .attr("class", "y axis rightAxis")
        .attr("transform", "translate(" + width + " ,0)")
        .call(donationAxis)
      .append("text")
        .attr("transform", "rotate(-90)")
        .attr("y", -6)
        .attr("dy", ".6 0em")
        .style("text-anchor", "end")
        .text("Donations");

    var lineGroup = svg.append("g");

    lineGroup.selectAll('g').data(games).enter().append("line")
        .attr("class", "line gameLine")
        .attr("x1", function(d) { return x(d.start_time)} )
        .attr("x2", function(d) { return x(d.start_time)} )
        .attr("y1", height - 8)
        .attr("y2", height);

    context.append("path")
      .datum(data)
      .attr("class", "line contextLine")
      .attr("d", brushLine);

    context.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height2 + ")")
      .call(xAxis2);

    context.append("g")
      .attr("class", "x brush")
      .call(brush)
    .selectAll("rect")
      .attr("y", -6)
      .attr("height", height2 + 7);

    context.append("rect")
      .attr("class", "contextBound")
      .attr("width", width)
      .attr("height", height2 + 7)
      .attr("transform", "translate(0,-6)");

    svg.append("defs").append("clipPath")
        .attr("id", "clip")
      .append("rect")
        .attr("width", width)
        .attr("height", height + 10)
        .attr("transform", "translate(0,-5)");

    var focusLineG = svg.append('g')
      .attr('class', 'focusline');
    var focusLine = focusLineG.append('line')
      .style('display', 'none')
      .style('stroke', '#bbb');

    var viewerFocus = svg.append("g")
        .style("display", "none")
    viewerFocus.append("circle")
        .attr("r", 3)
        .attr("class", "viewerFocus");

    var donationFocus = svg.append("g")
        .style("display", "none");
    donationFocus.append("circle")
        .attr("r", 3)
        .attr("class", "donationFocus");

    svg.append("rect")
      .attr("class", "overlay")
      .attr("width", width)
      .attr("height", height)
      .on("mouseover", function() {
        viewerFocus.style("display", null);
        donationFocus.style("display", null);
        focusLine.style("display", null);
        tip.style("display", null);
      })
      .on("mouseout", function() {
        viewerFocus.style("display", "none");
        donationFocus.style("display", "none");
        focusLine.style("display", "none");
        tip.style("display", "none");
      })
      .on("mousemove", mousemove)
      .on("click", click);

    var tip = d3.select(container).append('div')
      .attr('class', 'tooltip');

    var bisectDate = d3.bisector(function(d) { return d.date; }).left;
    var bisectStarttime = d3.bisector(function(d) { return d.start_time; }).left;
    function mousemove() {
      var x0 = x.invert(d3.mouse(this)[0]),
          i = bisectDate(data, x0, 1),
          d0 = data[i - 1],
          d1 = data[i],
          d = d1 === undefined ?  d0 : (x0 - d0.date > d1.date - x0 ? d1 : d0);
      var gi = bisectStarttime(games, x0, 1),
          g = games[gi - 1];
      viewerFocus.attr("transform", "translate(" + x(d.date) + "," + y0(d.viewers) + ")");
      donationFocus.attr("transform", "translate(" + x(d.date) + "," + y1(d.donations) + ")");

      focusLine
        .attr('x1', x(d.date))
        .attr('x2', x(d.date))
        .attr('y1', 0)
        .attr('y2', height)
        .attr('display', null);

      tip.html("Date: " + (new Date(parseInt(d.date))).toString() + "
Viewers: " + d.viewers + "
Donations: " + d.donations
        + "
Game: " + g.title)
        .style("left", (d3.event.pageX + 20) + "px")
        .style("text-alight", "left")
        .style("top", (d3.event.pageY - 20) + "px");
    }
    function click() {
      var x0 = x.invert(d3.mouse(this)[0]);
      var gi = bisectStarttime(games, x0, 1);
      adjustToGame(gi - 1);
    }

    renderGames();
  });
}

function adjustToGame(i) {
  var left = games[i].start_time;
  var right = games[i+1].start_time;
  // Open up brush if it's empty
  if(brush.empty()) {
    adjustBrush(x2.domain()[0], x2.domain()[1], 0);
  }
  // Zoom out if already zoomed in
  else if (brush.extent()[0] == left && brush.extent()[1] == right){
    left = x2.domain()[0];
    right = x2.domain()[1];
    adjustBrush(left, right, 1000, true);
    return;
  }
  adjustBrush(left, right);
}

// Performance ++
function binarySearch(ar, el, compare_fn) {
    var m = 0;
    var n = ar.length - 1;
    while (m <= n) {
        var k = (n + m) >> 1;
        var cmp = compare_fn(el, ar[k]);
        if (cmp > 0) {
            m = k + 1;
        } else if(cmp < 0) {
            n = k - 1;
        } else {
            return k;
        }
    }
    return m + 1;
}

function renderGames(){
  var elm = $("
");
  elm.append("

Title	" +
    "Runner	" +
    "Duration	" +
    "

")
  for(var i in games){
    elm.append("
" +
      "" + games[i].title + "	" +
      "" + games[i].runner + "	" +
      "" + games[i].duration + "	" +
      "
")
  }
  elm.append("
")
  $("#game-list").append(elm);
  $('.gameSelector').on("click", function(e){
    adjustToGame(parseInt(e.toElement.id));
    $(this).addClass("selected").siblings().removeClass("selected");
  });
}

drawGraph("#chart");<commit_msg>Fixed last-game-not-zooming bug<commit_after>'use strict';
var jsonSource = "https://sgdq-backend.firebaseio.com/.json"

var svg, brush, games, x2, raw_data;

function adjustBrush(left, right, duration, clear){
  duration = duration || 1000;
  clear    = clear || false;
  d3.selectAll(".brush").transition()
    .duration(duration)
    .call(brush.extent([left,right]))
    .call(brush.event);
  // Clear after duration if necessary
  if(clear) setTimeout(function() {
    d3.selectAll(".brush").call(brush.clear()).call(brush);
  }, duration + 50);
}

function drawGraph(container){

  // Setup objects for d3 to render
  var margin = {top: 20, right: 75, bottom: 30, left: 50},
    width = $(container).width() - margin.left - margin.right,
    height = $(container).height() - 75 - margin.top - margin.bottom,
    margin2 = {top: height + 50, right: 10, bottom: 35, left: 0},
    height2 = $(container).height() - margin2.top - margin2.bottom;

  var x = d3.time.scale()
      .range([0, width]);

  x2 = d3.time.scale()
      .range([0, width]);

  var y0 = d3.scale.linear()
      .range([height, 0]);

  var y1 = d3.scale.linear()
      .range([height, 0]);

  var y3 = d3.scale.linear()
      .range([height2, 0])

  var xAxis = d3.svg.axis()
      .scale(x)
      .orient("bottom");

  var xAxis2 = d3.svg.axis()
      .scale(x2)
      .orient("bottom");

  var yAxis = d3.svg.axis()
      .scale(y0)
      .orient("left");

  var donationAxis = d3.svg.axis()
      .scale(y1)
      .orient("right")
      .tickFormat(function(d) { return '$' + d3.format(",.0f")(d) });

  var viewerLine = d3.svg.line()
      .x(function(d) { return x(d.date); })
      .y(function(d) { return y0(d.viewers); })
      .defined(function(d) { return d.date > 0 && d.viewers > 0 })
      .interpolate("monotone");

  var donationLine = d3.svg.line()
      .x(function(d) { return x(d.date); })
      .y(function(d) { return y1(d.donations); })
      .defined(function(d) { return d.date > 0 && d.donations > 0 })
      .interpolate("basis");

  var brushLine = d3.svg.line()
      .x(function(d) { return x2(d.date); })
      .y(function(d) { return y3(d.viewers); })
      .defined(function(d) { return d.date > 0 && d.viewers > 0 })
      .interpolate("basis");

  svg = d3.select(container).append("div")
      .append("svg")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom + height2 + margin2.bottom + margin2.top)
    .append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  var context = svg.append("g")
      .attr("class", "context")
      .attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");

  // Actually pull down JSON data and do the graph render
  d3.json(jsonSource, function(error, data) {
    if (error) {
      throw error;
    }
    games = data.games;
    data = data.data;
    var data_copy = [];
    var data_val;
    // Condition data
    for(var key in data) {
      // Ignore null viewer counts
      if(data[key].v < 0){
        continue;
      }
      data_val = {
        viewers: data[key].v,
        donators: data[key].d,
        donations: data[key].m,
        date: key
      };
      if(data_val.donations == undefined) {
        data_val.donations = -1;
      }
      data_copy.push(data_val);
    }

    // Condition games
    var games_arr = [];
    for(var key in games) {
      games_arr.push(games[key])
    }
    games = games_arr;

    raw_data = data_copy;
    data = raw_data;

    function inDomainX(d) {
      return d.date < x.domain()[1].getTime() && d.date > x.domain()[0].getTime()
    }

    function numPointsInDomain(){
      var loBound = binarySearch(raw_data, {date: x.domain()[0]}, function(a, b){ return a.date - b.date });
      var hiBound = binarySearch(raw_data, {date: x.domain()[1]}, function(a, b){ return a.date - b.date });
      return [hiBound - loBound, loBound, hiBound];
    }

    function resample(){
      var res = numPointsInDomain()
      var dataPerPixel = res[0]/width;
      var lo = Math.max([res[1] - 5, 0]);
      return raw_data.slice(lo, res[2] + 5).filter(
        function(d, i) { return i % Math.ceil(dataPerPixel) == 0 && inDomainX(d); }
      );
    }

    function brushed() {
      x.domain(brush.empty() ? x2.domain() : brush.extent());
      data = resample();
      y0.domain(d3.extent(data, function(d) { return d.viewers; }));
      y1.domain(d3.extent(data, function(d) { return d.donations; }));
      svg.select(".line.viewerLine").datum(data).attr("d", viewerLine);
      svg.select(".line.donationLine").datum(data).attr("d", donationLine)
      svg.select(".x.axis.top").call(xAxis);
      svg.select(".y.axis.leftAxis").call(yAxis);
      svg.select(".y.axis.rightAxis").call(donationAxis);
      lineGroup.selectAll('.line').data(games)
        .attr("x1", function(d) { return x(d.start_time)} )
        .attr("x2", function(d) { return x(d.start_time)} )
    }

    brush = d3.svg.brush()
      .x(x2)
      .on("brush", brushed);

    x.domain(d3.extent(data, function(d) { return d.date; }));
    x2.domain(x.domain());
    y0.domain(d3.extent(data, function(d) { return d.viewers; }));
    y1.domain(d3.extent(data, function(d) { return d.donations; }));
    y3.domain(y0.domain());

    data = resample();

    svg.append("g")
        .attr("class", "x axis top")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);

    svg.append("g")
        .attr("class", "y axis leftAxis")
        .call(yAxis)
      .append("text")
        .attr("transform", "rotate(-90)")
        .attr("y", 6)
        .attr("dy", ".71em")
        .style("text-anchor", "end")
        .text("Viewers");

    svg.append("path")
        .datum(data)
        .attr("class", "line viewerLine")
        .attr("d", viewerLine);

    svg.append("path")
        .datum(data)
        .attr("class", "line donationLine")
        .attr("d", donationLine);

    svg.append("g")
        .attr("class", "y axis rightAxis")
        .attr("transform", "translate(" + width + " ,0)")
        .call(donationAxis)
      .append("text")
        .attr("transform", "rotate(-90)")
        .attr("y", -6)
        .attr("dy", ".6 0em")
        .style("text-anchor", "end")
        .text("Donations");

    var lineGroup = svg.append("g");

    lineGroup.selectAll('g').data(games).enter().append("line")
        .attr("class", "line gameLine")
        .attr("x1", function(d) { return x(d.start_time)} )
        .attr("x2", function(d) { return x(d.start_time)} )
        .attr("y1", height - 8)
        .attr("y2", height);

    context.append("path")
      .datum(data)
      .attr("class", "line contextLine")
      .attr("d", brushLine);

    context.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height2 + ")")
      .call(xAxis2);

    context.append("g")
      .attr("class", "x brush")
      .call(brush)
    .selectAll("rect")
      .attr("y", -6)
      .attr("height", height2 + 7);

    context.append("rect")
      .attr("class", "contextBound")
      .attr("width", width)
      .attr("height", height2 + 7)
      .attr("transform", "translate(0,-6)");

    svg.append("defs").append("clipPath")
        .attr("id", "clip")
      .append("rect")
        .attr("width", width)
        .attr("height", height + 10)
        .attr("transform", "translate(0,-5)");

    var focusLineG = svg.append('g')
      .attr('class', 'focusline');
    var focusLine = focusLineG.append('line')
      .style('display', 'none')
      .style('stroke', '#bbb');

    var viewerFocus = svg.append("g")
        .style("display", "none")
    viewerFocus.append("circle")
        .attr("r", 3)
        .attr("class", "viewerFocus");

    var donationFocus = svg.append("g")
        .style("display", "none");
    donationFocus.append("circle")
        .attr("r", 3)
        .attr("class", "donationFocus");

    svg.append("rect")
      .attr("class", "overlay")
      .attr("width", width)
      .attr("height", height)
      .on("mouseover", function() {
        viewerFocus.style("display", null);
        donationFocus.style("display", null);
        focusLine.style("display", null);
        tip.style("display", null);
      })
      .on("mouseout", function() {
        viewerFocus.style("display", "none");
        donationFocus.style("display", "none");
        focusLine.style("display", "none");
        tip.style("display", "none");
      })
      .on("mousemove", mousemove)
      .on("click", click);

    var tip = d3.select(container).append('div')
      .attr('class', 'tooltip');

    var bisectDate = d3.bisector(function(d) { return d.date; }).left;
    var bisectStarttime = d3.bisector(function(d) { return d.start_time; }).left;
    function mousemove() {
      var x0 = x.invert(d3.mouse(this)[0]),
          i = bisectDate(data, x0, 1),
          d0 = data[i - 1],
          d1 = data[i],
          d = d1 === undefined ?  d0 : (x0 - d0.date > d1.date - x0 ? d1 : d0);
      var gi = bisectStarttime(games, x0, 1),
          g = games[gi - 1];
      viewerFocus.attr("transform", "translate(" + x(d.date) + "," + y0(d.viewers) + ")");
      donationFocus.attr("transform", "translate(" + x(d.date) + "," + y1(d.donations) + ")");

      focusLine
        .attr('x1', x(d.date))
        .attr('x2', x(d.date))
        .attr('y1', 0)
        .attr('y2', height)
        .attr('display', null);

      tip.html("Date: " + (new Date(parseInt(d.date))).toString() + "
Viewers: " + d.viewers + "
Donations: " + d.donations
        + "
Game: " + g.title)
        .style("left", (d3.event.pageX + 20) + "px")
        .style("text-alight", "left")
        .style("top", (d3.event.pageY - 20) + "px");
    }
    function click() {
      var x0 = x.invert(d3.mouse(this)[0]);
      var gi = bisectStarttime(games, x0, 1);
      adjustToGame(gi - 1);
    }

    renderGames();
  });
}

function adjustToGame(i) {
  var left = games[i].start_time;
  // Bail if game hasn't started yet
  if(left > raw_data[raw_data.length - 1].date) return;

  // Set 'end' time to last data point if we are zooming to last game in list
  var right = (games.length < i && games[i+1].start_time <= raw_data[raw_data.length - 1].date) ? games[i+1].start_time : raw_data[raw_data.length - 1].date;

  // Open up brush if it's empty
  if(brush.empty()) {
    adjustBrush(x2.domain()[0], x2.domain()[1], 0);
  }
  // Zoom out if already zoomed in
  else if (brush.extent()[0] == left && brush.extent()[1] == right){
    left = x2.domain()[0];
    right = x2.domain()[1];
    adjustBrush(left, right, 1000, true);
    return;
  }
  adjustBrush(left, right);
}

// Performance ++
function binarySearch(ar, el, compare_fn) {
    var m = 0;
    var n = ar.length - 1;
    while (m <= n) {
        var k = (n + m) >> 1;
        var cmp = compare_fn(el, ar[k]);
        if (cmp > 0) {
            m = k + 1;
        } else if(cmp < 0) {
            n = k - 1;
        } else {
            return k;
        }
    }
    return m + 1;
}

function renderGames(){
  var elm = $("
");
  elm.append("

Title	" +
    "Runner	" +
    "Duration	" +
    "

")
  for(var i in games){
    elm.append("
" +
      "" + games[i].title + "	" +
      "" + games[i].runner + "	" +
      "" + games[i].duration + "	" +
      "
")
  }
  elm.append("
")
  $("#game-list").append(elm);
  $('.gameSelector').on("click", function(e){
    adjustToGame(parseInt(e.toElement.id));
    $(this).addClass("selected").siblings().removeClass("selected");
  });
}

drawGraph("#chart");<|endoftext|>