<commit_before>'use strict';

process.on('unhandledRejection', function (e) {
    throw e;
});

const bluebird = require('bluebird');
const co = require('co');
const fs = bluebird.promisifyAll(require('fs'));
const MongoClient = require('mongodb').MongoClient;
const path = require('path');
const sdfParser = require('sdf-parser');
const zlib = require('zlib');

const config = require('./config.json');

const getMolecule = require('./molecule').getMolecule;

const dataDir = path.join(config.data, 'Weekly');
const kHalfStringMaxLength = 268435440 / 2;

let db;
co(function*() {
    db = yield MongoClient.connect(config.mongodb);
    console.log('connected to MongoDB');

    const adminCollection = getCollection(db, 'admin');
    const dataCollection = getCollection(db, 'data');

    let progress = yield adminCollection.find({_id: 'main_progress'}).next();

    const lastDate = progress.date;
    const weeklyDirs = yield fs.readdirAsync(dataDir);

    for (const week of weeklyDirs) {
        const weekDate = new Date(week);
        if (weekDate < lastDate) continue;
        console.log(`treating directory ${week}`);
        const weekDir = path.join(dataDir, week);

        // remove killed compounds
        let killed;
        try {
            const killedFile = yield fs.readFileAsync(path.join(weekDir, 'killed-CIDs'), 'ascii');
            killed = killedFile.split(/\r|\n|\r\n/).map(Number);
        } catch (e) {
            if (e.code !== 'ENOENT') throw e;
        }
        if (killed) {
            console.log(`removing ${killed.length} killed IDs`);
            for (const killedID of killed) {
                yield dataCollection.deleteOne({_id: killedID});
            }
        }

        // insert new or updated compounds
        const sdfDir = path.join(weekDir, 'SDF');
        const sdfList = yield fs.readdirAsync(sdfDir);
        for (const sdfFile of sdfList) {
            if (!sdfFile.endsWith('.sdf.gz')) continue;
            const sdfPath = path.join(sdfDir, sdfFile);
            console.log(`treating file ${sdfFile}`);
            const gzValue = yield fs.readFileAsync(sdfPath);
            const bufferValue = zlib.gunzipSync(gzValue);
            let n = 0, nextIndex = 0;
            while (n < bufferValue.length) {
                nextIndex = bufferValue.indexOf('$$$$', n + kHalfStringMaxLength);
                if (nextIndex === -1) nextIndex = bufferValue.length;
                const strValue = bufferValue.slice(n, nextIndex).toString();
                const molecules = sdfParser(strValue).molecules;
                for (let j = 0; j < molecules.length; j++) {
                    const molecule = molecules[j];
                    const result = getMolecule(molecule);
                    result.seq = ++progress.seq;
                    yield dataCollection.updateOne({_id: result._id}, result, {upsert: true});
                    yield adminCollection.updateOne({_id: progress._id}, progress);
                }
                n = nextIndex;
            }
        }

        progress.date = weekDate;
        yield adminCollection.updateOne({_id: progress._id}, progress);
    }
}).catch(function (e) {
    console.log('error');
    console.error(e);
}).then(function () {
    console.log('closing DB');
    if (db) db.close();
});

function getCollection(db, name) {
    return db.collection(name);
}
<commit_msg>fix split regex<commit_after>'use strict';

process.on('unhandledRejection', function (e) {
    throw e;
});

const bluebird = require('bluebird');
const co = require('co');
const fs = bluebird.promisifyAll(require('fs'));
const MongoClient = require('mongodb').MongoClient;
const path = require('path');
const sdfParser = require('sdf-parser');
const zlib = require('zlib');

const config = require('./config.json');

const getMolecule = require('./molecule').getMolecule;

const dataDir = path.join(config.data, 'Weekly');
const kHalfStringMaxLength = 268435440 / 2;

let db;
co(function*() {
    db = yield MongoClient.connect(config.mongodb);
    console.log('connected to MongoDB');

    const adminCollection = getCollection(db, 'admin');
    const dataCollection = getCollection(db, 'data');

    let progress = yield adminCollection.find({_id: 'main_progress'}).next();

    const lastDate = progress.date;
    const weeklyDirs = yield fs.readdirAsync(dataDir);

    for (const week of weeklyDirs) {
        const weekDate = new Date(week);
        if (weekDate < lastDate) continue;
        console.log(`treating directory ${week}`);
        const weekDir = path.join(dataDir, week);

        // remove killed compounds
        let killed;
        try {
            const killedFile = yield fs.readFileAsync(path.join(weekDir, 'killed-CIDs'), 'ascii');
            killed = killedFile.split(/\r\n|\r|\n/).map(Number);
        } catch (e) {
            if (e.code !== 'ENOENT') throw e;
        }
        if (killed) {
            console.log(`removing ${killed.length} killed IDs`);
            for (const killedID of killed) {
                yield dataCollection.deleteOne({_id: killedID});
            }
        }

        // insert new or updated compounds
        const sdfDir = path.join(weekDir, 'SDF');
        const sdfList = yield fs.readdirAsync(sdfDir);
        for (const sdfFile of sdfList) {
            if (!sdfFile.endsWith('.sdf.gz')) continue;
            const sdfPath = path.join(sdfDir, sdfFile);
            console.log(`treating file ${sdfFile}`);
            const gzValue = yield fs.readFileAsync(sdfPath);
            const bufferValue = zlib.gunzipSync(gzValue);
            let n = 0, nextIndex = 0;
            while (n < bufferValue.length) {
                nextIndex = bufferValue.indexOf('$$$$', n + kHalfStringMaxLength);
                if (nextIndex === -1) nextIndex = bufferValue.length;
                const strValue = bufferValue.slice(n, nextIndex).toString();
                const molecules = sdfParser(strValue).molecules;
                for (let j = 0; j < molecules.length; j++) {
                    const molecule = molecules[j];
                    const result = getMolecule(molecule);
                    result.seq = ++progress.seq;
                    yield dataCollection.updateOne({_id: result._id}, result, {upsert: true});
                    yield adminCollection.updateOne({_id: progress._id}, progress);
                }
                n = nextIndex;
            }
        }

        progress.date = weekDate;
        yield adminCollection.updateOne({_id: progress._id}, progress);
    }
}).catch(function (e) {
    console.log('error');
    console.error(e);
}).then(function () {
    console.log('closing DB');
    if (db) db.close();
});

function getCollection(db, name) {
    return db.collection(name);
}
<|endoftext|>


<commit_before>var field_radius = 180;
var width = field_radius * 2,
    height = field_radius * 2;

var num_players = 3,
    player_radius = 0.04,  // 4cm
    frame_step = 1 / 60;  // s

var x = d3.scale.linear()
        .domain([-1, 1]).range([0, width]),
    y = d3.scale.linear()
        .domain([-1, 1]).range([height, 0]),
    r = d3.scale.linear()
        .domain([0, 1]).range([0, field_radius]),
    ox = d3.scale.linear()
        .domain([-1, 1]).range([-field_radius, field_radius]),
    oy = d3.scale.linear()
        .domain([-1, 1]).range([-field_radius, field_radius]);

var area = d3.select('body').append('svg')
    .attr('width', width)
    .attr('height', height);

var field = area.append('g').attr('class', 'field').selectAll('circle')
        .data(d3.range(1, 0, -0.1))
    .enter().append('circle')
        .attr('cx', x(0))
        .attr('cy', y(0))
        .attr('r', r)
        .attr('class', 'field-ring');
        // .style('opacity', function(d, i) { return i / 10.0; });

var players_groups = area.selectAll('.player')
        .data(d3.range(num_players))
    .enter().append('g')
        .attr('class', 'player');
players_groups.append('line').attr('x1', 0).attr('y1', 0);
players_groups.append('circle').attr('r', r(player_radius));

var vec = {
    sum: function(v1, v2) {
        return [v1[0] + v2[0], v1[1] + v2[1]];
    },
    sub: function(v1, v2) {
        return [v1[0] - v2[0], v1[1] - v2[1]];
    },
    scale: function(v, scale) {
        return [v[0] * scale, v[1] * scale];
    },
    dot: function(v1, v2) {
        return (v1[0] * v2[0]) + (v1[1] * v2[1]);
    },
    cross: function(v1, v2) {
        return (v1[0] * v2[1]) - (v2[0] * v1[1])
    },
    len: function(v) {
        return Math.sqrt(Math.pow(v[0], 2) + Math.pow(v[1], 2));
    },
    // ang: function(v) {
    //     return 0;
    // },
};

var strategy_wrapper = function(strategy) {
    var wrapped = function(pos, vel, mass) {
        dist = vec.len(pos);
        in_speed = -vec.dot(vel, pos);
        tan_speed = -vec.cross(vel, pos);

        p_force = strategy(dist, in_speed, tan_speed);

        pos_norm = vec.scale(pos, 1.0 / vec.len(pos));
        tan_norm = [-pos_norm[1], pos_norm[0]];
        c_force = vec.scale(pos_norm, -p_force[0]);
        t_force = vec.scale(tan_norm, p_force[1]);
        force = vec.sum(c_force, t_force);
        accel = vec.scale(force, 1.0 / mass)
        return accel;
    };
    return wrapped;
};

var player_next = function(player) {
    // runge kutta

    pos1 = player.pos;
    vel1 = player.vel;
    accel1 = player.strategy(pos1, vel1, player.mass);

    pos2 = vec.sum(player.pos, vec.scale(vel1, frame_step / 2));
    vel2 = vec.sum(player.vel, vec.scale(accel1, frame_step / 2));
    accel2 = player.strategy(pos2, vel2, player.mass);

    pos3 = vec.sum(player.pos, vec.scale(vel2, frame_step / 2));
    vel3 = vec.sum(player.vel, vec.scale(accel2, frame_step / 2));
    accel3 = player.strategy(pos3, vel3, player.mass);

    pos4 = vec.sum(player.pos, vec.scale(vel3, frame_step));
    vel4 = vec.sum(player.vel, vec.scale(accel3, frame_step));
    accel4 = player.strategy(pos4, vel4, player.mass);

    rk_vel = vec.scale(vec.sum(vec.sum(vel1, vel4),
                       vec.scale(vec.sum(vel2, vel3), 2)), 1 / 6.0);
    rk_accel = vec.scale(vec.sum(vec.sum(accel1, accel4),
                         vec.scale(vec.sum(accel2, accel3), 2)), 1 / 6.0);

    player.pos = vec.sum(player.pos, vec.scale(rk_vel, frame_step));
    player.vel = vec.sum(player.vel, vec.scale(rk_accel, frame_step));
    player.accel = rk_accel;
};

var p1strategy = function(dist, in_speed, tan_speed) {
    return [0.5, 0];
}

var p2strategy = function(dist, in_speed, tan_speed) {
    return [dist * 3, 0];
}

var p3strategy = function(dist, in_speed, tan_speed) {
    return [Math.pow(dist, 2)*6, 0];
}

var collide = function(p1, p2, diff_vect) {
    normal = vec.scale(diff_vect, 1.0 / vec.len(diff_vect));
    tangential = [-normal[1], normal[0]];

    p1_norm = vec.dot(p1.vel, normal);
    p2_norm = vec.dot(p2.vel, normal);
    p1_tang = vec.scale(tangential, vec.dot(p1.vel, tangential));
    p2_tang = vec.scale(tangential, vec.dot(p2.vel, tangential));

    p1_post = (p1_norm * (p1.mass - p2.mass) + 2 * p2_norm * p2.mass) / (p1.mass + p2.mass);
    p2_post = (p2_norm * (p2.mass - p1.mass) + 2 * p1_norm * p1.mass) / (p2.mass + p1.mass);

    p1_post_norm = vec.scale(normal, p1_post);
    p2_post_norm = vec.scale(normal, p2_post);

    p1.vel = vec.sum(p1_post_norm, p1_tang);
    p2.vel = vec.sum(p2_post_norm, p2_tang);
}

var collisions = function(players) {
    for (p1_n = 0; p1_n < (players.length - 1); p1_n++) {
        p1 = players[p1_n];
        for (p2_n = (p1_n + 1); p2_n < players.length; p2_n++) {
            p2 = players[p2_n];
            diff = vec.sub(p1.pos, p2.pos);
            offset = vec.len(diff)
            if (offset < (player_radius * 2)) {
                collide(p1, p2, diff);
            }
        }
    }
}

var game = {
    frame_count: 0,
    players: [
        {
            pos: [0.667, 0],
            vel: [0, 0.1],
            accel: [0, 0],
            mass: 4,  // Kg
            strategy: strategy_wrapper(p1strategy),
        },
        {
            pos: [-0.667, 0],
            vel: [0, 0.02],
            accel: [0, 0],
            mass: 4,
            strategy: strategy_wrapper(p2strategy),
        },
        {
            pos: [0, 0.667],
            vel: [0, 0.02],
            accel: [0, 0],
            mass: 4,
            strategy: strategy_wrapper(p3strategy),
        }
    ]
}
game.col
game.update = function() {
    game.players.forEach(player_next);
    collisions(game.players);
    players_groups.data(game.players)
        .attr('transform', function(d) {
            return "translate(" + [x(d.pos[0]), y(d.pos[1])] + ")"; })
        .select('line').data(game.players)
            .attr('x2', function(d) { return ox(-d.accel[0]); })
            .attr('y2', function(d) { return oy(d.accel[1]); });
},

window.setInterval(game.update, frame_step * 1000);
<commit_msg>deadly boundaries<commit_after>var field_radius = 180;
var width = field_radius * 2,
    height = field_radius * 2;

var num_players = 3,
    player_radius = 0.04,  // 4cm
    frame_step = 1 / 60;  // s

var x = d3.scale.linear()
        .domain([-1, 1]).range([0, width]),
    y = d3.scale.linear()
        .domain([-1, 1]).range([height, 0]),
    r = d3.scale.linear()
        .domain([0, 1]).range([0, field_radius]),
    ox = d3.scale.linear()
        .domain([-1, 1]).range([-field_radius, field_radius]),
    oy = d3.scale.linear()
        .domain([-1, 1]).range([-field_radius, field_radius]);

var area = d3.select('body').append('svg')
    .attr('width', width)
    .attr('height', height);

var field = area.append('g').attr('class', 'field').selectAll('circle')
        .data(d3.range(1, 0, -0.1))
    .enter().append('circle')
        .attr('cx', x(0))
        .attr('cy', y(0))
        .attr('r', r)
        .attr('class', 'field-ring');
        // .style('opacity', function(d, i) { return i / 10.0; });

var players_groups = area.selectAll('.player')
        .data(d3.range(num_players))
    .enter().append('g')
        .attr('class', 'player');
players_groups.append('line').attr('x1', 0).attr('y1', 0);
players_groups.append('circle').attr('r', r(player_radius));

var vec = {
    sum: function(v1, v2) {
        return [v1[0] + v2[0], v1[1] + v2[1]];
    },
    sub: function(v1, v2) {
        return [v1[0] - v2[0], v1[1] - v2[1]];
    },
    scale: function(v, scale) {
        return [v[0] * scale, v[1] * scale];
    },
    dot: function(v1, v2) {
        return (v1[0] * v2[0]) + (v1[1] * v2[1]);
    },
    cross: function(v1, v2) {
        return (v1[0] * v2[1]) - (v2[0] * v1[1])
    },
    len: function(v) {
        return Math.sqrt(Math.pow(v[0], 2) + Math.pow(v[1], 2));
    },
};

var strategy_wrapper = function(strategy) {
    var wrapped = function(pos, vel, mass) {
        dist = vec.len(pos);
        in_speed = -vec.dot(vel, pos);
        tan_speed = -vec.cross(vel, pos);

        p_force = strategy(dist, in_speed, tan_speed);

        pos_norm = vec.scale(pos, 1.0 / vec.len(pos));
        tan_norm = [-pos_norm[1], pos_norm[0]];
        c_force = vec.scale(pos_norm, -p_force[0]);
        t_force = vec.scale(tan_norm, p_force[1]);
        force = vec.sum(c_force, t_force);
        accel = vec.scale(force, 1.0 / mass)
        return accel;
    };
    return wrapped;
};

var player_next = function(player) {
    // runge kutta

    pos1 = player.pos;
    vel1 = player.vel;
    accel1 = player.strategy(pos1, vel1, player.mass);

    pos2 = vec.sum(player.pos, vec.scale(vel1, frame_step / 2));
    vel2 = vec.sum(player.vel, vec.scale(accel1, frame_step / 2));
    accel2 = player.strategy(pos2, vel2, player.mass);

    pos3 = vec.sum(player.pos, vec.scale(vel2, frame_step / 2));
    vel3 = vec.sum(player.vel, vec.scale(accel2, frame_step / 2));
    accel3 = player.strategy(pos3, vel3, player.mass);

    pos4 = vec.sum(player.pos, vec.scale(vel3, frame_step));
    vel4 = vec.sum(player.vel, vec.scale(accel3, frame_step));
    accel4 = player.strategy(pos4, vel4, player.mass);

    rk_vel = vec.scale(vec.sum(vec.sum(vel1, vel4),
                       vec.scale(vec.sum(vel2, vel3), 2)), 1 / 6.0);
    rk_accel = vec.scale(vec.sum(vec.sum(accel1, accel4),
                         vec.scale(vec.sum(accel2, accel3), 2)), 1 / 6.0);

    player.pos = vec.sum(player.pos, vec.scale(rk_vel, frame_step));
    player.vel = vec.sum(player.vel, vec.scale(rk_accel, frame_step));
    player.accel = rk_accel;
};

var p1strategy = function(dist, in_speed, tan_speed) {
    return [0.7, 0];
}

var p2strategy = function(dist, in_speed, tan_speed) {
    return [dist + 0.3, 0.02];
}

var p3strategy = function(dist, in_speed, tan_speed) {
    return [Math.pow(dist, 2) + 0.5, -0.02];
}

var collide = function(p1, p2, diff_vect) {
    normal = vec.scale(diff_vect, 1.0 / vec.len(diff_vect));
    tangential = [-normal[1], normal[0]];

    p1_norm = vec.dot(p1.vel, normal);
    p2_norm = vec.dot(p2.vel, normal);
    p1_tang = vec.scale(tangential, vec.dot(p1.vel, tangential));
    p2_tang = vec.scale(tangential, vec.dot(p2.vel, tangential));

    p1_post = (p1_norm * (p1.mass - p2.mass) + 2 * p2_norm * p2.mass) / (p1.mass + p2.mass);
    p2_post = (p2_norm * (p2.mass - p1.mass) + 2 * p1_norm * p1.mass) / (p2.mass + p1.mass);

    p1_post_norm = vec.scale(normal, p1_post);
    p2_post_norm = vec.scale(normal, p2_post);

    p1.vel = vec.sum(p1_post_norm, p1_tang);
    p2.vel = vec.sum(p2_post_norm, p2_tang);
}

var collisions = function(players) {
    for (p1_n = 0; p1_n < (players.length - 1); p1_n++) {
        p1 = players[p1_n];
        for (p2_n = (p1_n + 1); p2_n < players.length; p2_n++) {
            p2 = players[p2_n];
            diff = vec.sub(p1.pos, p2.pos);
            offset = vec.len(diff)
            if (offset < (player_radius * 2)) {
                collide(p1, p2, diff);
            }
        }
    }
}

var boundaries = function(players) {
    for (n in players) {
        p = players[n];
        if (r(vec.len(p.pos)) > field_radius) {
            players.splice(n, 1);
        }
    }
}

var game = {
    frame_count: 0,
    players: [
        {
            pos: [0.7, 0.05],
            vel: [-0.09, 0.06],
            accel: [0, 0],
            mass: 4,  // Kg
            strategy: strategy_wrapper(p1strategy),
        },
        {
            pos: [-0.5, -0.1],
            vel: [-0.06, 0.15],
            accel: [0, 0],
            mass: 4,
            strategy: strategy_wrapper(p2strategy),
        },
        {
            pos: [0, 0.667],
            vel: [0.12, 0.05],
            accel: [0, 0],
            mass: 4,
            strategy: strategy_wrapper(p3strategy),
        }
    ]
}
game.col
game.update = function() {
    game.players.forEach(player_next);
    collisions(game.players);
    boundaries(game.players);
    groups = players_groups.data(game.players);
    groups.exit().remove();
    groups.attr('transform', function(d) {
            return "translate(" + [x(d.pos[0]), y(d.pos[1])] + ")"; })
        .select('line').data(game.players)
            .attr('x2', function(d) { return ox(-d.accel[0]); })
            .attr('y2', function(d) { return oy(d.accel[1]); });
},

window.setInterval(game.update, frame_step * 1000);
<|endoftext|>


<commit_before>var bindings = require('bindings')('bcrypt_lib');
var crypto = require('crypto');

/// generate a salt (sync)
/// @param {Number} [rounds] number of rounds (default 10)
/// @return {String} salt
module.exports.genSaltSync = function(rounds) {
    // default 10 rounds
    if (!rounds) {
        rounds = 10;
    } else if (typeof rounds !== 'number') {
        throw new Error('rounds must be a number');
    }

    return bindings.gen_salt_sync(rounds, crypto.randomBytes(16));
};

/// generate a salt
/// @param {Number} [rounds] number of rounds (default 10)
/// @param {Function} cb callback(err, salt)
module.exports.genSalt = function(rounds, ignore, cb) {
    // if callback is first argument, then use defaults for others
    if (typeof arguments[0] === 'function') {
        // have to set callback first otherwise arguments are overriden
        cb = arguments[0];
        rounds = 10;
    // callback is second argument
    } else if (typeof arguments[1] === 'function') {
        // have to set callback first otherwise arguments are overriden
        cb = arguments[1];
    }

    // default 10 rounds
    if (!rounds) {
        rounds = 10;
    } else if (typeof rounds !== 'number') {
        return cb(new Error('rounds must be a number'));
    }

    if (!cb) {
        return;
    }

    return bindings.gen_salt(rounds, crypto.randomBytes(16), cb);
};

/// hash data using a salt
/// @param {String} data the data to encrypt
/// @param {String} salt the salt to use when hashing
/// @return {String} hash
module.exports.hashSync = function(data, salt) {
    if (data == null || data == undefined || salt == null || salt == undefined) {
        throw new Error('data and salt arguments required');
    } else if (typeof data !== 'string' && (typeof salt !== 'string' || typeof salt !== 'number')) {
        throw new Error('data must be a string and salt must either be a salt string or a number of rounds');
    }

    if (typeof salt === 'number') {
      salt = module.exports.genSaltSync(salt);
    }

    return bindings.encrypt_sync(data, salt);
};

/// hash data using a salt
/// @param {String} data the data to encrypt
/// @param {String} salt the salt to use when hashing
/// @param {Function} cb callback(err, hash)
module.exports.hash = function(data, salt, cb) {
    if (typeof data === 'function') {
        return data(new Error('data must be a string and salt must either be a salt string or a number of rounds'));
    }
    if (typeof salt === 'function') {
        return salt(new Error('data must be a string and salt must either be a salt string or a number of rounds'));
    }
    if (data == null || data == undefined || salt == null || salt == undefined) {
        return cb(new Error('data and salt arguments required'));
    } else if (typeof data !== 'string' && (typeof salt !== 'string' || typeof salt !== 'number')) {
        return cb(new Error('data must be a string and salt must either be a salt string or a number of rounds'));
    }

    if (!cb || typeof cb !== 'function') {
        return;
    }

    if (typeof salt === 'number') {
      return module.exports.genSalt(salt, function(err, salt) {
        return bindings.encrypt(data, salt, cb);
      });
    }

    return bindings.encrypt(data, salt, cb);
};

/// compare raw data to hash
/// @param {String} data the data to hash and compare
/// @param {String} hash expected hash
/// @return {bool} true if hashed data matches hash
module.exports.compareSync = function(data, hash) {
    if (data == null || data == undefined || hash == null || hash == undefined) {
        throw new Error('data and hash arguments required');
    } else if (typeof data !== 'string' || typeof hash !== 'string') {
        throw new Error('data and hash must be strings');
    }

    return bindings.compare_sync(data, hash);
};

/// compare raw data to hash
/// @param {String} data the data to hash and compare
/// @param {String} hash expected hash
/// @param {Function} cb callback(err, matched) - matched is true if hashed data matches hash
module.exports.compare = function(data, hash, cb) {
    if (data == null || data == undefined || hash == null || hash == undefined) {
        return cb(new Error('data and hash arguments required'));
    } else if (typeof data !== 'string' || typeof hash !== 'string') {
        return cb(new Error('data and hash must be strings'));
    }

    if (!cb || typeof cb !== 'function') {
        return;
    }

    return bindings.compare(data, hash, cb);
};

/// @param {String} hash extract rounds from this hash
/// @return {Number} the number of rounds used to encrypt a given hash
module.exports.getRounds = function(hash) {
    if (hash == null || hash == undefined) {
        throw new Error('hash argument required');
    } else if (typeof hash !== 'string') {
        throw new Error('hash must be a string');
    }

    return bindings.get_rounds(hash);
};

<commit_msg>Consistent indentation.<commit_after>var bindings = require('bindings')('bcrypt_lib');
var crypto = require('crypto');

/// generate a salt (sync)
/// @param {Number} [rounds] number of rounds (default 10)
/// @return {String} salt
module.exports.genSaltSync = function(rounds) {
    // default 10 rounds
    if (!rounds) {
        rounds = 10;
    } else if (typeof rounds !== 'number') {
        throw new Error('rounds must be a number');
    }

    return bindings.gen_salt_sync(rounds, crypto.randomBytes(16));
};

/// generate a salt
/// @param {Number} [rounds] number of rounds (default 10)
/// @param {Function} cb callback(err, salt)
module.exports.genSalt = function(rounds, ignore, cb) {
    // if callback is first argument, then use defaults for others
    if (typeof arguments[0] === 'function') {
        // have to set callback first otherwise arguments are overriden
        cb = arguments[0];
        rounds = 10;
    // callback is second argument
    } else if (typeof arguments[1] === 'function') {
        // have to set callback first otherwise arguments are overriden
        cb = arguments[1];
    }

    // default 10 rounds
    if (!rounds) {
        rounds = 10;
    } else if (typeof rounds !== 'number') {
        return cb(new Error('rounds must be a number'));
    }

    if (!cb) {
        return;
    }

    return bindings.gen_salt(rounds, crypto.randomBytes(16), cb);
};

/// hash data using a salt
/// @param {String} data the data to encrypt
/// @param {String} salt the salt to use when hashing
/// @return {String} hash
module.exports.hashSync = function(data, salt) {
    if (data == null || data == undefined || salt == null || salt == undefined) {
        throw new Error('data and salt arguments required');
    } else if (typeof data !== 'string' && (typeof salt !== 'string' || typeof salt !== 'number')) {
        throw new Error('data must be a string and salt must either be a salt string or a number of rounds');
    }

    if (typeof salt === 'number') {
        salt = module.exports.genSaltSync(salt);
    }

    return bindings.encrypt_sync(data, salt);
};

/// hash data using a salt
/// @param {String} data the data to encrypt
/// @param {String} salt the salt to use when hashing
/// @param {Function} cb callback(err, hash)
module.exports.hash = function(data, salt, cb) {
    if (typeof data === 'function') {
        return data(new Error('data must be a string and salt must either be a salt string or a number of rounds'));
    }
    if (typeof salt === 'function') {
        return salt(new Error('data must be a string and salt must either be a salt string or a number of rounds'));
    }
    if (data == null || data == undefined || salt == null || salt == undefined) {
        return cb(new Error('data and salt arguments required'));
    } else if (typeof data !== 'string' && (typeof salt !== 'string' || typeof salt !== 'number')) {
        return cb(new Error('data must be a string and salt must either be a salt string or a number of rounds'));
    }

    if (!cb || typeof cb !== 'function') {
        return;
    }

    if (typeof salt === 'number') {
        return module.exports.genSalt(salt, function(err, salt) {
            return bindings.encrypt(data, salt, cb);
        });
    }

    return bindings.encrypt(data, salt, cb);
};

/// compare raw data to hash
/// @param {String} data the data to hash and compare
/// @param {String} hash expected hash
/// @return {bool} true if hashed data matches hash
module.exports.compareSync = function(data, hash) {
    if (data == null || data == undefined || hash == null || hash == undefined) {
        throw new Error('data and hash arguments required');
    } else if (typeof data !== 'string' || typeof hash !== 'string') {
        throw new Error('data and hash must be strings');
    }

    return bindings.compare_sync(data, hash);
};

/// compare raw data to hash
/// @param {String} data the data to hash and compare
/// @param {String} hash expected hash
/// @param {Function} cb callback(err, matched) - matched is true if hashed data matches hash
module.exports.compare = function(data, hash, cb) {
    if (data == null || data == undefined || hash == null || hash == undefined) {
        return cb(new Error('data and hash arguments required'));
    } else if (typeof data !== 'string' || typeof hash !== 'string') {
        return cb(new Error('data and hash must be strings'));
    }

    if (!cb || typeof cb !== 'function') {
        return;
    }

    return bindings.compare(data, hash, cb);
};

/// @param {String} hash extract rounds from this hash
/// @return {Number} the number of rounds used to encrypt a given hash
module.exports.getRounds = function(hash) {
    if (hash == null || hash == undefined) {
        throw new Error('hash argument required');
    } else if (typeof hash !== 'string') {
        throw new Error('hash must be a string');
    }

    return bindings.get_rounds(hash);
};
<|endoftext|>


<commit_before>

/**
 * Helper for implementing retries with backoff. Initial retry
 * delay is 1 second, increasing by 2x (+jitter) for subsequent retries
 *
 * @constructor
 */
var RetryHandler = function() {
  this.interval = 1000; // Start at one second
  this.maxInterval = 60 * 1000; // Don't wait longer than a minute
};

/**
 * Invoke the function after waiting
 *
 * @param {function} fn Function to invoke
 */
RetryHandler.prototype.retry = function(fn) {
  setTimeout(fn, this.interval);
  this.interval = this.nextInterval_();
};

/**
 * Reset the counter (e.g. after successful request.)
 */
RetryHandler.prototype.reset = function() {
  this.interval = 1000;
};

/**
 * Calculate the next wait time.
 * @return {number} Next wait interval, in milliseconds
 *
 * @private
 */
RetryHandler.prototype.nextInterval_ = function() {
  var interval = this.interval * 2 + this.getRandomInt_(0, 1000);
  return Math.min(interval, this.maxInterval);
};

/**
 * Get a random int in the range of min to max. Used to add jitter to wait times.
 *
 * @param {number} min Lower bounds
 * @param {number} max Upper bounds
 * @private
 */
RetryHandler.prototype.getRandomInt_ = function(min, max) {
  return Math.floor(Math.random() * (max - min + 1) + min);
};

/**
 * Helper class for resumable uploads using XHR/CORS. Can upload any Blob-like item, whether
 * files or in-memory constructs.
 *
 * @example
 * var content = new Blob(["Hello world"], {"type": "text/plain"});
 * var uploader = new MediaUploader({
 *   file: content,
 *   token: accessToken,
 *   onComplete: function(data) { ... }
 *   onError: function(data) { ... }
 * });
 * uploader.upload();
 *
 * @constructor
 * @param {object} options Hash of options
 * @param {string} options.token Access token
 * @param {blob} options.file Blob-like item to upload
 * @param {string} [options.fileId] ID of file if replacing
 * @param {object} [options.params] Additional query parameters
 * @param {string} [options.contentType] Content-type, if overriding the type of the blob.
 * @param {object} [options.metadata] File metadata
 * @param {function} [options.onComplete] Callback for when upload is complete
 * @param {function} [options.onProgress] Callback for status for the in-progress upload
 * @param {function} [options.onError] Callback if upload fails
 */
var MediaUploader = function(options) {
  var noop = function() {};
  this.file = options.file || {};
  this.contentType = options.contentType || this.file.type || 'application/octet-stream';
  this.metadata = options.metadata || {
    'title': this.file.name,
    'mimeType': this.contentType
  };
  this.videoData = options.videoData;
  this.token = options.token;
  this.upgrade_to_1080 = options.upgrade_to_1080;
  this.onComplete = options.onComplete || noop;
  this.onProgress = options.onProgress || noop;
  this.onError = options.onError || noop;
  this.offset = options.offset || 0;
  this.chunkSize = options.chunkSize || 0;
  this.retryHandler = new RetryHandler();
  this.metadata = [];

  this.url = options.url;

  if (!this.url) {
    var params = options.params || {};
    // params.uploadType = 'resumable';
    this.url = this.buildUrl_(options.fileId, params, options.baseUrl);
  }

  this.httpMethod = options.fileId ? 'PUT' : 'POST';
};

/**
 * Initiate the upload (Get vimeo ticket number and upload url)
 */
MediaUploader.prototype.upload = function() {
  var self = this;
  var xhr = new XMLHttpRequest();

  xhr.open(this.httpMethod, this.url, true);
  xhr.setRequestHeader('Authorization', 'Bearer ' + this.token);
  xhr.setRequestHeader('Content-Type', 'application/json');

  xhr.onload = function(e) {
    // get vimeo upload  url, user (for available quote), ticket id and complete url
    if (e.target.status < 400) {
      var response = JSON.parse(e.target.responseText);
      this.url = response.upload_link_secure;
      this.user = response.user;
      this.ticket_id = response.ticket_id;
      this.complete_url = "https://api.vimeo.com"+response.complete_uri;
      this.sendFile_();
    } else {
      this.onUploadError_(e);
    }
  }.bind(this);

  xhr.onerror = this.onUploadError_.bind(this);
  xhr.send(JSON.stringify({
    type:'streaming',
    upgrade_to_1080: this.upgrade_to_1080
  }));

};

/**
 * Send the actual file content.
 *
 * @private
 */
MediaUploader.prototype.sendFile_ = function() {
  var content = this.file;
  var end = this.file.size;

  if (this.offset || this.chunkSize) {
    // Only bother to slice the file if we're either resuming or uploading in chunks
    if (this.chunkSize) {
      end = Math.min(this.offset + this.chunkSize, this.file.size);
    }
    content = content.slice(this.offset, end);
  }

  var xhr = new XMLHttpRequest();
  xhr.open('PUT', this.url, true);
  xhr.setRequestHeader('Content-Type', this.contentType);
  // xhr.setRequestHeader('Content-Length', this.file.size);
  xhr.setRequestHeader('Content-Range', "bytes " + this.offset + "-" + (end - 1) + "/" + this.file.size);

  if (xhr.upload) {
    xhr.upload.addEventListener('progress', this.onProgress);
  }
  xhr.onload = this.onContentUploadSuccess_.bind(this);
  xhr.onerror = this.onContentUploadError_.bind(this);
  xhr.send(content);
};

/**
 * Query for the state of the file for resumption.
 *
 * @private
 */
MediaUploader.prototype.resume_ = function() {
  var xhr = new XMLHttpRequest();
  xhr.open('PUT', this.url, true);
  xhr.setRequestHeader('Content-Range', "bytes */" + this.file.size);
  xhr.setRequestHeader('X-Upload-Content-Type', this.file.type);
  if (xhr.upload) {
    xhr.upload.addEventListener('progress', this.onProgress);
  }
  xhr.onload = this.onContentUploadSuccess_.bind(this);
  xhr.onerror = this.onContentUploadError_.bind(this);
  xhr.send();
};

/**
 * Extract the last saved range if available in the request.
 *
 * @param {XMLHttpRequest} xhr Request object
 */
MediaUploader.prototype.extractRange_ = function(xhr) {
  var range = xhr.getResponseHeader('Range');
  if (range) {
    this.offset = parseInt(range.match(/\d+/g).pop(), 10) + 1;
  }
};

/**
 * The final step is to call vimeo.videos.upload.complete to queue up
 * the video for transcoding.
 *
 * If successful call 'onComplete'
 *
 * @private
 */
MediaUploader.prototype.complete_ = function() {

  var xhr = new XMLHttpRequest();
  xhr.open('DELETE', this.complete_url, true);
  xhr.setRequestHeader('Authorization', 'Bearer ' + this.token);

  xhr.onload = function(e) {

    // Get the video location (videoId)
    if (e.target.status < 400) {

      var location = e.target.getResponseHeader('Location');

      // Example of location: ' /videos/115365719', extract the video id only
      var video_id = location.split('/').pop();

      this.onComplete(video_id);
      this.onUpdateVideoData_(video_id);

    } else {
      this.onCompleteError_(e);
    }
  }.bind(this);

  xhr.onerror = this.onCompleteError_.bind(this);
  xhr.send();
};

/**
 * Update the Video Data and add the metadata to the upload object
 *
 * @private
 * @param {string} [id] Video Id
 */
MediaUploader.prototype.onUpdateVideoData_ = function(video_id) {
  var url = this.buildUrl_(video_id, [], 'https://api.vimeo.com/videos/');
  var httpMethod = 'PATCH';
  var xhr = new XMLHttpRequest();

  xhr.open(httpMethod, url, true);
  xhr.setRequestHeader('Authorization', 'Bearer ' + this.token);
  xhr.onload = function (e) {
    //add the metadata
    this.onGetMetadata_(e, video_id);
  }.bind(this);
  xhr.send(this.buildQuery_(this.videoData));
}

/**
 * Retrieve the metadata from a successful onUpdateVideoData_ response
 * This is is useful when uploading unlisted videos as the URI has changed.
 *
 * If successful call 'onUpdateVideoData_'
 *
 * @private
 * @param {object} e XHR event
 * @param {string} [id] Video Id
 */
MediaUploader.prototype.onGetMetadata_ = function (e, video_id) {
  // Get the video location (videoId)
  if (e.target.status < 400) {

    if (e.target.response) {
      //add the returned metadata to the metadata array
      var meta = JSON.parse(e.target.response);
      //get the new index of the item
      var index = this.metadata.push(meta) - 1;
      //call the complete method
      this.onComplete(video_id, index);
    } else {
      this.onCompleteError_(e);
    }
  }
};

/**
 * Handle successful responses for uploads. Depending on the context,
 * may continue with uploading the next chunk of the file or, if complete,
 * invokes vimeo complete service.
 *
 * @private
 * @param {object} e XHR event
 */
MediaUploader.prototype.onContentUploadSuccess_ = function(e) {

  if (e.target.status == 200 || e.target.status == 201) {

    this.complete_();

  } else if (e.target.status == 308) {
    this.extractRange_(e.target);
    this.retryHandler.reset();
    this.sendFile_();
  }

};

/**
 * Handles errors for uploads. Either retries or aborts depending
 * on the error.
 *
 * @private
 * @param {object} e XHR event
 */
MediaUploader.prototype.onContentUploadError_ = function(e) {
  if (e.target.status && e.target.status < 500) {
    this.onError(e.target.response);
  } else {
    this.retryHandler.retry(this.resume_.bind(this));
  }
};

/**
 * Handles errors for the complete request.
 *
 * @private
 * @param {object} e XHR event
 */
MediaUploader.prototype.onCompleteError_ = function(e) {
  this.onError(e.target.response); // TODO - Retries for initial upload
};

/**
 * Handles errors for the initial request.
 *
 * @private
 * @param {object} e XHR event
 */
MediaUploader.prototype.onUploadError_ = function(e) {
  this.onError(e.target.response); // TODO - Retries for initial upload
};

/**
 * Construct a query string from a hash/object
 *
 * @private
 * @param {object} [params] Key/value pairs for query string
 * @return {string} query string
 */
MediaUploader.prototype.buildQuery_ = function(params) {
  params = params || {};
  return Object.keys(params).map(function(key) {
    return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);
  }).join('&');
};

/**
 * Build the drive upload URL
 *
 * @private
 * @param {string} [id] File ID if replacing
 * @param {object} [params] Query parameters
 * @return {string} URL
 */
MediaUploader.prototype.buildUrl_ = function(id, params, baseUrl) {
  var url = baseUrl || 'https://api.vimeo.com/me/videos';
  if (id) {
    url += id;
  }
  var query = this.buildQuery_(params);
  if (query) {
    url += '?' + query;
  }
  return url;
};
<commit_msg>updating comments removing call to onComplete - this will be called from the new onGetMetadata_ method<commit_after>

/**
 * Helper for implementing retries with backoff. Initial retry
 * delay is 1 second, increasing by 2x (+jitter) for subsequent retries
 *
 * @constructor
 */
var RetryHandler = function() {
  this.interval = 1000; // Start at one second
  this.maxInterval = 60 * 1000; // Don't wait longer than a minute
};

/**
 * Invoke the function after waiting
 *
 * @param {function} fn Function to invoke
 */
RetryHandler.prototype.retry = function(fn) {
  setTimeout(fn, this.interval);
  this.interval = this.nextInterval_();
};

/**
 * Reset the counter (e.g. after successful request.)
 */
RetryHandler.prototype.reset = function() {
  this.interval = 1000;
};

/**
 * Calculate the next wait time.
 * @return {number} Next wait interval, in milliseconds
 *
 * @private
 */
RetryHandler.prototype.nextInterval_ = function() {
  var interval = this.interval * 2 + this.getRandomInt_(0, 1000);
  return Math.min(interval, this.maxInterval);
};

/**
 * Get a random int in the range of min to max. Used to add jitter to wait times.
 *
 * @param {number} min Lower bounds
 * @param {number} max Upper bounds
 * @private
 */
RetryHandler.prototype.getRandomInt_ = function(min, max) {
  return Math.floor(Math.random() * (max - min + 1) + min);
};

/**
 * Helper class for resumable uploads using XHR/CORS. Can upload any Blob-like item, whether
 * files or in-memory constructs.
 *
 * @example
 * var content = new Blob(["Hello world"], {"type": "text/plain"});
 * var uploader = new MediaUploader({
 *   file: content,
 *   token: accessToken,
 *   onComplete: function(data) { ... }
 *   onError: function(data) { ... }
 * });
 * uploader.upload();
 *
 * @constructor
 * @param {object} options Hash of options
 * @param {string} options.token Access token
 * @param {blob} options.file Blob-like item to upload
 * @param {string} [options.fileId] ID of file if replacing
 * @param {object} [options.params] Additional query parameters
 * @param {string} [options.contentType] Content-type, if overriding the type of the blob.
 * @param {object} [options.metadata] File metadata
 * @param {function} [options.onComplete] Callback for when upload is complete
 * @param {function} [options.onProgress] Callback for status for the in-progress upload
 * @param {function} [options.onError] Callback if upload fails
 */
var MediaUploader = function(options) {
  var noop = function() {};
  this.file = options.file || {};
  this.contentType = options.contentType || this.file.type || 'application/octet-stream';
  this.metadata = options.metadata || {
    'title': this.file.name,
    'mimeType': this.contentType
  };
  this.videoData = options.videoData;
  this.token = options.token;
  this.upgrade_to_1080 = options.upgrade_to_1080;
  this.onComplete = options.onComplete || noop;
  this.onProgress = options.onProgress || noop;
  this.onError = options.onError || noop;
  this.offset = options.offset || 0;
  this.chunkSize = options.chunkSize || 0;
  this.retryHandler = new RetryHandler();
  this.metadata = [];

  this.url = options.url;

  if (!this.url) {
    var params = options.params || {};
    // params.uploadType = 'resumable';
    this.url = this.buildUrl_(options.fileId, params, options.baseUrl);
  }

  this.httpMethod = options.fileId ? 'PUT' : 'POST';
};

/**
 * Initiate the upload (Get vimeo ticket number and upload url)
 */
MediaUploader.prototype.upload = function() {
  var self = this;
  var xhr = new XMLHttpRequest();

  xhr.open(this.httpMethod, this.url, true);
  xhr.setRequestHeader('Authorization', 'Bearer ' + this.token);
  xhr.setRequestHeader('Content-Type', 'application/json');

  xhr.onload = function(e) {
    // get vimeo upload  url, user (for available quote), ticket id and complete url
    if (e.target.status < 400) {
      var response = JSON.parse(e.target.responseText);
      this.url = response.upload_link_secure;
      this.user = response.user;
      this.ticket_id = response.ticket_id;
      this.complete_url = "https://api.vimeo.com"+response.complete_uri;
      this.sendFile_();
    } else {
      this.onUploadError_(e);
    }
  }.bind(this);

  xhr.onerror = this.onUploadError_.bind(this);
  xhr.send(JSON.stringify({
    type:'streaming',
    upgrade_to_1080: this.upgrade_to_1080
  }));

};

/**
 * Send the actual file content.
 *
 * @private
 */
MediaUploader.prototype.sendFile_ = function() {
  var content = this.file;
  var end = this.file.size;

  if (this.offset || this.chunkSize) {
    // Only bother to slice the file if we're either resuming or uploading in chunks
    if (this.chunkSize) {
      end = Math.min(this.offset + this.chunkSize, this.file.size);
    }
    content = content.slice(this.offset, end);
  }

  var xhr = new XMLHttpRequest();
  xhr.open('PUT', this.url, true);
  xhr.setRequestHeader('Content-Type', this.contentType);
  // xhr.setRequestHeader('Content-Length', this.file.size);
  xhr.setRequestHeader('Content-Range', "bytes " + this.offset + "-" + (end - 1) + "/" + this.file.size);

  if (xhr.upload) {
    xhr.upload.addEventListener('progress', this.onProgress);
  }
  xhr.onload = this.onContentUploadSuccess_.bind(this);
  xhr.onerror = this.onContentUploadError_.bind(this);
  xhr.send(content);
};

/**
 * Query for the state of the file for resumption.
 *
 * @private
 */
MediaUploader.prototype.resume_ = function() {
  var xhr = new XMLHttpRequest();
  xhr.open('PUT', this.url, true);
  xhr.setRequestHeader('Content-Range', "bytes */" + this.file.size);
  xhr.setRequestHeader('X-Upload-Content-Type', this.file.type);
  if (xhr.upload) {
    xhr.upload.addEventListener('progress', this.onProgress);
  }
  xhr.onload = this.onContentUploadSuccess_.bind(this);
  xhr.onerror = this.onContentUploadError_.bind(this);
  xhr.send();
};

/**
 * Extract the last saved range if available in the request.
 *
 * @param {XMLHttpRequest} xhr Request object
 */
MediaUploader.prototype.extractRange_ = function(xhr) {
  var range = xhr.getResponseHeader('Range');
  if (range) {
    this.offset = parseInt(range.match(/\d+/g).pop(), 10) + 1;
  }
};

/**
 * The final step is to call vimeo.videos.upload.complete to queue up
 * the video for transcoding.
 *
 * If successful call 'onUpdateVideoData_'
 *
 * @private
 */
MediaUploader.prototype.complete_ = function() {

  var xhr = new XMLHttpRequest();
  xhr.open('DELETE', this.complete_url, true);
  xhr.setRequestHeader('Authorization', 'Bearer ' + this.token);

  xhr.onload = function(e) {

    // Get the video location (videoId)
    if (e.target.status < 400) {

      var location = e.target.getResponseHeader('Location');

      // Example of location: ' /videos/115365719', extract the video id only
      var video_id = location.split('/').pop();
      // Update the video metadata
      this.onUpdateVideoData_(video_id);

    } else {
      this.onCompleteError_(e);
    }
  }.bind(this);

  xhr.onerror = this.onCompleteError_.bind(this);
  xhr.send();
};

/**
 * Update the Video Data and add the metadata to the upload object
 *
 * @private
 * @param {string} [id] Video Id
 */
MediaUploader.prototype.onUpdateVideoData_ = function(video_id) {
  var url = this.buildUrl_(video_id, [], 'https://api.vimeo.com/videos/');
  var httpMethod = 'PATCH';
  var xhr = new XMLHttpRequest();

  xhr.open(httpMethod, url, true);
  xhr.setRequestHeader('Authorization', 'Bearer ' + this.token);
  xhr.onload = function (e) {
    //add the metadata
    this.onGetMetadata_(e, video_id);
  }.bind(this);
  xhr.send(this.buildQuery_(this.videoData));
}

/**
 * Retrieve the metadata from a successful onUpdateVideoData_ response
 * This is is useful when uploading unlisted videos as the URI has changed.
 *
 * If successful call 'onUpdateVideoData_'
 *
 * @private
 * @param {object} e XHR event
 * @param {string} [id] Video Id
 */
MediaUploader.prototype.onGetMetadata_ = function (e, video_id) {
  // Get the video location (videoId)
  if (e.target.status < 400) {

    if (e.target.response) {
      //add the returned metadata to the metadata array
      var meta = JSON.parse(e.target.response);
      //get the new index of the item
      var index = this.metadata.push(meta) - 1;
      //call the complete method
      this.onComplete(video_id, index);
    } else {
      this.onCompleteError_(e);
    }
  }
};

/**
 * Handle successful responses for uploads. Depending on the context,
 * may continue with uploading the next chunk of the file or, if complete,
 * invokes vimeo complete service.
 *
 * @private
 * @param {object} e XHR event
 */
MediaUploader.prototype.onContentUploadSuccess_ = function(e) {

  if (e.target.status == 200 || e.target.status == 201) {

    this.complete_();

  } else if (e.target.status == 308) {
    this.extractRange_(e.target);
    this.retryHandler.reset();
    this.sendFile_();
  }

};

/**
 * Handles errors for uploads. Either retries or aborts depending
 * on the error.
 *
 * @private
 * @param {object} e XHR event
 */
MediaUploader.prototype.onContentUploadError_ = function(e) {
  if (e.target.status && e.target.status < 500) {
    this.onError(e.target.response);
  } else {
    this.retryHandler.retry(this.resume_.bind(this));
  }
};

/**
 * Handles errors for the complete request.
 *
 * @private
 * @param {object} e XHR event
 */
MediaUploader.prototype.onCompleteError_ = function(e) {
  this.onError(e.target.response); // TODO - Retries for initial upload
};

/**
 * Handles errors for the initial request.
 *
 * @private
 * @param {object} e XHR event
 */
MediaUploader.prototype.onUploadError_ = function(e) {
  this.onError(e.target.response); // TODO - Retries for initial upload
};

/**
 * Construct a query string from a hash/object
 *
 * @private
 * @param {object} [params] Key/value pairs for query string
 * @return {string} query string
 */
MediaUploader.prototype.buildQuery_ = function(params) {
  params = params || {};
  return Object.keys(params).map(function(key) {
    return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);
  }).join('&');
};

/**
 * Build the drive upload URL
 *
 * @private
 * @param {string} [id] File ID if replacing
 * @param {object} [params] Query parameters
 * @return {string} URL
 */
MediaUploader.prototype.buildUrl_ = function(id, params, baseUrl) {
  var url = baseUrl || 'https://api.vimeo.com/me/videos';
  if (id) {
    url += id;
  }
  var query = this.buildQuery_(params);
  if (query) {
    url += '?' + query;
  }
  return url;
};
<|endoftext|>


<commit_before>/**
 * @file aop插件
 * @author exodia(d_xinxin@163.com)
 */

import BasePlugin from './BasePlugin';

/**
 * @private
 * @ignore
 */
export default class AopPlugin extends BasePlugin {
    static AOP_COMPONENT_CONFIG = {
        module: 'uaop',
        scope: 'static'
    };

    static AOP_ID = Symbol('internalAop');

    get name() {
        return 'aop';
    }

    /**
     * @override
     */
    onContainerInit(ioc, iocConfig) {
        ioc.addComponent(this.constructor.AOP_ID, this.constructor.AOP_COMPONENT_CONFIG);
        return iocConfig;
    }

    /**
     * 是否需要代理
     *
     * @param {Object} config 配置
     * @param {string} type 代理类型[class|object]
     * @return {boolean}
     */
    canProxy(config, type) {
        if (!('aopConfig' in config)) {
            return false;
        }

        // 默认代理对象为类
        let {proxyTarget = 'class'} = config.aopConfig;
        return proxyTarget === type;
    }

    /**
     * 类或对象代理过程
     *
     * @param {IoC} ioc ioc 实例
     * @param {string} type 代理类型[class|object]
     * @param {Function} initial 待拦截的类或对象
     * @param {Array.<Object>} [advisors=[]] 切面
     * @return {Promise.<Object | Class>}
     */
    proxyAop(ioc, type, initial, advisors = []) {
        const METHOD = type === 'class' ? 'createClassProxy' : 'createObjectProxy';
        const AOP_ID = this.constructor.AOP_ID;

        return ioc.getComponent(AOP_ID).then(
            aop => advisors.reduce(
                (target, {matcher, advices}) => aop[METHOD](target, matcher, advices),
                initial
            )
        );
    }

    /**
     * @override
     */
    beforeCreateInstance(ioc, componentId, instance) {
        let config = ioc.getComponentConfig(componentId) || {};

        let proxyTarget = 'class';
        let promise = Promise.resolve();

        if (this.canProxy(config, proxyTarget)) {
            promise = this
                .proxyAop(ioc, proxyTarget, config.creator, config.aopConfig.advisors)
                .then(ProxyClass => config.creator = ProxyClass);
        }

        return promise.then(() => instance);
    }

    /**
     * @override
     */
    afterCreateInstance(ioc, componentId, instance) {
        let config = ioc.getComponentConfig(componentId) || {};
        let proxyTarget = 'object';

        if (this.canProxy(config, proxyTarget)) {
            return this.proxyAop(ioc, proxyTarget, instance, config.aopConfig.advisors);
        }

        return Promise.resolve(instance);
    }
}
<commit_msg>默认代理对象改为 object && 简洁代码<commit_after>/**
 * @file aop插件
 * @author exodia(d_xinxin@163.com)
 */

import BasePlugin from './BasePlugin';

/**
 * @private
 * @ignore
 */
export default class AopPlugin extends BasePlugin {
    static AOP_COMPONENT_CONFIG = {
        module: 'uaop',
        scope: 'static'
    };

    static AOP_ID = Symbol('internalAop');

    get name() {
        return 'aop';
    }

    /**
     * @override
     */
    onContainerInit(ioc, iocConfig) {
        ioc.addComponent(this.constructor.AOP_ID, this.constructor.AOP_COMPONENT_CONFIG);
        return iocConfig;
    }

    /**
     * 是否需要代理
     *
     * @param {Object} config 配置
     * @param {string} type 代理类型[class|object]
     * @return {boolean}
     */
    canProxy(config, type) {
        if (!('aopConfig' in config)) {
            return false;
        }

        // 默认代理对象为 object
        let {proxyTarget = 'object'} = config.aopConfig;
        return proxyTarget === type;
    }

    /**
     * 类或对象代理过程
     *
     * @param {IoC} ioc ioc 实例
     * @param {string} type 代理类型[class|object]
     * @param {Function} initial 待拦截的类或对象
     * @param {Array.<Object>} [advisors=[]] 切面
     * @return {Promise.<Object | Class>}
     */
    proxyAop(ioc, type, initial, advisors = []) {
        const METHOD = type === 'class' ? 'createClassProxy' : 'createObjectProxy';
        const AOP_ID = this.constructor.AOP_ID;

        return ioc.getComponent(AOP_ID).then(
            aop => advisors.reduce(
                (target, {matcher, advices}) => aop[METHOD](target, matcher, advices),
                initial
            )
        );
    }

    /**
     * @override
     */
    beforeCreateInstance(ioc, componentId, instance) {
        let config = ioc.getComponentConfig(componentId) || {};

        let proxyTarget = 'class';
        let promise = Promise.resolve();

        if (this.canProxy(config, proxyTarget)) {
            promise = this
                .proxyAop(ioc, proxyTarget, config.creator, config.aopConfig.advisors)
                .then(ProxyClass => config.creator = ProxyClass);
        }

        return promise.then(() => instance);
    }

    /**
     * @override
     */
    afterCreateInstance(ioc, componentId, instance) {
        let config = ioc.getComponentConfig(componentId) || {};
        let proxyTarget = 'object';

        return this.canProxy(config, proxyTarget)
            ? this.proxyAop(ioc, proxyTarget, instance, config.aopConfig.advisors)
            : Promise.resolve(instance);
    }
}
<|endoftext|>


<commit_before>// This module encapsulates reusable reactive flows
// common to many D3-based visualizations.
//
// Curran Kelleher April 2015
define(["model","d3"], function (Model,d3){

  // The returned public API object, containing functions that take as input
  // a Model.js model, and as a side effect add reactive flows within that model.
  var reactivis = {};

  // This value is used for optionally defined public properties
  var None = Model.None;

  // Adds a public property to a model.
  function addPublicProperty(model, property, defaultValue){
    if(!model.publicProperties){
      model.publicProperties = [];
    }
    model.publicProperties.push(property);
    model[property] = defaultValue;
  }

  // Constructs an SVG element as a child of the `container` element.
  // Makes the SVG size and location update with respect to the `box` property.
  // Also adds a G element as a child of the SVG element, for visualizations.
  reactivis.svg = function(model){

    // Create the SVG element from the container DOM element.
    model.when("container", function (container) {

      // Use CSS `position: absolute;` so setting `left` and `top` CSS
      // properties later will position the SVG relative to containing div.
      model.svg = d3.select(container).append("svg")
        .style("position", "absolute");
    });

    // Adjust the SVG based on the `box` property.
    model.when(["svg", "box"], function (svg, box) {

      // Set the CSS `left` and `top` properties to move the
      // SVG to `(box.x, box.y)` relative to its container.
      svg
        .style("left", box.x + "px")
        .style("top", box.y + "px")
        .attr("width", box.width)
        .attr("height", box.height);
    });

    // Create the SVG group that will contain the visualization.
    model.when("svg", function (svg) {
      model.g = svg.append("g");
    });
  };

  // Encapsulates conventional D3 margins.
  // See http://bl.ocks.org/mbostock/3019563
  reactivis.margin = function(model){

    // Set up the default margin.
    addPublicProperty(model, "margin",{
      "top": 32,
      "right": 2,
      "bottom": 40,
      "left": 47
    });

    // Compute the inner box from the outer box and margin.
    model.when(["box", "margin"], function (box, margin) {
      model.width = box.width - margin.left - margin.right;
      model.height = box.height - margin.top - margin.bottom;
    });

    // Adjust the translation of the group based on the margin.
    model.when(["g", "margin"], function (g, margin) {
      g.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    });

    // TODO change layout such that size is not added as a property.
    addPublicProperty(model, "size", 1);
  };

  // Adds a text title at the top of the visualization.
  reactivis.title = function(model){

    addPublicProperty(model, "title", "");
    addPublicProperty(model, "titleOffset", -0.3);

    // Create the title text element.
    model.when("g", function (g){
      model.titleText = g.append("text").attr("class", "title-text");
    });

    // Center the title text when width changes.
    model.when(["titleText", "width"], function (titleText, width) {
      titleText.attr("x", width / 2);
    });

    // Update the title text based on the public `title` property.
    model.when(["titleText", "title"], function (titleText, title){
      titleText.text(title);
    });

    // Update the title text offset.
    model.titleOffset = 1;
    model.when(["titleText", "titleOffset"], function (titleText, titleOffset){
      titleText.attr("dy", titleOffset + "em");
    });
  };

  reactivis.color = function(model){

    addPublicProperty(model, "colorColumn", None);
    addPublicProperty(model, "colorDomain", None);
    addPublicProperty(model, "colorRange", None);
    addPublicProperty(model, "colorDefault", "black");

    // Set up the color scale.
    model.when(["colorDefault", "colorDomain", "colorRange"],
        function (colorDefault, colorDomain, colorRange){
      if(colorDomain !== None && colorRange !== None){
        model.colorScale = d3.scale.ordinal()
          .domain(colorDomain)
          .range(colorRange);
      } else {
        model.colorScale = None;
      }
    });

    // Set up the color evaluation function.
    model.when(["colorColumn", "colorScale", "colorDefault"],
        function(colorColumn, colorScale, colorDefault){
      if(colorColumn !== None && colorScale !== None){
        model.color = function(d){ return colorScale(d[colorColumn]); };
      }
      else {
        model.color = colorDefault;
      }
    });
  };

  // Generates a function for getting the X value.
  reactivis.xAccessor = function(model){
    addPublicProperty(model, "xColumn", None);
    model.when(["xColumn"], function (xColumn) {
      if(xColumn !== None){
        model.xAccessor = function (d) { return d[xColumn]; };
      }
    });
  };

  // Generates a function for getting the Y value.
  reactivis.yAccessor = function(model){
    addPublicProperty(model, "yColumn", None);
    model.when(["yColumn"], function (yColumn) {
      if(yColumn !== None){
        model.yAccessor = function (d) { return d[yColumn]; };
      }
    });
  };

  // A lookup table for scale constructors based on their type.
  var scaleConstructors = {
    linear: d3.scale.linear,
    time: d3.time.scale,
    ordinalBands: d3.scale.ordinal
  };

  // Sets up the X scale.
  // The argument "xScaleType" should be a string,
  // one of "linear", "time", or "ordinalBands".
  reactivis.xScale = function(model, scaleType){

    // Make the scale type a public property so it is configurable at runtime.
    // Example use case: dynamically switching between linear and log scales.
    addPublicProperty(model, "xScaleType", scaleType);

    // Allow the API client to optionally specify fixed min and max values.
    // Relevant only for quantitative scales (linear, time).
    addPublicProperty(model, "xDomainMin", None);
    addPublicProperty(model, "xDomainMax", None);

    // The padding between range bands (e.g. the space between bars in a bar chart).
    // Relevant only for ordinal scales.
    addPublicProperty(model, "xRangePadding", 0.1);

    // Compute the domain of the X column.
    model.when(["data", "xScaleType", "xAccessor", "xDomainMin", "xDomainMax"],
        function (data, xScaleType, xAccessor, xDomainMin, xDomainMax) {

      // Compute the scale domain.
      if(xScaleType === "linear" || xScaleType === "time"){
        if(xDomainMin === None && xDomainMax === None){
          model.xDomain = d3.extent(data, xAccessor);
        } else {
          if(xDomainMin === None){
            xDomainMin = d3.min(data, xAccessor);
          }
          if(xDomainMax === None){
            xDomainMax = d3.max(data, xAccessor);
          }
          model.xDomain = [xDomainMin, xDomainMax];
        }
      } else if (xScaleType === "ordinalBands"){
        model.xDomain = data.map(xAccessor);
      }
    });

    // Compute the X scale.
    model.when(["xScaleType", "xDomain", "width", "xRangePadding"],
        function (xScaleType, xDomain, width, xRangePadding) {
      var scale = scaleConstructors[xScaleType]().domain(xDomain);
      if(xScaleType === "ordinalBands"){
        scale.rangeRoundBands([0, width], xRangePadding);
      } else {
        scale.range([0, width]);
      }
      model.xScale = scale;
    });

    // Generate a function for getting the scaled X value.
    model.when(["data", "xScale", "xAccessor"], function (data, xScale, xAccessor) {
      model.x = function (d) { return xScale(xAccessor(d)); };
    });
  };

  // Adds the X axis and its label text.
  reactivis.xAxis = function(model){

    addPublicProperty(model, "xAxisLabel", "");
    addPublicProperty(model, "xAxisLabelOffset", 1.9);

    // Set up the X axis.
    model.when("g", function (g) {
      model.xAxisG = g.append("g").attr("class", "x axis");
      model.xAxisText = model.xAxisG.append("text").style("text-anchor", "middle");
    });

    // Move the X axis label based on its specified offset.
    model.when(["xAxisText", "xAxisLabelOffset"], function (xAxisText, xAxisLabelOffset){
      xAxisText.attr("dy", xAxisLabelOffset + "em");
    });

    // Update the X axis transform when height changes.
    model.when(["xAxisG", "height"], function (xAxisG, height) {
      xAxisG.attr("transform", "translate(0," + height + ")");
    });

    // Center the X axis label when width changes.
    model.when(["xAxisText", "width"], function (xAxisText, width) {
      xAxisText.attr("x", width / 2);
    });

    // Update the X axis based on the X scale.
    model.when(["xAxisG", "xScale"], function (xAxisG, xScale) {
      xAxisG.call(d3.svg.axis().orient("bottom").scale(xScale));
    });

    // Update X axis label.
    model.when(["xAxisText", "xAxisLabel"], function (xAxisText, xAxisLabel) {
      xAxisText.text(xAxisLabel);
    });
  };

  // Adds the Y axis and its label text.
  reactivis.yAxis = function(model){

    // The text shown as the axis label.
    addPublicProperty(model, "yAxisLabel", "");

    // The left-right offset of the axis label, unit is CSS "em"s.
    addPublicProperty(model, "yAxisLabelOffset", 1.4);

    // Set up the Y axis.
    model.when("g", function (g) {
      model.yAxisG = g.append("g").attr("class", "y axis");
      model.yAxisText = model.yAxisG.append("text")
        .style("text-anchor", "middle")
        .attr("transform", "rotate(-90)")
        .attr("y", 0);
    });

    // Move the Y axis label based on its specified offset.
    model.when(["yAxisText", "yAxisLabelOffset"], function (yAxisText, yAxisLabelOffset){
      yAxisText.attr("dy", "-" + yAxisLabelOffset + "em");
    });

    // Center the Y axis label when height changes.
    model.when(["yAxisText", "height"], function (yAxisText, height) {
      yAxisText.attr("x", -height / 2);
    });

    // Update Y axis label.
    model.when(["yAxisText", "yAxisLabel"], function (yAxisText, yAxisLabel) {
      yAxisText.text(yAxisLabel);
    });

    // Update the Y axis based on the Y scale.
    model.when(["yAxisG", "yScale"], function (yAxisG, yScale) {
      yAxisG.call(d3.svg.axis().orient("left").scale(yScale));
    });
  };

  // Expose the addPublicProperty function, as it is a useful utility
  // function for visualizations that build on top of reactivis.
  reactivis.addPublicProperty = addPublicProperty;

  return reactivis;
});
<commit_msg>Fixed CSS bug with position absolute -> relative<commit_after>// This module encapsulates reusable reactive flows
// common to many D3-based visualizations.
//
// Curran Kelleher April 2015
define(["model","d3"], function (Model,d3){

  // The returned public API object, containing functions that take as input
  // a Model.js model, and as a side effect add reactive flows within that model.
  var reactivis = {};

  // This value is used for optionally defined public properties
  var None = Model.None;

  // Adds a public property to a model.
  function addPublicProperty(model, property, defaultValue){
    if(!model.publicProperties){
      model.publicProperties = [];
    }
    model.publicProperties.push(property);
    model[property] = defaultValue;
  }

  // Constructs an SVG element as a child of the `container` element.
  // Makes the SVG size and location update with respect to the `box` property.
  // Also adds a G element as a child of the SVG element, for visualizations.
  reactivis.svg = function(model){

    // Create the SVG element from the container DOM element.
    model.when("container", function (container) {

      // Use CSS `position: relative;` so setting `left` and `top` CSS
      // properties later will position the SVG relative to containing div.
      model.svg = d3.select(container).append("svg")
        .style("position", "relative");
    });

    // Adjust the SVG based on the `box` property.
    model.when(["svg", "box"], function (svg, box) {

      // Set the CSS `left` and `top` properties to move the
      // SVG to `(box.x, box.y)` relative to its container.
      svg
        .style("left", box.x + "px")
        .style("top", box.y + "px")
        .attr("width", box.width)
        .attr("height", box.height);
    });

    // Create the SVG group that will contain the visualization.
    model.when("svg", function (svg) {
      model.g = svg.append("g");
    });
  };

  // Encapsulates conventional D3 margins.
  // See http://bl.ocks.org/mbostock/3019563
  reactivis.margin = function(model){

    // Set up the default margin.
    addPublicProperty(model, "margin",{
      "top": 32,
      "right": 2,
      "bottom": 40,
      "left": 47
    });

    // Compute the inner box from the outer box and margin.
    model.when(["box", "margin"], function (box, margin) {
      model.width = box.width - margin.left - margin.right;
      model.height = box.height - margin.top - margin.bottom;
    });

    // Adjust the translation of the group based on the margin.
    model.when(["g", "margin"], function (g, margin) {
      g.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    });

    // TODO change layout such that size is not added as a property.
    addPublicProperty(model, "size", 1);
  };

  // Adds a text title at the top of the visualization.
  reactivis.title = function(model){

    addPublicProperty(model, "title", "");
    addPublicProperty(model, "titleOffset", -0.3);

    // Create the title text element.
    model.when("g", function (g){
      model.titleText = g.append("text").attr("class", "title-text");
    });

    // Center the title text when width changes.
    model.when(["titleText", "width"], function (titleText, width) {
      titleText.attr("x", width / 2);
    });

    // Update the title text based on the public `title` property.
    model.when(["titleText", "title"], function (titleText, title){
      titleText.text(title);
    });

    // Update the title text offset.
    model.titleOffset = 1;
    model.when(["titleText", "titleOffset"], function (titleText, titleOffset){
      titleText.attr("dy", titleOffset + "em");
    });
  };

  reactivis.color = function(model){

    addPublicProperty(model, "colorColumn", None);
    addPublicProperty(model, "colorDomain", None);
    addPublicProperty(model, "colorRange", None);
    addPublicProperty(model, "colorDefault", "black");

    // Set up the color scale.
    model.when(["colorDefault", "colorDomain", "colorRange"],
        function (colorDefault, colorDomain, colorRange){
      if(colorDomain !== None && colorRange !== None){
        model.colorScale = d3.scale.ordinal()
          .domain(colorDomain)
          .range(colorRange);
      } else {
        model.colorScale = None;
      }
    });

    // Set up the color evaluation function.
    model.when(["colorColumn", "colorScale", "colorDefault"],
        function(colorColumn, colorScale, colorDefault){
      if(colorColumn !== None && colorScale !== None){
        model.color = function(d){ return colorScale(d[colorColumn]); };
      }
      else {
        model.color = colorDefault;
      }
    });
  };

  // Generates a function for getting the X value.
  reactivis.xAccessor = function(model){
    addPublicProperty(model, "xColumn", None);
    model.when(["xColumn"], function (xColumn) {
      if(xColumn !== None){
        model.xAccessor = function (d) { return d[xColumn]; };
      }
    });
  };

  // Generates a function for getting the Y value.
  reactivis.yAccessor = function(model){
    addPublicProperty(model, "yColumn", None);
    model.when(["yColumn"], function (yColumn) {
      if(yColumn !== None){
        model.yAccessor = function (d) { return d[yColumn]; };
      }
    });
  };

  // A lookup table for scale constructors based on their type.
  var scaleConstructors = {
    linear: d3.scale.linear,
    time: d3.time.scale,
    ordinalBands: d3.scale.ordinal
  };

  // Sets up the X scale.
  // The argument "xScaleType" should be a string,
  // one of "linear", "time", or "ordinalBands".
  reactivis.xScale = function(model, scaleType){

    // Make the scale type a public property so it is configurable at runtime.
    // Example use case: dynamically switching between linear and log scales.
    addPublicProperty(model, "xScaleType", scaleType);

    // Allow the API client to optionally specify fixed min and max values.
    // Relevant only for quantitative scales (linear, time).
    addPublicProperty(model, "xDomainMin", None);
    addPublicProperty(model, "xDomainMax", None);

    // The padding between range bands (e.g. the space between bars in a bar chart).
    // Relevant only for ordinal scales.
    addPublicProperty(model, "xRangePadding", 0.1);

    // Compute the domain of the X column.
    model.when(["data", "xScaleType", "xAccessor", "xDomainMin", "xDomainMax"],
        function (data, xScaleType, xAccessor, xDomainMin, xDomainMax) {

      // Compute the scale domain.
      if(xScaleType === "linear" || xScaleType === "time"){
        if(xDomainMin === None && xDomainMax === None){
          model.xDomain = d3.extent(data, xAccessor);
        } else {
          if(xDomainMin === None){
            xDomainMin = d3.min(data, xAccessor);
          }
          if(xDomainMax === None){
            xDomainMax = d3.max(data, xAccessor);
          }
          model.xDomain = [xDomainMin, xDomainMax];
        }
      } else if (xScaleType === "ordinalBands"){
        model.xDomain = data.map(xAccessor);
      }
    });

    // Compute the X scale.
    model.when(["xScaleType", "xDomain", "width", "xRangePadding"],
        function (xScaleType, xDomain, width, xRangePadding) {
      var scale = scaleConstructors[xScaleType]().domain(xDomain);
      if(xScaleType === "ordinalBands"){
        scale.rangeRoundBands([0, width], xRangePadding);
      } else {
        scale.range([0, width]);
      }
      model.xScale = scale;
    });

    // Generate a function for getting the scaled X value.
    model.when(["data", "xScale", "xAccessor"], function (data, xScale, xAccessor) {
      model.x = function (d) { return xScale(xAccessor(d)); };
    });
  };

  // Adds the X axis and its label text.
  reactivis.xAxis = function(model){

    addPublicProperty(model, "xAxisLabel", "");
    addPublicProperty(model, "xAxisLabelOffset", 1.9);

    // Set up the X axis.
    model.when("g", function (g) {
      model.xAxisG = g.append("g").attr("class", "x axis");
      model.xAxisText = model.xAxisG.append("text").style("text-anchor", "middle");
    });

    // Move the X axis label based on its specified offset.
    model.when(["xAxisText", "xAxisLabelOffset"], function (xAxisText, xAxisLabelOffset){
      xAxisText.attr("dy", xAxisLabelOffset + "em");
    });

    // Update the X axis transform when height changes.
    model.when(["xAxisG", "height"], function (xAxisG, height) {
      xAxisG.attr("transform", "translate(0," + height + ")");
    });

    // Center the X axis label when width changes.
    model.when(["xAxisText", "width"], function (xAxisText, width) {
      xAxisText.attr("x", width / 2);
    });

    // Update the X axis based on the X scale.
    model.when(["xAxisG", "xScale"], function (xAxisG, xScale) {
      xAxisG.call(d3.svg.axis().orient("bottom").scale(xScale));
    });

    // Update X axis label.
    model.when(["xAxisText", "xAxisLabel"], function (xAxisText, xAxisLabel) {
      xAxisText.text(xAxisLabel);
    });
  };

  // Adds the Y axis and its label text.
  reactivis.yAxis = function(model){

    // The text shown as the axis label.
    addPublicProperty(model, "yAxisLabel", "");

    // The left-right offset of the axis label, unit is CSS "em"s.
    addPublicProperty(model, "yAxisLabelOffset", 1.4);

    // Set up the Y axis.
    model.when("g", function (g) {
      model.yAxisG = g.append("g").attr("class", "y axis");
      model.yAxisText = model.yAxisG.append("text")
        .style("text-anchor", "middle")
        .attr("transform", "rotate(-90)")
        .attr("y", 0);
    });

    // Move the Y axis label based on its specified offset.
    model.when(["yAxisText", "yAxisLabelOffset"], function (yAxisText, yAxisLabelOffset){
      yAxisText.attr("dy", "-" + yAxisLabelOffset + "em");
    });

    // Center the Y axis label when height changes.
    model.when(["yAxisText", "height"], function (yAxisText, height) {
      yAxisText.attr("x", -height / 2);
    });

    // Update Y axis label.
    model.when(["yAxisText", "yAxisLabel"], function (yAxisText, yAxisLabel) {
      yAxisText.text(yAxisLabel);
    });

    // Update the Y axis based on the Y scale.
    model.when(["yAxisG", "yScale"], function (yAxisG, yScale) {
      yAxisG.call(d3.svg.axis().orient("left").scale(yScale));
    });
  };

  // Expose the addPublicProperty function, as it is a useful utility
  // function for visualizations that build on top of reactivis.
  reactivis.addPublicProperty = addPublicProperty;

  return reactivis;
});
<|endoftext|>


<commit_before>var config      = require('config'),
    pg          = require('pg-query');

var pg_config   = config.pg_config,
    schema_name = config.schema_name;
pg.connectionParameters = pg_config + '/' + schema_name;

var error_response = "Schema already exists - bypassing db initialization step\n";

function create_db_schema(err, rows, result) {
  if(err && err.code == "ECONNREFUSED"){
    console.error("DB connection unavailable, see README notes for setup assistance\n", err);
    return;
  }
  // drop tables first
  // errors in this part are ignored - tables may and may not be present so DROP can fail
  console.info('Dropping table container');
  pg('DROP TABLE container;', function(err, rows, result){
    console.info('Dropping table place');
    pg('DROP TABLE place;', function(err, rows, result) {
      create_container_table(null, null, null);
    });
  });
}

function create_container_table(err, rows, result) {
  if(err) {
    console.error(error_response, err);
    return;
  }
  var table_name = 'container';
  var query = "CREATE TABLE "+table_name+" ( " +
    "gid serial NOT NULL," +
    "place_name character varying(240), " +
    "time_from TIMESTAMP NOT NULL, " +
    "time_to TIMESTAMP NOT NULL, " +
    "the_geom geometry, " +
    "CONSTRAINT "+table_name+ "_pkey PRIMARY KEY (gid), " +
    "CONSTRAINT "+table_name+"_enforce_dims_geom CHECK (st_ndims(the_geom) = 2), " +
    "CONSTRAINT "+table_name+"_enforce_geotype_geom CHECK (geometrytype(the_geom) = 'POINT'::text OR the_geom IS NULL), " +
    "CONSTRAINT "+table_name+"_enforce_srid_geom CHECK (st_srid(the_geom) = 4326), " +
    "UNIQUE (place_name, time_from, time_to)" +
    ") WITH ( OIDS=FALSE );";
  console.info('Creating table '+table_name);
  pg(query, add_spatial_index);
}

function add_spatial_index(err, rows, result) {
  if(err) {
    console.error(error_response, err);
    return;
  }
  var table_name = 'container';
  console.info('Creating spatial index on table container');
  pg("CREATE INDEX "+table_name+"_geom_gist ON "+table_name+" USING gist (the_geom);", create_place_table);
}

function create_place_table(err, rows, result) {
  if(err) {
    console.error(error_response, err);
    return;
  }
  var table_name = 'place';
  var query = "CREATE TABLE "+table_name+" ( " +
    "place_name character varying(240), " +
    "the_geom geometry, " +
    "CONSTRAINT "+table_name+"_pkey PRIMARY KEY (place_name), " +
    "CONSTRAINT "+table_name+"_enforce_dims_geom CHECK (st_ndims(the_geom) = 2), " +
    "CONSTRAINT "+table_name+"_enforce_geotype_geom CHECK (geometrytype(the_geom) = 'POINT'::text OR the_geom IS NULL), " +
    "CONSTRAINT "+table_name+"_enforce_srid_geom CHECK (st_srid(the_geom) = 4326) " +
    ") WITH ( OIDS=FALSE );";
  console.info('Creating table '+table_name);
  pg(query, function(err, rows, result) {
    if(err) {
      console.error(error_response, err);
      return;
    }
    console.info('Database initialized!');
  });
}

function import_containers(containers) {
  console.info('Importing containers to DB');
  var stmt = 'INSERT INTO container (place_name, time_from, time_to) VALUES ($1::text, $2::timestamp, $3::timestamp);';
  for (var i = 0; i < containers.length; i++) {
    pg(stmt, containers[i], function(err, rows, result) {
      if(err) {
        return console.error('Cannot insert container to DB!', err);
      }
    });
  }
  // update geo location to containers
  console.info('Updating location of containers');
  stmt = 'UPDATE container SET the_geom = (SELECT the_geom FROM place p WHERE p.place_name = place_name);';
  pg (stmt, function(err, rows, result) {
    if (err) {
      return console.error('Cannot update container locations!', err);
    }

    // update list of places
    console.info('Updating list of places');
    stmt = 'INSERT INTO place (place_name) SELECT DISTINCT o.place_name AS place_name FROM container o WHERE o.the_geom IS NULL AND NOT EXISTS (SELECT kp.place_name FROM place kp WHERE kp.place_name = o.place_name);';
    pg (stmt, function(err, rows, result) {
      if (err) {
        return console.error('Cannot update list of places!', err);
      }
    });
  });
}

function init_db(){
  pg('CREATE EXTENSION postgis;', create_db_schema);
}

function select_all(req, res, next){
  console.info('Selecting all containers');
  pg('SELECT gid, place_name, ST_X(the_geom) AS lon, ST_Y(the_geom) AS lat FROM container WHERE time_to > NOW() AND the_geom IS NOT NULL;', function(err, rows, result) {
    if(err) {
      console.error('Error running select_all query', err);
      return next(err);
    }
    res.json(rows);
    return next();
  });
}

function unknown_places(req, res, next){
  console.info('Selecting unknown places');
  pg('SELECT place_name FROM place WHERE the_geom IS NULL;', function(err, rows, result) {
    if(err) {
      console.error('Error running unknown_places query', err);
      return next(err);
    }
    res.json(rows);
    return next();
  });
}

function add_place(place_name, lat, lng, callback) {
  console.info('Adding place to DB');
  var stmt = "INSERT INTO place (place_name, the_geom) VALUES ($1::text, ST_SetSRID(ST_MakePoint($2::float, $3::float), 4326));";
  pg(stmt, [place_name, lat, lng], function(err, rows, result) {
    if(err) {
      console.error('Cannot add place to DB!', err);
      callback(err);
      return;
    }
    // update location of related containers
    console.info('Updating related containers')
    stmt = 'UPDATE container SET the_geom = (SELECT the_geom FROM place WHERE place_name = $1::text) WHERE place_name = $2::text;';
    pg(stmt, [place_name, place_name], function(err, rows, result) {
      if (err) {
        console.error('Cannot update location of related containers!', err);
        callback(err);
      }
      callback();
    });
  });
}

module.exports = exports = {
  selectAll:         select_all,
  initDB:            init_db,
  importContainers:  import_containers,
  unknownPlaces:     unknown_places,
  addPlace:          add_place
};
<commit_msg>Fixed DB statement that sets up place geo location<commit_after>var config      = require('config'),
    pg          = require('pg-query');

var pg_config   = config.pg_config,
    schema_name = config.schema_name;
pg.connectionParameters = pg_config + '/' + schema_name;

var error_response = "Schema already exists - bypassing db initialization step\n";

function create_db_schema(err, rows, result) {
  if(err && err.code == "ECONNREFUSED"){
    console.error("DB connection unavailable, see README notes for setup assistance\n", err);
    return;
  }
  // drop tables first
  // errors in this part are ignored - tables may and may not be present so DROP can fail
  console.info('Dropping table container');
  pg('DROP TABLE container;', function(err, rows, result){
    console.info('Dropping table place');
    pg('DROP TABLE place;', function(err, rows, result) {
      create_container_table(null, null, null);
    });
  });
}

function create_container_table(err, rows, result) {
  if(err) {
    console.error(error_response, err);
    return;
  }
  var table_name = 'container';
  var query = "CREATE TABLE "+table_name+" ( " +
    "gid serial NOT NULL," +
    "place_name character varying(240), " +
    "time_from TIMESTAMP NOT NULL, " +
    "time_to TIMESTAMP NOT NULL, " +
    "the_geom geometry, " +
    "CONSTRAINT "+table_name+ "_pkey PRIMARY KEY (gid), " +
    "CONSTRAINT "+table_name+"_enforce_dims_geom CHECK (st_ndims(the_geom) = 2), " +
    "CONSTRAINT "+table_name+"_enforce_geotype_geom CHECK (geometrytype(the_geom) = 'POINT'::text OR the_geom IS NULL), " +
    "CONSTRAINT "+table_name+"_enforce_srid_geom CHECK (st_srid(the_geom) = 4326), " +
    "UNIQUE (place_name, time_from, time_to)" +
    ") WITH ( OIDS=FALSE );";
  console.info('Creating table '+table_name);
  pg(query, add_spatial_index);
}

function add_spatial_index(err, rows, result) {
  if(err) {
    console.error(error_response, err);
    return;
  }
  var table_name = 'container';
  console.info('Creating spatial index on table container');
  pg("CREATE INDEX "+table_name+"_geom_gist ON "+table_name+" USING gist (the_geom);", create_place_table);
}

function create_place_table(err, rows, result) {
  if(err) {
    console.error(error_response, err);
    return;
  }
  var table_name = 'place';
  var query = "CREATE TABLE "+table_name+" ( " +
    "place_name character varying(240), " +
    "the_geom geometry, " +
    "CONSTRAINT "+table_name+"_pkey PRIMARY KEY (place_name), " +
    "CONSTRAINT "+table_name+"_enforce_dims_geom CHECK (st_ndims(the_geom) = 2), " +
    "CONSTRAINT "+table_name+"_enforce_geotype_geom CHECK (geometrytype(the_geom) = 'POINT'::text OR the_geom IS NULL), " +
    "CONSTRAINT "+table_name+"_enforce_srid_geom CHECK (st_srid(the_geom) = 4326) " +
    ") WITH ( OIDS=FALSE );";
  console.info('Creating table '+table_name);
  pg(query, function(err, rows, result) {
    if(err) {
      console.error(error_response, err);
      return;
    }
    console.info('Database initialized!');
  });
}

function import_containers(containers) {
  console.info('Importing containers to DB');
  var stmt = 'INSERT INTO container (place_name, time_from, time_to) VALUES ($1::text, $2::timestamp, $3::timestamp);';
  for (var i = 0; i < containers.length; i++) {
    pg(stmt, containers[i], function(err, rows, result) {
      if(err) {
        return console.error('Cannot insert container to DB!', err);
      }
    });
  }
  // update geo location to containers
  console.info('Updating location of containers');
  stmt = 'UPDATE container SET the_geom = (SELECT the_geom FROM place p WHERE p.place_name = place_name);';
  pg (stmt, function(err, rows, result) {
    if (err) {
      return console.error('Cannot update container locations!', err);
    }

    // update list of places
    console.info('Updating list of places');
    stmt = 'INSERT INTO place (place_name) SELECT DISTINCT o.place_name AS place_name FROM container o WHERE o.the_geom IS NULL AND NOT EXISTS (SELECT kp.place_name FROM place kp WHERE kp.place_name = o.place_name);';
    pg (stmt, function(err, rows, result) {
      if (err) {
        return console.error('Cannot update list of places!', err);
      }
    });
  });
}

function init_db(){
  pg('CREATE EXTENSION postgis;', create_db_schema);
}

function select_all(req, res, next){
  console.info('Selecting all containers');
  pg('SELECT gid, place_name, ST_X(the_geom) AS lon, ST_Y(the_geom) AS lat FROM container WHERE time_to > NOW() AND the_geom IS NOT NULL;', function(err, rows, result) {
    if(err) {
      console.error('Error running select_all query', err);
      return next(err);
    }
    res.json(rows);
    return next();
  });
}

function unknown_places(req, res, next){
  console.info('Selecting unknown places');
  pg('SELECT place_name FROM place WHERE the_geom IS NULL;', function(err, rows, result) {
    if(err) {
      console.error('Error running unknown_places query', err);
      return next(err);
    }
    res.json(rows);
    return next();
  });
}

function add_place(place_name, lat, lng, callback) {
  console.info('Adding place to DB');
  var stmt = "UPDATE place SET the_geom = ST_SetSRID(ST_MakePoint($1::float, $2::float), 4326) WHERE place_name = $3::text;";
  pg(stmt, [lat, lng, place_name], function(err, rows, result) {
    if(err) {
      console.error('Cannot add place to DB!', err);
      callback(err);
      return;
    }
    // update location of related containers
    console.info('Updating related containers')
    stmt = 'UPDATE container SET the_geom = (SELECT the_geom FROM place WHERE place_name = $1::text) WHERE place_name = $2::text;';
    pg(stmt, [place_name, place_name], function(err, rows, result) {
      if (err) {
        console.error('Cannot update location of related containers!', err);
        callback(err);
      }
      callback();
    });
  });
}

module.exports = exports = {
  selectAll:         select_all,
  initDB:            init_db,
  importContainers:  import_containers,
  unknownPlaces:     unknown_places,
  addPlace:          add_place
};
<|endoftext|>


<commit_before>define([
    'jquery',
    'function/emojioneReady',
    'function/htmlFromText',
    'function/trigger',
    'prototype/var/EmojioneArea'
],
function($, emojioneReady, htmlFromText, trigger, EmojioneArea) {
    EmojioneArea.prototype.setText = function (str) {
        var self = this, args = arguments;
        emojioneReady(function () {
            self.editor.html(htmlFromText(str, self));
            self.content = self.editor.html();
            if (args.length === 1) {
                trigger(self, 'change', [self.editor]);
            }
            trigger(self, '!resize', []);
        });
    }
});<commit_msg>Update<commit_after>define([
    'jquery',
    'function/emojioneReady',
    'function/htmlFromText',
    'function/trigger',
    'function/calcButtonPosition',
    'prototype/var/EmojioneArea'
],
function($, emojioneReady, htmlFromText, trigger, calcButtonPosition, EmojioneArea) {
    EmojioneArea.prototype.setText = function (str) {
        var self = this, args = arguments;
        emojioneReady(function () {
            self.editor.html(htmlFromText(str, self));
            self.content = self.editor.html();
            if (args.length === 1) {
                trigger(self, 'change', [self.editor]);
            }
            calcButtonPosition.apply(self);
        });
        return self;
    }
});<|endoftext|>