<commit_before>#!/usr/bin/env node
'use strict';

// application config
var config = require('./config.json');

var app             = require('express')(),
    multer          = require('multer'),
    //upload          = multer({ dest: 'uploads/' }),
    http            = require('http').Server(app),
    cors            = require('cors'),
    bodyParser      = require('body-parser');

var request         = require('request'),
    extend          = require('util')._extend,
    cliOptions 	    = require('commander'),
    fs              = require('fs'),
    pathUtil        = require('path'),
    execSync        = require('child_process').execSync,
    when            = require("promised-io/promise").when;

var validateToken 	= require('./lib/validateToken.js');

cliOptions.version('0.0.1')
           .option('-d, --dev', 'Developer mode; this option attempts to use a token in the file: dev-user-token')
           .parse(process.argv);

// if --dev option is used, set token to token in file "./dev-user-token"
// otherwise, pass on token for all routes, if there is one
if (cliOptions.dev) {
    let token = fs.readFileSync('dev-user-token', 'utf8').trim();
    console.log('\n\x1b[36m'+'using development token:'+'\x1b[0m', token, '\n')

    app.all('/', (req, res, next) => {
        req.headers = {"Authorization": token};
        next();
    }).use((req, res, next) => {
        req.headers = {"Authorization": token};
        next();
    })
}

// handle desination of uploads
var storage = multer.diskStorage({
    destination: (req, file, cb) => {

        // if multiple files, take path of first
        let reqPath = req.body.userPath;
        let path = reqPath instanceof Array ? reqPath[0] : reqPath;
        let securedReq = securePath(req.user.id, path)

        file.reqPath = securedReq.path+file.originalname;

        cb(null, config.ftpRoot+securedReq.path)
    },
    filename: (req, file, cb) => {
        cb(null, file.originalname)
    }
})

// Configure CORs and body parser.
app.use( cors() )
   .use( bodyParser.urlencoded({extended: false, limit: '50mb'}) );

// Configure Logging
app.use( (req, res, next) => {
    console.log('%s %s', req.method, req.url);
    next();
});

/**
 * @api {get} /list/:path list files/folders in path
 * @apiName list
 *
 * @apiParam {string} path path to directory
 * @apiParam {string} ?type=(folders|files) only fetch folders or files
 *
 * @apiSampleRequest /list/my/genomes/
 *
 * @apiSuccess {json} meta metadata for listed objects
 * @apiSuccessExample {json} Success-Response:
 *     HTTP/1.1 200 OK
 *      [
 *       {
 *           name: "blue-panda",
 *           mtime: 1459822597000,
 *           size: 476
 *       }, {
 *           name: "blue-zebra",
  *          mtime: 1458347601000,
 *           size: 170,
 *           isFolder: true
 *       }
 *     ]
 */
app.get('/v0/list/*', AuthRequired, (req, res) => {
    console.log('req.user.id:', req.user.id);
    const user = req.user.id,
          opts = req.query;

    const requestedPath = req.params[0];
    const securedReq = securePath(user, requestedPath);

    // throw error if user doesn't have access
    if (user != securedReq.requestedHome) {
        let msg = 'User ('+user+')'+
                ' does not have permission to access: '
                + requestedPath
        res.status(403).send({error: msg});
        return;
    }

    const rootDir = config.ftpRoot,
          path = securedReq.path,
          fullPath = rootDir+path;

    // check if user has home
    let hasHome = false;
    try {
        hasHome = fs.statSync(rootDir+'/'+user).isDirectory();
    } catch(e) {
        console.log("User's home not found:", user);
    }

    // if needed, create user's directory and enable acl's
    if (!hasHome) {
        let scriptPath = '/root/add_acl_dolson.py';
        try {
            let scriptRes = execSync(scriptPath+' --share-dir="'+rootDir+'/'+
                user+'/" --share-name="'+user+'"');
        } catch(e) {
            let msg = 'User ('+user+')'+
                ' does not have a directory and server could not run share script: '
                + scriptPath

            console.log(msg);
            res.status(500).send({error: msg});
            return;
        }
    }

    // generate list of objects describing contents
    let files = [];
    fs.readdirSync(fullPath).forEach( (file) => {
        let filePath = pathUtil.join(fullPath, file),
            stats = fs.statSync(filePath),
            isDir = stats.isDirectory();

        if (opts.type === 'file' && isDir) return;
        if (opts.type === 'folder' && !isDir) return;

        let fileObj = {
            name: file,
            path: path+file,
            mtime: stats.mtime.getTime(),
            size: stats.size
        }

        // additional info if is directory
        if (isDir) {
            fileObj.isFolder = true;
            //fileObj.folderCount = parseInt( execSync('find "' +
            //    filePath+'" -maxdepth 1 -type d | wc -l').toString() ) - 1;
        }

        files.push(fileObj);
    });

    res.send(files);
})

/**
 * @api {get} /upload post endpoint to upload data
 * @apiName upload
 *
 * @apiSampleRequest /upload/
 *
 * @apiSuccess {json} meta meta on data uploaded
 * @apiSuccessExample {json} Success-Response:
 *     HTTP/1.1 200 OK
 *     [{
            "path": "/nconrad/Athaliana.TAIR10_GeneAtlas_Experiments.tsv",
            "size": 3190639,
            "encoding": "7bit",
            "name": "Athaliana.TAIR10_GeneAtlas_Experiments.tsv"
        }, {
            "path": "/nconrad/Sandbox_Experiments-1.tsv",
            "size": 4309,
            "encoding": "7bit",
            "name": "Sandbox_Experiments-1.tsv"
        }]
 */
.post("/v0/upload",  AuthRequired, multer({ storage: storage }).array('uploads', 12),
    (req, res) => {
    let user = req.user.id;

    let log = [],
        response = [];
    req.files.forEach(f => {
        log.push(f.path)
        response.push({
            path: f.reqPath,
            size: f.size,
            encoding: f.encoding,
            name: f.originalname,
            mtime: Date.now()
        })
    })

    console.log('user ('+user+') uploaded:\n', log.join('\n')+'\n' )

    res.send(response);
})

/**
 * @api {get} /test-service/  Way to test simple, unauthenticated GET request.
 *  Note: no version "v0", "v1", etc, in the endpoint.
 *
 * @apiName test-service
 *
 * @apiSampleRequest /test-server/
 *
 * @apiSuccess {json} string Should return code 200 with string
 *  "This is just a test. This is only a test."
 *
 */
.get('/test-service', (req, res) => {
    res.status(200).send( 'This is just a test. This is only a test.' );
})

function AuthRequired(req, res, next) {
    // if no token at all, return 401
    if (!('authorization' in req.headers)) {
        res.status(401).send( {error: 'Auth is required!'} );
    }

    when(validateToken(req.headers.authorization),
        userObj => {
            if (!(userObj && 'id' in userObj)) {
                res.status(401).send( {error: 'Invalid token!'} );
                return;
            }

            // Pass user id along
            req.user = userObj;

            // safe to move along.
            next();
        })
}

// takes user and requested path
// returns the requested home and the path with user's home in path
function securePath(user, path) {
    let pathList = path.split('/').filter(s => {
        if (s !== '') return true;
    })
    let home = pathList.shift();

    // replace user with authenticated user
    let allowedPath = '/'+user+'/'+pathList.join('/');

    return {
        requestedHome: home,
        path: allowedPath
    }
}

var server = http.listen(3000, () => {
    var host = server.address().address;
    var port = server.address().port;

    console.log('Service listening at http://%s:%s', host, port);
});
<commit_msg>change destPath param name<commit_after>#!/usr/bin/env node
'use strict';

// application config
var config = require('./config.json');

var app             = require('express')(),
    multer          = require('multer'),
    //upload          = multer({ dest: 'uploads/' }),
    http            = require('http').Server(app),
    cors            = require('cors'),
    bodyParser      = require('body-parser');

var request         = require('request'),
    extend          = require('util')._extend,
    cliOptions 	    = require('commander'),
    fs              = require('fs'),
    pathUtil        = require('path'),
    execSync        = require('child_process').execSync,
    when            = require("promised-io/promise").when;

var validateToken 	= require('./lib/validateToken.js');

cliOptions.version('0.0.1')
           .option('-d, --dev', 'Developer mode; this option attempts to use a token in the file: dev-user-token')
           .parse(process.argv);

// if --dev option is used, set token to token in file "./dev-user-token"
// otherwise, pass on token for all routes, if there is one
if (cliOptions.dev) {
    let token = fs.readFileSync('dev-user-token', 'utf8').trim();
    console.log('\n\x1b[36m'+'using development token:'+'\x1b[0m', token, '\n')

    app.all('/', (req, res, next) => {
        req.headers = {"Authorization": token};
        next();
    }).use((req, res, next) => {
        req.headers = {"Authorization": token};
        next();
    })
}

// handle desination of uploads
var storage = multer.diskStorage({
    destination: (req, file, cb) => {

        // if multiple files, take path of first
        let reqPath = req.body.destPath;
        let path = reqPath instanceof Array ? reqPath[0] : reqPath;
        let securedReq = securePath(req.user.id, path)

        file.reqPath = securedReq.path+file.originalname;

        cb(null, config.ftpRoot+securedReq.path)
    },
    filename: (req, file, cb) => {
        cb(null, file.originalname)
    }
})

// Configure CORs and body parser.
app.use( cors() )
   .use( bodyParser.urlencoded({extended: false, limit: '50mb'}) );

// Configure Logging
app.use( (req, res, next) => {
    console.log('%s %s', req.method, req.url);
    next();
});

/**
 * @api {get} /list/:path list files/folders in path
 * @apiName list
 *
 * @apiParam {string} path path to directory
 * @apiParam {string} ?type=(folders|files) only fetch folders or files
 *
 * @apiSampleRequest /list/my/genomes/
 *
 * @apiSuccess {json} meta metadata for listed objects
 * @apiSuccessExample {json} Success-Response:
 *     HTTP/1.1 200 OK
 *      [
 *       {
 *           name: "blue-panda",
 *           mtime: 1459822597000,
 *           size: 476
 *       }, {
 *           name: "blue-zebra",
  *          mtime: 1458347601000,
 *           size: 170,
 *           isFolder: true
 *       }
 *     ]
 */
app.get('/v0/list/*', AuthRequired, (req, res) => {
    console.log('req.user.id:', req.user.id);
    const user = req.user.id,
          opts = req.query;

    const requestedPath = req.params[0];
    const securedReq = securePath(user, requestedPath);

    // throw error if user doesn't have access
    if (user != securedReq.requestedHome) {
        let msg = 'User ('+user+')'+
                ' does not have permission to access: '
                + requestedPath
        res.status(403).send({error: msg});
        return;
    }

    const rootDir = config.ftpRoot,
          path = securedReq.path,
          fullPath = rootDir+path;

    // check if user has home
    let hasHome = false;
    try {
        hasHome = fs.statSync(rootDir+'/'+user).isDirectory();
    } catch(e) {
        console.log("User's home not found:", user);
    }

    // if needed, create user's directory and enable acl's
    if (!hasHome) {
        let scriptPath = '/root/add_acl_dolson.py';
        try {
            let scriptRes = execSync(scriptPath+' --share-dir="'+rootDir+'/'+
                user+'/" --share-name="'+user+'"');
        } catch(e) {
            let msg = 'User ('+user+')'+
                ' does not have a directory and server could not run share script: '
                + scriptPath

            console.log(msg);
            res.status(500).send({error: msg});
            return;
        }
    }

    // generate list of objects describing contents
    let files = [];
    fs.readdirSync(fullPath).forEach( (file) => {
        let filePath = pathUtil.join(fullPath, file),
            stats = fs.statSync(filePath),
            isDir = stats.isDirectory();

        if (opts.type === 'file' && isDir) return;
        if (opts.type === 'folder' && !isDir) return;

        let fileObj = {
            name: file,
            path: path+file,
            mtime: stats.mtime.getTime(),
            size: stats.size
        }

        // additional info if is directory
        if (isDir) {
            fileObj.isFolder = true;
            //fileObj.folderCount = parseInt( execSync('find "' +
            //    filePath+'" -maxdepth 1 -type d | wc -l').toString() ) - 1;
        }

        files.push(fileObj);
    });

    res.send(files);
})

/**
 * @api {get} /upload post endpoint to upload data
 * @apiName upload
 *
 * @apiSampleRequest /upload/
 *
 * @apiSuccess {json} meta meta on data uploaded
 * @apiSuccessExample {json} Success-Response:
 *     HTTP/1.1 200 OK
 *     [{
            "path": "/nconrad/Athaliana.TAIR10_GeneAtlas_Experiments.tsv",
            "size": 3190639,
            "encoding": "7bit",
            "name": "Athaliana.TAIR10_GeneAtlas_Experiments.tsv"
        }, {
            "path": "/nconrad/Sandbox_Experiments-1.tsv",
            "size": 4309,
            "encoding": "7bit",
            "name": "Sandbox_Experiments-1.tsv"
        }]
 */
.post("/v0/upload",  AuthRequired, multer({ storage: storage }).array('uploads', 12),
    (req, res) => {
    let user = req.user.id;

    let log = [],
        response = [];
    req.files.forEach(f => {
        log.push(f.path)
        response.push({
            path: f.reqPath,
            size: f.size,
            encoding: f.encoding,
            name: f.originalname,
            mtime: Date.now()
        })
    })

    console.log('user ('+user+') uploaded:\n', log.join('\n')+'\n' )

    res.send(response);
})

/**
 * @api {get} /test-service/  Way to test simple, unauthenticated GET request.
 *  Note: no version "v0", "v1", etc, in the endpoint.
 *
 * @apiName test-service
 *
 * @apiSampleRequest /test-server/
 *
 * @apiSuccess {json} string Should return code 200 with string
 *  "This is just a test. This is only a test."
 *
 */
.get('/test-service', (req, res) => {
    res.status(200).send( 'This is just a test. This is only a test.' );
})

function AuthRequired(req, res, next) {
    // if no token at all, return 401
    if (!('authorization' in req.headers)) {
        res.status(401).send( {error: 'Auth is required!'} );
    }

    when(validateToken(req.headers.authorization),
        userObj => {
            if (!(userObj && 'id' in userObj)) {
                res.status(401).send( {error: 'Invalid token!'} );
                return;
            }

            // Pass user id along
            req.user = userObj;

            // safe to move along.
            next();
        })
}

// takes user and requested path
// returns the requested home and the path with user's home in path
function securePath(user, path) {
    let pathList = path.split('/').filter(s => {
        if (s !== '') return true;
    })
    let home = pathList.shift();

    // replace user with authenticated user
    let allowedPath = '/'+user+'/'+pathList.join('/');

    return {
        requestedHome: home,
        path: allowedPath
    }
}

var server = http.listen(3000, () => {
    var host = server.address().address;
    var port = server.address().port;

    console.log('Service listening at http://%s:%s', host, port);
});
<|endoftext|>


<commit_before>export const FORAGE_RANGE_3KM = '3km';
export const FORAGE_RANGE_5KM = '5km';

export const FORAGE_RANGES = {
    [FORAGE_RANGE_3KM]: FORAGE_RANGE_3KM,
    [FORAGE_RANGE_5KM]: FORAGE_RANGE_5KM,
};
export const DEFAULT_SORT = 'default';

export const INDICATORS = {
    NESTING_QUALITY: 'nesting',
    INSECTICIDE: 'insecticide',
    FLORAL_SPRING: 'floral_spring',
    FLORAL_SUMMER: 'floral_summer',
    FLORAL_FALL: 'floral_fall',
};

export const INDICATOR_DETAILS = {
    nesting: {
        name: 'Nesting quality',
        scoreLabels: ['Nesting'],
        shortLabel: 'Nesting',
        scoreStops: [7, 33, 39, 46, 66],
    },
    insecticide: {
        name: 'Insecticide load',
        scoreLabels: ['Insecticide'],
        shortLabel: 'Insecticide',
        scoreStops: [0, 13, 26, 37, 208],
    },
    floral_spring: {
        name: 'Spring Floral quality',
        scoreLabels: ['Spring Floral'],
        shortLabel: 'Spr. Floral',
        scoreStops: [18, 40, 46, 53, 69],
    },
    floral_summer: {
        name: 'Summer Floral quality',
        scoreLabels: ['Summer Floral'],
        shortLabel: 'Sum. Floral',
        scoreStops: [21, 44, 46, 49, 56],
    },
    floral_fall: {
        name: 'Fall Floral quality',
        scoreLabels: ['Fall Floral'],
        shortLabel: 'Fall Floral',
        scoreStops: [15, 30, 33, 36, 44],
    },
};

export const SORT_OPTIONS = Object
    .entries(INDICATOR_DETAILS)
    .reduce((acc, [indicator, { name }]) => {
        acc[indicator] = name;
        return acc;
    }, { default: 'Default' });

export const MAP_CENTER = [40.0, -76.079];
export const MAP_ZOOM = 10;
export const MAX_MAP_ZOOM = 18;

export const CONTRIBUTION_LEVEL_LIGHT = 'LIGHT';
export const CONTRIBUTION_LEVEL_PRO = 'PRO';

export const SURVEY_TYPE_NOVEMBER = 'NOVEMBER';
export const SURVEY_TYPE_APRIL = 'APRIL';
export const SURVEY_TYPE_MONTHLY = 'MONTHLY';

export const MITE_MANAGEMENT_OPTIONS = [
    'THYMOL',
    'AMITRAZ',
    'FORMIC_ACID',
    'OXALIC_ACID',
    'APISTAN',
    'CHECKMITE',
    'QUEEN_MANIPULATION',
    'DRONE_REMOVE',
    'NONE',
];

export const SEASONS = [
    'SPRING',
    'SUMMER',
    'FALL',
    'WINTER',
];

export const VARROA_CHECK_METHODS = [
    'ALCOHOL_WASH',
    'SUGAR_SHAKE',
    'STICKY_BOARDS',
];

export const COLONY_LOSS_REASONS = [
    'VARROA_MITES',
    'INADEQUATE_FOOD_STORES',
    'POOR_QUEENS',
    'POOR_WEATHER_CONDITIONS',
    'COLONY_TOO_SMALL_IN_NOVEMBER',
    'PESTICIDE_EXPOSURE',
    'BEAR_OR_NATURAL_DISASTER',
];

export const SPRING_COLONY_LOSS_REASONS = [
    'N/A_-_NO_COLONIES_LOST',
    ...COLONY_LOSS_REASONS,
];

export const ACTIVITY_SINCE_LAST = [
    'REMOVED_HONEY',
    'REMOVED_BROOD',
    'FED_POLLEN_OR_PROTEIN',
    'FED_SUGAR',
];

export const CONTRIBUTION_LEVEL_LIGHT_DESCRIPTION = {
    title: 'Light: ',
    body: 'You will be asked to fill out a survey about your bees in November and April.',
};

export const CONTRIBUTION_LEVEL_PRO_DESCRIPTION = {
    title: 'Pro: ',
    body: 'You will be asked to fill out a monthly survey, providing information for one to three colonies per apiary. This information includes colony size, Varroa counts, and colony management. You can access the surveys later, so you can also see how your colonies perform over time.',
};

export const RELOCATE_COLONIES_DESCRIPTION = {
    title: '',
    body: 'At this point, we are only gathering data on colonies that remain in one location throughout the year.',
};

export const VARROA_MANAGEMENT_DESCRIPTION = {
    title: '',
    body: 'Beekeepers manage for varroa in numerous ways. Chemical treatments include organic acids (oxalic and formic acids), thymol and other menthols, and synthetics. Mechanical strategies include removing drone brood and briefly caging the queen in the summer.',
};

export const VARROA_ALCOHOL_WASH_DESCRIPTION = {
    title: '',
    body: 'The alcohol wash is the gold standard in Varroa monitoring and is recommended due to precision and consistency. Premade cups designed for the alcohol wash work very well. Submerge nurse bees (1/2 cup or 300 bees) in alcohol, shake for 30 seconds, and allow to soak for 30 seconds to 1 minute. Next, strain the bees from the alcohol and count the mites. An important consideration when monitoring for mites is to do things as consistently as possible.',
};

export const VARROA_SUGAR_SHAKE_DESCRIPTION = {
    title: '',
    body: 'The sugar shake or sugar roll method is similar to the alcohol wash. Powdered sugar is used instead of alcohol and the bees are vigorously shaken for 2 minutes or more to dislodge mites. The mites are then separated through a screened lid and counted. This method is less accurate and more time consuming than the alcohol wash but is preferred by beekeepers who want to reduce bee deaths and who are not monitoring large numbers of colonies.',
};

export const VARROA_STICKYBOARD_DESCRIPTION = {
    title: '',
    body: 'Stickyboards are a less reliable form of regular Varroa monitoring. The number of mites that drop can vary greatly and the boards require specific equipment, regular visits to the colonies, and the time to count multiple boards over many days. Mite counts with using this method cannot be integrated into the pro level of participation at this time.',
};

export const DEEP_HIVE_BODIES_DESCRIPTION = {
    title: 'Deep hive bodies ',
    body: 'for 10 frame Langstroth measure 19 ⅞” in length, 16 ¼” wide and 9 ⅝” in height, the frames measures 19” in length and 9 ⅛” in height.',
};

export const MEDIUM_HIVE_BODIES_DESCRIPTION = {
    title: 'Medium hive bodies ',
    body: 'or Illinois super for 10 frame Langstroth measure 19 ⅞” in length, 16 ¼” wide and 6 ⅝” in height, the frames measures 19” in length and 6 ¼” in height.',
};

export const SHALLOW_HIVE_BODIES_DESCRIPTION = {
    title: 'Shallow hive bodies ',
    body: 'or honey super for 10 frame Langstroth measure 19 ⅞” in length, 16 ¼” wide and 5 11/16” in height, the frames measures 19” in length and 5 ⅝” in height.',
};

export const QUEEN_STOCK_DESCRIPTION = {
    title: '',
    body: 'Please note here if your queen came from a particular genetic line such as Russian, Italian, or has Varroa sensitive or mite resistant genetics.',
};

export const QUEENRIGHT_DESCRIPTION = {
    title: '',
    body: 'You can determine if a queen is present by locating her physically or checking for eggs. However, by late fall the queen stops laying eggs. We ask that beekeepers use their best judgement when making the assessments during these times.',
};

export const THYMOL_DESCRIPTION = {
    title: '',
    body: 'The active ingredient in Apilife and Apiguard is thymol.',
};

export const AMITRAZ_DESCRIPTION = {
    title: '',
    body: 'The active ingredient in Apivar is amitraz.',
};
<commit_msg>Update insecticide reference ranges<commit_after>export const FORAGE_RANGE_3KM = '3km';
export const FORAGE_RANGE_5KM = '5km';

export const FORAGE_RANGES = {
    [FORAGE_RANGE_3KM]: FORAGE_RANGE_3KM,
    [FORAGE_RANGE_5KM]: FORAGE_RANGE_5KM,
};
export const DEFAULT_SORT = 'default';

export const INDICATORS = {
    NESTING_QUALITY: 'nesting',
    INSECTICIDE: 'insecticide',
    FLORAL_SPRING: 'floral_spring',
    FLORAL_SUMMER: 'floral_summer',
    FLORAL_FALL: 'floral_fall',
};

export const INDICATOR_DETAILS = {
    nesting: {
        name: 'Nesting quality',
        scoreLabels: ['Nesting'],
        shortLabel: 'Nesting',
        scoreStops: [7, 33, 39, 46, 66],
    },
    insecticide: {
        name: 'Insecticide load',
        scoreLabels: ['Insecticide'],
        shortLabel: 'Insecticide',
        scoreStops: [0, 79, 159, 226, 1271],
    },
    floral_spring: {
        name: 'Spring Floral quality',
        scoreLabels: ['Spring Floral'],
        shortLabel: 'Spr. Floral',
        scoreStops: [18, 40, 46, 53, 69],
    },
    floral_summer: {
        name: 'Summer Floral quality',
        scoreLabels: ['Summer Floral'],
        shortLabel: 'Sum. Floral',
        scoreStops: [21, 44, 46, 49, 56],
    },
    floral_fall: {
        name: 'Fall Floral quality',
        scoreLabels: ['Fall Floral'],
        shortLabel: 'Fall Floral',
        scoreStops: [15, 30, 33, 36, 44],
    },
};

export const SORT_OPTIONS = Object
    .entries(INDICATOR_DETAILS)
    .reduce((acc, [indicator, { name }]) => {
        acc[indicator] = name;
        return acc;
    }, { default: 'Default' });

export const MAP_CENTER = [40.0, -76.079];
export const MAP_ZOOM = 10;
export const MAX_MAP_ZOOM = 18;

export const CONTRIBUTION_LEVEL_LIGHT = 'LIGHT';
export const CONTRIBUTION_LEVEL_PRO = 'PRO';

export const SURVEY_TYPE_NOVEMBER = 'NOVEMBER';
export const SURVEY_TYPE_APRIL = 'APRIL';
export const SURVEY_TYPE_MONTHLY = 'MONTHLY';

export const MITE_MANAGEMENT_OPTIONS = [
    'THYMOL',
    'AMITRAZ',
    'FORMIC_ACID',
    'OXALIC_ACID',
    'APISTAN',
    'CHECKMITE',
    'QUEEN_MANIPULATION',
    'DRONE_REMOVE',
    'NONE',
];

export const SEASONS = [
    'SPRING',
    'SUMMER',
    'FALL',
    'WINTER',
];

export const VARROA_CHECK_METHODS = [
    'ALCOHOL_WASH',
    'SUGAR_SHAKE',
    'STICKY_BOARDS',
];

export const COLONY_LOSS_REASONS = [
    'VARROA_MITES',
    'INADEQUATE_FOOD_STORES',
    'POOR_QUEENS',
    'POOR_WEATHER_CONDITIONS',
    'COLONY_TOO_SMALL_IN_NOVEMBER',
    'PESTICIDE_EXPOSURE',
    'BEAR_OR_NATURAL_DISASTER',
];

export const SPRING_COLONY_LOSS_REASONS = [
    'N/A_-_NO_COLONIES_LOST',
    ...COLONY_LOSS_REASONS,
];

export const ACTIVITY_SINCE_LAST = [
    'REMOVED_HONEY',
    'REMOVED_BROOD',
    'FED_POLLEN_OR_PROTEIN',
    'FED_SUGAR',
];

export const CONTRIBUTION_LEVEL_LIGHT_DESCRIPTION = {
    title: 'Light: ',
    body: 'You will be asked to fill out a survey about your bees in November and April.',
};

export const CONTRIBUTION_LEVEL_PRO_DESCRIPTION = {
    title: 'Pro: ',
    body: 'You will be asked to fill out a monthly survey, providing information for one to three colonies per apiary. This information includes colony size, Varroa counts, and colony management. You can access the surveys later, so you can also see how your colonies perform over time.',
};

export const RELOCATE_COLONIES_DESCRIPTION = {
    title: '',
    body: 'At this point, we are only gathering data on colonies that remain in one location throughout the year.',
};

export const VARROA_MANAGEMENT_DESCRIPTION = {
    title: '',
    body: 'Beekeepers manage for varroa in numerous ways. Chemical treatments include organic acids (oxalic and formic acids), thymol and other menthols, and synthetics. Mechanical strategies include removing drone brood and briefly caging the queen in the summer.',
};

export const VARROA_ALCOHOL_WASH_DESCRIPTION = {
    title: '',
    body: 'The alcohol wash is the gold standard in Varroa monitoring and is recommended due to precision and consistency. Premade cups designed for the alcohol wash work very well. Submerge nurse bees (1/2 cup or 300 bees) in alcohol, shake for 30 seconds, and allow to soak for 30 seconds to 1 minute. Next, strain the bees from the alcohol and count the mites. An important consideration when monitoring for mites is to do things as consistently as possible.',
};

export const VARROA_SUGAR_SHAKE_DESCRIPTION = {
    title: '',
    body: 'The sugar shake or sugar roll method is similar to the alcohol wash. Powdered sugar is used instead of alcohol and the bees are vigorously shaken for 2 minutes or more to dislodge mites. The mites are then separated through a screened lid and counted. This method is less accurate and more time consuming than the alcohol wash but is preferred by beekeepers who want to reduce bee deaths and who are not monitoring large numbers of colonies.',
};

export const VARROA_STICKYBOARD_DESCRIPTION = {
    title: '',
    body: 'Stickyboards are a less reliable form of regular Varroa monitoring. The number of mites that drop can vary greatly and the boards require specific equipment, regular visits to the colonies, and the time to count multiple boards over many days. Mite counts with using this method cannot be integrated into the pro level of participation at this time.',
};

export const DEEP_HIVE_BODIES_DESCRIPTION = {
    title: 'Deep hive bodies ',
    body: 'for 10 frame Langstroth measure 19 ⅞” in length, 16 ¼” wide and 9 ⅝” in height, the frames measures 19” in length and 9 ⅛” in height.',
};

export const MEDIUM_HIVE_BODIES_DESCRIPTION = {
    title: 'Medium hive bodies ',
    body: 'or Illinois super for 10 frame Langstroth measure 19 ⅞” in length, 16 ¼” wide and 6 ⅝” in height, the frames measures 19” in length and 6 ¼” in height.',
};

export const SHALLOW_HIVE_BODIES_DESCRIPTION = {
    title: 'Shallow hive bodies ',
    body: 'or honey super for 10 frame Langstroth measure 19 ⅞” in length, 16 ¼” wide and 5 11/16” in height, the frames measures 19” in length and 5 ⅝” in height.',
};

export const QUEEN_STOCK_DESCRIPTION = {
    title: '',
    body: 'Please note here if your queen came from a particular genetic line such as Russian, Italian, or has Varroa sensitive or mite resistant genetics.',
};

export const QUEENRIGHT_DESCRIPTION = {
    title: '',
    body: 'You can determine if a queen is present by locating her physically or checking for eggs. However, by late fall the queen stops laying eggs. We ask that beekeepers use their best judgement when making the assessments during these times.',
};

export const THYMOL_DESCRIPTION = {
    title: '',
    body: 'The active ingredient in Apilife and Apiguard is thymol.',
};

export const AMITRAZ_DESCRIPTION = {
    title: '',
    body: 'The active ingredient in Apivar is amitraz.',
};
<|endoftext|>


<commit_before>#!/usr/bin/env node

'use strict';

var fs = require('fs');
var path = require('path');
var program = require('commander');
var mkdirp = require('mkdirp');
var chalk = require('chalk');
var jade = require('jade');
var escapeRegex = require('escape-string-regexp');

var basename = path.basename;
var dirname = path.dirname;
var resolve = path.resolve;
var normalize = path.normalize;
var join = path.join;

// jade options

var options = {};

// options

program
  .version(
    'jade version: '     + require('jade/package.json').version + '\n' +
    'jade-cli version: ' + require(   './package.json').version
  )
  .usage('[options] [dir|file ...]')
  .option('-O, --obj <str|path>', 'JSON/JavaScript options object or file')
  .option('-o, --out <dir>', 'output the rendered HTML or compiled JavaScript to <dir>')
  .option('-p, --path <path>', 'filename used to resolve includes')
  .option('-P, --pretty', 'compile pretty html output')
  .option('-c, --client', 'compile function for client-side runtime.js')
  .option('-n, --name <str>', 'the name of the compiled template (requires --client)')
  .option('-D, --no-debug', 'compile without debugging (smaller functions)')
  .option('-w, --watch', 'watch files for changes and automatically re-render')
  .option('-E, --extension <ext>', 'specify the output file extension')
  .option('-H, --hierarchy', 'keep directory hierarchy when a directory is specified')
  .option('-s, --silent', 'do not output logs')
  .option('--name-after-file', 'name the template after the last section of the file path (requires --client and overriden by --name)')
  .option('--doctype <str>', 'specify the doctype on the command line (useful if it is not specified by the template)')

program.on('--help', function(){
  console.log('  Examples:');
  console.log('');
  console.log('    # Render all files in the `templates` directory:');
  console.log('    $ jade templates');
  console.log('');
  console.log('    # Create {foo,bar}.html:');
  console.log('    $ jade {foo,bar}.jade');
  console.log('');
  console.log('    # Using `jade` over standard input and output streams');
  console.log('    $ jade < my.jade > my.html');
  console.log('    $ echo \'h1 Jade!\' | jade');
  console.log('');
  console.log('    # Render all files in `foo` and `bar` directories to `/tmp`:');
  console.log('    $ jade foo bar --out /tmp');
  console.log('');
});

program.parse(process.argv);

// options given, parse them

if (program.obj) {
  options = parseObj(program.obj);
}

/**
 * Parse object either in `input` or in the file called `input`. The latter is
 * searched first.
 */
function parseObj (input) {
  var str;
  try {
    str = fs.readFileSync(program.obj, 'utf8');
  } catch (e) {
    str = program.obj;
  }
  try {
    return JSON.parse(str);
  } catch (e) {
    return eval('(' + str + ')');
  }
}

// --path

options.filename = program.path || options.filename;

// --no-debug

options.compileDebug = program.debug || options.compileDebug;

// --client

options.client = program.client || options.client;

// --pretty

options.pretty = program.pretty || options.pretty;

// --watch

options.watch = program.watch;

// --name

if (typeof program.name === 'string') {
  options.name = program.name;
}

// --doctype

options.doctype = program.doctype || options.doctype;

// --hierarchy

if (program.hierarchy) {
  console.warn('`-H`, `--hierarchy` option is redundant now, and will be removed in jade-cli@1.0.0');
}

// --silent

var consoleLog = program.silent ? function() {} : console.log;

// left-over args are file paths

var files = program.args;

// object of reverse dependencies of a watched file, including itself if
// applicable

var watchList = {};

// function for rendering
var render = program.watch ? tryRender : renderFile;

// compile files

if (files.length) {
  consoleLog();
  if (options.watch) {
    process.on('SIGINT', function() {
      process.exit(1);
    });
  }
  files.forEach(function (file) {
    render(file);
  });
// stdio
} else {
  stdin();
}

/**
 * Watch for changes on path
 *
 * Renders `base` if specified, otherwise renders `path`.
 */
function watchFile(path, base, rootPath) {
  path = normalize(path);

  var log = '  ' + chalk.gray('watching') + ' ' + chalk.cyan(path);
  if (!base) {
    base = path;
  } else {
    log += '\n    ' + chalk.gray('as a dependency of') + ' ';
    log += chalk.cyan(base);
  }

  if (watchList[path]) {
    if (watchList[path].indexOf(base) !== -1) return;
    consoleLog(log);
    watchList[path].push(base);
    return;
  }

  consoleLog(log);
  watchList[path] = [base];
  fs.watchFile(path, {persistent: true, interval: 200},
               function (curr, prev) {
    // File doesn't exist anymore. Keep watching.
    if (curr.mtime.getTime() === 0) return;
    // istanbul ignore if
    if (curr.mtime.getTime() === prev.mtime.getTime()) return;
    watchList[path].forEach(function(file) {
      tryRender(file, rootPath);
    });
  });
}

/**
 * Convert error to string
 */
function errorToString(e) {
  return e.stack || /* istanbul ignore next */ (e.message || e);
}

/**
 * Try to render `path`; if an exception is thrown it is printed to stderr and
 * otherwise ignored.
 *
 * This is used in watch mode.
 */
function tryRender(path, rootPath) {
  try {
    renderFile(path, rootPath);
  } catch (e) {
    // keep watching when error occured.
    console.error(errorToString(e));
  }
}

/**
 * Compile from stdin.
 */

function stdin() {
  var buf = '';
  process.stdin.setEncoding('utf8');
  process.stdin.on('data', function(chunk){ buf += chunk; });
  process.stdin.on('end', function(){
    var output;
    if (options.client) {
      output = jade.compileClient(buf, options);
    } else {
      var fn = jade.compile(buf, options);
      var output = fn(options);
    }
    process.stdout.write(output);
  }).resume();

  /* istanbul ignore next */
  process.on('SIGINT', function() {
    process.stdout.write('\n');
    process.stdin.emit('end');
    process.stdout.write('\n');
    process.stderr.write('In jade-cli@1.0.0, SIGINT will no longer signify end of input, and `jade` will exit immediately. If you are using a POSIX shell, this means that you will have to press Control-D (EOF) instead of Control-C.\n');
    process.exit();
  })
}

/**
 * Process the given path, compiling the jade files found.
 * Always walk the subdirectories.
 *
 * @param path      path of the file, might be relative
 * @param rootPath  path relative to the directory specified in the command
 */

function renderFile(path, rootPath) {
  var re = /\.jade$/;
  var stat = fs.lstatSync(path);
  // Found jade file/\.jade$/
  if (stat.isFile() && re.test(path)) {
    // Try to watch the file if needed. watchFile takes care of duplicates.
    if (options.watch) watchFile(path, null, rootPath);
    if (program.nameAfterFile) {
      options.name = getNameFromFileName(path);
    }
    var fn = options.client
           ? jade.compileFileClient(path, options)
           : jade.compileFile(path, options);
    if (options.watch && fn.dependencies) {
      // watch dependencies, and recompile the base
      fn.dependencies.forEach(function (dep) {
        watchFile(dep, path, rootPath);
      });
    }

    // --extension
    var extname;
    if (program.extension)   extname = '.' + program.extension;
    else if (options.client) extname = '.js';
    else                     extname = '.html';

    // path: foo.jade -> foo.<ext>
    path = path.replace(re, extname);
    if (program.out) {
      // prepend output directory
      if (rootPath) {
        // replace the rootPath of the resolved path with output directory
        path = resolve(path).replace(new RegExp('^' + escapeRegex(resolve(rootPath))), '');
        path = join(program.out, path);
      } else {
        // old behavior or if no rootPath handling is needed
        path = join(program.out, basename(path));
      }
    }
    var dir = resolve(dirname(path));
    mkdirp.sync(dir);
    var output = options.client ? fn : fn(options);
    fs.writeFileSync(path, output);
    consoleLog('  ' + chalk.gray('rendered') + ' ' + chalk.cyan('%s'), normalize(path));
  // Found directory
  } else if (stat.isDirectory()) {
    var files = fs.readdirSync(path);
    files.map(function(filename) {
      return path + '/' + filename;
    }).forEach(function (file) {
      render(file, rootPath || path);
    });
  }
}

/**
 * Get a sensible name for a template function from a file path
 *
 * @param {String} filename
 * @returns {String}
 */
function getNameFromFileName(filename) {
  var file = basename(filename, '.jade');
  return file.toLowerCase().replace(/[^a-z0-9]+([a-z])/g, function (_, character) {
    return character.toUpperCase();
  }) + 'Template';
}
<commit_msg>Remove deprecated hierarchy/-H option<commit_after>#!/usr/bin/env node

'use strict';

var fs = require('fs');
var path = require('path');
var program = require('commander');
var mkdirp = require('mkdirp');
var chalk = require('chalk');
var jade = require('jade');
var escapeRegex = require('escape-string-regexp');

var basename = path.basename;
var dirname = path.dirname;
var resolve = path.resolve;
var normalize = path.normalize;
var join = path.join;

// jade options

var options = {};

// options

program
  .version(
    'jade version: '     + require('jade/package.json').version + '\n' +
    'jade-cli version: ' + require(   './package.json').version
  )
  .usage('[options] [dir|file ...]')
  .option('-O, --obj <str|path>', 'JSON/JavaScript options object or file')
  .option('-o, --out <dir>', 'output the rendered HTML or compiled JavaScript to <dir>')
  .option('-p, --path <path>', 'filename used to resolve includes')
  .option('-P, --pretty', 'compile pretty html output')
  .option('-c, --client', 'compile function for client-side runtime.js')
  .option('-n, --name <str>', 'the name of the compiled template (requires --client)')
  .option('-D, --no-debug', 'compile without debugging (smaller functions)')
  .option('-w, --watch', 'watch files for changes and automatically re-render')
  .option('-E, --extension <ext>', 'specify the output file extension')
  .option('-s, --silent', 'do not output logs')
  .option('--name-after-file', 'name the template after the last section of the file path (requires --client and overriden by --name)')
  .option('--doctype <str>', 'specify the doctype on the command line (useful if it is not specified by the template)')

program.on('--help', function(){
  console.log('  Examples:');
  console.log('');
  console.log('    # Render all files in the `templates` directory:');
  console.log('    $ jade templates');
  console.log('');
  console.log('    # Create {foo,bar}.html:');
  console.log('    $ jade {foo,bar}.jade');
  console.log('');
  console.log('    # Using `jade` over standard input and output streams');
  console.log('    $ jade < my.jade > my.html');
  console.log('    $ echo \'h1 Jade!\' | jade');
  console.log('');
  console.log('    # Render all files in `foo` and `bar` directories to `/tmp`:');
  console.log('    $ jade foo bar --out /tmp');
  console.log('');
});

program.parse(process.argv);

// options given, parse them

if (program.obj) {
  options = parseObj(program.obj);
}

/**
 * Parse object either in `input` or in the file called `input`. The latter is
 * searched first.
 */
function parseObj (input) {
  var str;
  try {
    str = fs.readFileSync(program.obj, 'utf8');
  } catch (e) {
    str = program.obj;
  }
  try {
    return JSON.parse(str);
  } catch (e) {
    return eval('(' + str + ')');
  }
}

// --path

options.filename = program.path || options.filename;

// --no-debug

options.compileDebug = program.debug || options.compileDebug;

// --client

options.client = program.client || options.client;

// --pretty

options.pretty = program.pretty || options.pretty;

// --watch

options.watch = program.watch;

// --name

if (typeof program.name === 'string') {
  options.name = program.name;
}

// --doctype

options.doctype = program.doctype || options.doctype;

// --silent

var consoleLog = program.silent ? function() {} : console.log;

// left-over args are file paths

var files = program.args;

// object of reverse dependencies of a watched file, including itself if
// applicable

var watchList = {};

// function for rendering
var render = program.watch ? tryRender : renderFile;

// compile files

if (files.length) {
  consoleLog();
  if (options.watch) {
    process.on('SIGINT', function() {
      process.exit(1);
    });
  }
  files.forEach(function (file) {
    render(file);
  });
// stdio
} else {
  stdin();
}

/**
 * Watch for changes on path
 *
 * Renders `base` if specified, otherwise renders `path`.
 */
function watchFile(path, base, rootPath) {
  path = normalize(path);

  var log = '  ' + chalk.gray('watching') + ' ' + chalk.cyan(path);
  if (!base) {
    base = path;
  } else {
    log += '\n    ' + chalk.gray('as a dependency of') + ' ';
    log += chalk.cyan(base);
  }

  if (watchList[path]) {
    if (watchList[path].indexOf(base) !== -1) return;
    consoleLog(log);
    watchList[path].push(base);
    return;
  }

  consoleLog(log);
  watchList[path] = [base];
  fs.watchFile(path, {persistent: true, interval: 200},
               function (curr, prev) {
    // File doesn't exist anymore. Keep watching.
    if (curr.mtime.getTime() === 0) return;
    // istanbul ignore if
    if (curr.mtime.getTime() === prev.mtime.getTime()) return;
    watchList[path].forEach(function(file) {
      tryRender(file, rootPath);
    });
  });
}

/**
 * Convert error to string
 */
function errorToString(e) {
  return e.stack || /* istanbul ignore next */ (e.message || e);
}

/**
 * Try to render `path`; if an exception is thrown it is printed to stderr and
 * otherwise ignored.
 *
 * This is used in watch mode.
 */
function tryRender(path, rootPath) {
  try {
    renderFile(path, rootPath);
  } catch (e) {
    // keep watching when error occured.
    console.error(errorToString(e));
  }
}

/**
 * Compile from stdin.
 */

function stdin() {
  var buf = '';
  process.stdin.setEncoding('utf8');
  process.stdin.on('data', function(chunk){ buf += chunk; });
  process.stdin.on('end', function(){
    var output;
    if (options.client) {
      output = jade.compileClient(buf, options);
    } else {
      var fn = jade.compile(buf, options);
      var output = fn(options);
    }
    process.stdout.write(output);
  }).resume();

  /* istanbul ignore next */
  process.on('SIGINT', function() {
    process.stdout.write('\n');
    process.stdin.emit('end');
    process.stdout.write('\n');
    process.stderr.write('In jade-cli@1.0.0, SIGINT will no longer signify end of input, and `jade` will exit immediately. If you are using a POSIX shell, this means that you will have to press Control-D (EOF) instead of Control-C.\n');
    process.exit();
  })
}

/**
 * Process the given path, compiling the jade files found.
 * Always walk the subdirectories.
 *
 * @param path      path of the file, might be relative
 * @param rootPath  path relative to the directory specified in the command
 */

function renderFile(path, rootPath) {
  var re = /\.jade$/;
  var stat = fs.lstatSync(path);
  // Found jade file/\.jade$/
  if (stat.isFile() && re.test(path)) {
    // Try to watch the file if needed. watchFile takes care of duplicates.
    if (options.watch) watchFile(path, null, rootPath);
    if (program.nameAfterFile) {
      options.name = getNameFromFileName(path);
    }
    var fn = options.client
           ? jade.compileFileClient(path, options)
           : jade.compileFile(path, options);
    if (options.watch && fn.dependencies) {
      // watch dependencies, and recompile the base
      fn.dependencies.forEach(function (dep) {
        watchFile(dep, path, rootPath);
      });
    }

    // --extension
    var extname;
    if (program.extension)   extname = '.' + program.extension;
    else if (options.client) extname = '.js';
    else                     extname = '.html';

    // path: foo.jade -> foo.<ext>
    path = path.replace(re, extname);
    if (program.out) {
      // prepend output directory
      if (rootPath) {
        // replace the rootPath of the resolved path with output directory
        path = resolve(path).replace(new RegExp('^' + escapeRegex(resolve(rootPath))), '');
        path = join(program.out, path);
      } else {
        // old behavior or if no rootPath handling is needed
        path = join(program.out, basename(path));
      }
    }
    var dir = resolve(dirname(path));
    mkdirp.sync(dir);
    var output = options.client ? fn : fn(options);
    fs.writeFileSync(path, output);
    consoleLog('  ' + chalk.gray('rendered') + ' ' + chalk.cyan('%s'), normalize(path));
  // Found directory
  } else if (stat.isDirectory()) {
    var files = fs.readdirSync(path);
    files.map(function(filename) {
      return path + '/' + filename;
    }).forEach(function (file) {
      render(file, rootPath || path);
    });
  }
}

/**
 * Get a sensible name for a template function from a file path
 *
 * @param {String} filename
 * @returns {String}
 */
function getNameFromFileName(filename) {
  var file = basename(filename, '.jade');
  return file.toLowerCase().replace(/[^a-z0-9]+([a-z])/g, function (_, character) {
    return character.toUpperCase();
  }) + 'Template';
}
<|endoftext|>


<commit_before>'use strict';

var express = require('express');
var routes = require('./app/routes/index.js');
var mongoose = require('mongoose');
var passport = require('passport');
var session = require('express-session');

var app = express();
require('dotenv').load();
require('./app/config/passport')(passport);

mongoose.connect(process.env.MONGO_URI);

app.use('/controllers', express.static(process.cwd() + '/app/controllers'));
app.use('/public', express.static(process.cwd() + '/public'));
app.use('/common', express.static(process.cwd() + '/app/common'));

app.use(session({
	secret: 'secretClementine',
	resave: false,
	saveUninitialized: true
}));

app.use(passport.initialize());
app.use(passport.session());

routes(app, passport);

var port = process.env.PORT || 8080;
app.listen(port,  function () {
	console.log('Node.js listening on port ' + port + '...');
});<commit_msg>Minimal server w/MongoDB<commit_after>'use strict';

var express = require('express'),
    routes = require('./app/routes/index.js'),
    mongoose = require('mongoose');

var app = express();
require('dotenv').load();

var mongo_uri = process.env.MONGO_URI || 'mongodb://localhost/test';
mongoose.connect(mongo_uri);

var db = mongoose.connection;

db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {

  app.use('/public', express.static(process.cwd() + '/public'));
  //app.use('/controllers', express.static(process.cwd() + '/app/controllers'));

  routes(app);

  var port = process.env.PORT || 8080;
  app.listen(port, function () {
    console.log('Node.js listening on port ' + port + '...');
  });

});
<|endoftext|>


<commit_before>const cheerio = require('cheerio');
const request = require('request');
const fs = require('fs');
const util = require('util');
const moment = require('moment');
const TelegramBot = require('node-telegram-bot-api');

var username;

var pubDate = moment();
var config = JSON.parse(fs.readFileSync('data.json', 'utf8'));
var bot = new TelegramBot(config.key, {
    polling: true
});

bot.getMe().then(function(me) {
    username = me.username;
});

bot.on('new_chat_participant', function(msg) {
    if (msg.new_chat_member.username === username) {
        console.log('join %s(%s)', msg.chat.title, msg.chat.id);
        if (config.channel.indexOf(msg.chat.id) < 0) {
            config.channel.push(msg.chat.id);
            fs.writeFile('data.json', JSON.stringify(config));
        }
    }
});

bot.on('left_chat_participant', function(msg) {
    if (msg.left_chat_member.username === username) {
        console.log('left %s(%s)', msg.chat.title, msg.chat.id);
        if (config.channel.indexOf(msg.chat.id) > -1) {
            config.channel.splice(config.channel.indexOf(msg.chat.id), 1);
            fs.writeFile('data.json', JSON.stringify(config));
        }
    }
});

var getUpdate = function() {
    request('https://share.dmhy.org/topics/rss/sort_id/2/rss.xml', function(error, response, body) {
        if (!error && response.statusCode == 200) {
            var messages = [];
            var tmpDate;
            var $ = cheerio.load(body, {
                xmlMode: true
            });

            $("item").each(function(i, elem) {
                var date = moment($(this).children('pubDate').text(), 'ddd, DD MMM YYYY HH:mm:ss ZZ');
                if (pubDate.isBefore(date)) {
                    messages.push(util.format('%s', $(this).children('link').text(), $(this).children('title').text()));
                    if (!tmpDate) tmpDate = date;
                }
            });
            if (tmpDate) pubDate = tmpDate;

            if (messages.length > 0) {
                messages.reverse();
                console.log('fetch %s updates', messages.length);
                config.channel.forEach(function(channel) {
                    console.log('Send Update to %s', channel);
                    bot.sendMessage(channel, messages.join('\n'), {
                        parse_mode: 'HTML',
                        disable_web_page_preview: true,
                        disable_notification: true
                    });
                });
            }
        }
    });
};

setInterval(getUpdate, 3600000);<commit_msg>多加一個換行<commit_after>const cheerio = require('cheerio');
const request = require('request');
const fs = require('fs');
const util = require('util');
const moment = require('moment');
const TelegramBot = require('node-telegram-bot-api');

var username;

var pubDate = moment();
var config = JSON.parse(fs.readFileSync('data.json', 'utf8'));
var bot = new TelegramBot(config.key, {
    polling: true
});

bot.getMe().then(function(me) {
    username = me.username;
});

bot.on('new_chat_participant', function(msg) {
    if (msg.new_chat_member.username === username) {
        console.log('join %s(%s)', msg.chat.title, msg.chat.id);
        if (config.channel.indexOf(msg.chat.id) < 0) {
            config.channel.push(msg.chat.id);
            fs.writeFile('data.json', JSON.stringify(config));
        }
    }
});

bot.on('left_chat_participant', function(msg) {
    if (msg.left_chat_member.username === username) {
        console.log('left %s(%s)', msg.chat.title, msg.chat.id);
        if (config.channel.indexOf(msg.chat.id) > -1) {
            config.channel.splice(config.channel.indexOf(msg.chat.id), 1);
            fs.writeFile('data.json', JSON.stringify(config));
        }
    }
});

var getUpdate = function() {
    request('https://share.dmhy.org/topics/rss/sort_id/2/rss.xml', function(error, response, body) {
        if (!error && response.statusCode == 200) {
            var messages = [];
            var tmpDate;
            var $ = cheerio.load(body, {
                xmlMode: true
            });

            $("item").each(function(i, elem) {
                var date = moment($(this).children('pubDate').text(), 'ddd, DD MMM YYYY HH:mm:ss ZZ');
                if (pubDate.isBefore(date)) {
                    messages.push(util.format('%s', $(this).children('link').text(), $(this).children('title').text()));
                    if (!tmpDate) tmpDate = date;
                }
            });
            if (tmpDate) pubDate = tmpDate;

            if (messages.length > 0) {
                messages.reverse();
                console.log('fetch %s updates', messages.length);
                config.channel.forEach(function(channel) {
                    console.log('Send Update to %s', channel);
                    bot.sendMessage(channel, messages.join('\n\n'), {
                        parse_mode: 'HTML',
                        disable_web_page_preview: true,
                        disable_notification: true
                    });
                });
            }
        }
    });
};

setInterval(getUpdate, 3600000);<|endoftext|>


<commit_before>var needToAppendScript;
/*Transpiler Code*/
if (location.protocol === "file:") {
    //If page is served through local file system, fail gracefully
    console.warn("Scratch-JS is not running on localhost or http, anonymous callbacks now need to be explicitly declared");
    needToAppendScript = true;
} else {
    //Otherwise, do everything normally
    var code = "";
    var request = new XMLHttpRequest();
    request.open("GET", "index.sjs");
    request.send();
    //when ready state changes and the new state, shows success, get the code and store it in the code variable
    request.onreadystatechange = function() {
        if (request.readyState === 4 && request.status == 200) {
            code = request.responseText;
            transpileCode();
        }
    };
}

function whenCodeLoads() {}

function transpileCode() {
    //Transpile starting from index 0
    transpileCallbackFromIndex(0);
    transpileAnonFromIndex(0);
    evalCode();
}
//recursive function to go through each callback and transpile them to JS
function transpileCallbackFromIndex(currentIndex) {
    var indexOfCallback = code.indexOf("({", currentIndex) + 1;
    if (indexOfCallback != 0) {
        code = code.substring(0, indexOfCallback) + "function()" + code.substring(indexOfCallback, code.length);
        transpileCallbackFromIndex(indexOfCallback + 1);
    }
}

function transpileAnonFromIndex(startingIndex) {
    var index = code.indexOf("{", startingIndex);
    if (index !== -1) {
        if (code.charAt(index - 1) !== ")" && code.charAt(index - 2) !== ")") {
            code = code.substring(0, index) + " = function()" + code.substring(index, code.length);
        }
        transpileAnonFromIndex(index + 1);
    }
}

function evalCode() {
    eval(code);
    whenCodeLoads();
}
/*Main Scratch-JS Code*/
var spritesArray = [];
//sprite object constructor
function Sprite(x, y, value) {
    this.x = x;
    this.y = y;
    this.direction = 0;
    this.isHidden = false;
    spritesArray.push(this);
    //Sometimes this get changed inside other scopes, so using another variable as reference
    var thisReference = this;
    //updates both x and y
    this.updateLocation = function() {
        this.element.style.left = (page.originOffsetX + this.x - (this.element.clientWidth / 2)) + "px";
        this.element.style.top = (page.originOffsetY - this.y - (this.element.clientHeight / 2)) + "px";
    };
    //updates only x
    this.updateX = function() {
        this.element.style.left = (page.originOffsetX + this.x - (this.element.clientWidth / 2)) + "px";
    };
    //updates only y
    this.updateY = function() {
        this.element.style.top = (page.originOffsetY - this.y - (this.element.clientHeight / 2)) + "px";
    };
    this.updateRotation = function() {
        //by default turns clockwise, added "-" to make it turn counterclockwise like in geometry
        this.element.style.transform = "rotate(" + (this.direction * -1) + "deg)";
    };
    this.resize = function(scaleFactor) {
        var originalWidth = this.element.clientWidth;
        this.element.width = originalWidth * scaleFactor;
    };
    //hack of a hack of a solution, but still works. Regex checks if value is an html tag
    var valueIsHtmlTag = (/<(br|basefont|hr|input|source|frame|param|area|meta|!--|col|link|option|base|img|wbr|!DOCTYPE).*?>|<(a|abbr|acronym|address|applet|article|aside|audio|b|bdi|bdo|big|blockquote|body|button|canvas|caption|center|cite|code|colgroup|command|datalist|dd|del|details|dfn|dialog|dir|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|frameset|head|header|hgroup|h1|h2|h3|h4|h5|h6|html|i|iframe|ins|kbd|keygen|label|legend|li|map|mark|menu|meter|nav|noframes|noscript|object|ol|optgroup|output|p|pre|progress|q|rp|rt|ruby|s|samp|script|section|select|small|span|strike|strong|style|sub|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|track|tt|u|ul|var|video).*?<\/\2>/i.test(value));
    if (valueIsHtmlTag) {
        //if value is an html tag name
        var containingDiv = document.createElement("div");
        containingDiv.innerHTML = value;
        this.element = containingDiv.firstChild;
        this.updateLocation();
        document.body.appendChild(containingDiv);
        this.isImage = false;
    } else {
        //value is not html or error so custom sprite, use value as img src
        this.element = document.createElement("img");
        this.element.src = value;
        document.body.appendChild(this.element);
        this.element.style.visibility = "hidden";

        var that = this;
        var scaleFactor = arguments[3];

        this.element.onload = function() {
            //if size argument found, set it
            if (scaleFactor) {
                thisReference.resize(scaleFactor);
            }

            that.updateLocation();
            that.isImage = true;
            that.element.style.visibility = "initial";
        }
    }
    this.goTo = function() {
        if (arguments[1] != undefined) {
            //two arguments provided, the arguments are expected to be x and y respectively. go to this x and y position
            this.x = arguments[0];
            this.y = arguments[1];
            this.updateLocation();
        } else if (arguments[0]) {
            //only one argument provided, expected to be sprite. go to that sprites position
            var spriteToGoTo = arguments[0];
            this.x = spriteToGoTo.x;
            this.y = spriteToGoTo.y;
            this.updateLocation();
        }
    };
    //sets the x of the sprite
    this.setXTo = function(newX) {
        this.x = newX;
        this.updateX();
    };
    //sets the y of the sprite
    this.setYTo = function(newY) {
        this.y = newY;
        this.updateY();
    };
    //changes the x of the sprite by an amount
    this.changeXBy = function(deltaX) {
        this.x += deltaX;
        this.updateX();
    };
    //changes the x of the sprite by an amount
    this.changeYBy = function(deltaY) {
        this.y += deltaY;
        this.updateY();
    };
    this.turn = function(degrees) {
        this.direction += degrees;
        this.updateRotation();
    };
    this.pointInDirection = function(direction) {
        this.direction = direction;
        this.updateRotation();
    };
    this.move = function(amount) {
        var deltaX = Math.cos(this.direction * Math.PI / 180) * amount;
        var deltaY = Math.sin(this.direction * Math.PI / 180) * amount;
        this.x += deltaX;
        this.y += deltaY;
        this.updateLocation();
    };
    var calculateDistance = function(x1, y1, x2, y2) {
        //simple pythagorean theorem to find distance between points
        return Math.sqrt(((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2)));
    };
    this.distanceTo = function() {
        if (arguments[1] != undefined) {
            //if two arguments are provided the two arguments must be x, y coordinates
            return calculateDistance(this.x, this.y, arguments[0], arguments[1]);
        } else {
            //if only one argument, argument must be a sprite
            return calculateDistance(this.x, this.y, arguments[0].x, arguments[0].y)
        }
    };
    this.show = function() {
        this.element.style.display = "initial";
        this.isHidden = false;
    };
    this.hide = function() {
        this.element.style.display = "none";
        this.isHidden = true;
    };
    this.glideTo = function() {
        var length;
        var y;
        var x;
        var argumentsAreCoordinates = arguments[2] !== undefined;
        if (argumentsAreCoordinates) {
            x = arguments[0];
            y = arguments[1];
            length = arguments[2];
        } else {
            x = arguments[0].x;
            y = arguments[0].y;
            length = arguments[1];
        }
        this.element.style.transition = "left " + length + "ms linear, top " + length + "ms linear";
        wait(1).then(function() {
            thisReference.goTo(x, y);
        });
        //After animation finishes, reset the sprites transition property
        setTimeout(function() {
            thisReference.element.style.transition = "left 0ms, top 0ms";
        }, length);
    };
    this.changeCostume = function(newCostume) {
        var containingDiv;
        var valueIsHtmlTag = (/<(br|basefont|hr|input|source|frame|param|area|meta|!--|col|link|option|base|img|wbr|!DOCTYPE).*?>|<(a|abbr|acronym|address|applet|article|aside|audio|b|bdi|bdo|big|blockquote|body|button|canvas|caption|center|cite|code|colgroup|command|datalist|dd|del|details|dfn|dialog|dir|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|frameset|head|header|hgroup|h1|h2|h3|h4|h5|h6|html|i|iframe|ins|kbd|keygen|label|legend|li|map|mark|menu|meter|nav|noframes|noscript|object|ol|optgroup|output|p|pre|progress|q|rp|rt|ruby|s|samp|script|section|select|small|span|strike|strong|style|sub|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|track|tt|u|ul|var|video).*?<\/\2>/i.test(newCostume));
        if (!valueIsHtmlTag && this.isImage) {
            //Old sprite is image, new sprite is also image
            this.element.src = newCostume;
            this.isImage = true;
        } else if (valueIsHtmlTag && this.isImage) {
            //Old sprite is image, new sprite is not
            document.body.removeChild(this.element);
            containingDiv = document.createElement("div");
            containingDiv.innerHTML = newCostume;
            this.element = containingDiv.firstChild;
            this.updateLocation();
            document.body.appendChild(containingDiv);
            this.isImage = false;
        } else if (valueIsHtmlTag && !this.isImage) {
            //Old sprite is not an image, new one is also not image
            containingDiv = this.element.parentNode;
            containingDiv.innerHTML = newCostume;
            this.element = containingDiv.firstChild;
            this.updateLocation();
            this.isImage = false;
        } else if (!valueIsHtmlTag && !this.isImage) {
            //Old sprite is not an image, new one is an image
            document.body.removeChild(this.element.parentNode);
            this.element = document.createElement("img");
            this.element.src = newCostume;
            this.updateLocation();
            document.body.appendChild(this.element);
            this.isImage = true;
        }
    };
    return this;
}

function repeat(times, callback) {
    for (var i = 0; i < times; i++) {
        callback();
    }
}

function wait(length) {
    return new Promise(function(resolve) {
        setTimeout(function() {
            resolve();
        }, length)
    });
}

//use of forever is slow and not recommended, use while loop instead
function forever(callback) {
    return setInterval(function() {
        callback();
    }, 1);
}

//Stops a forever
function stop(intervalToStop) {
    clearInterval(intervalToStop);
}

function say(text) {
    alert(text);
}

function ask(text) {
    return prompt(text);
}

var mouse = {
    ready: false
};

mouse.setCostume = function(costumeName) {
    var mouseSprite;
    var args = arguments;
    var isCustom = !(/alias|all-scroll|auto|cell|context-menu|col-resize|copy|crosshair|default|e-resize|ew-resize|grab|grabbing|help|move|n-resize|ne-resize|nesw-resize|ns-resize|nw-resize|nwse-resize|no-drop|none|not-allowed|pointer|progress|row-resize|s-resize|se-resize|sw-resize|text|vertical-text|w-resize|wait|zoom-in|zoom-out|initial/).test(costumeName);
    if (isCustom) {
        //if new costume is custom, make the real cursor hidden everywhere
        document.body.style.cursor = "none";
        //make it hidden when on top of other elements
        for (var i = 0; i < document.body.getElementsByTagName("*").length; i++) {
            document.body.getElementsByTagName("*")[i].style.cursor = "none";
        }
        //When we get the mouse coordinates, create our fake mouse
        var checkMouseReady = forever(function() {
            if (mouse.ready) {
                //stop checking for the mouse.ready
                stop(checkMouseReady);
                //If size argument is included pass it on when creating the fake mouse's sprite
                if (args[1] !== undefined) {
                    //create the fake mouse without a size argument
                    mouseSprite = new Sprite(0, 0, costumeName, args[1]);
                } else {
                    //create the fake mouse without a size argument
                    mouseSprite = new Sprite(0, 0, costumeName);
                }
                //javascript doesn't let me modify this directly so I use a css id to get around it
                mouseSprite.element.id = "cursorImage";
                //make sure that dragging the mouse doesn't drag the mouse sprite
                mouseSprite.element.draggable = "none";
                //Forever go to the mouse
                forever(function() {
                    mouseSprite.goTo(mouse);
                })
            }
        });
    } else {
        //If it's not a custom sprite, change the cursor to the cursor type provided as a parameter
        document.body.style.cursor = costumeName;
    }
};

document.onmousemove = function() {
    mouse.x = event.x - page.originOffsetX;
    mouse.y = page.originOffsetY - event.y;
    mouse.ready = true;
};

var whenPageLoads = function() {};
var page = {};
var bodyDiv;
window.onload = function() {
    page.originOffsetX = window.innerWidth / 2;
    page.originOffsetY = window.innerHeight / 2;
    page.maxX = page.originOffsetX;
    page.maxY = page.originOffsetY;
    bodyDiv = document.createElement("div");
    bodyDiv.style.width = page.originOffsetX * 2 + "px";
    bodyDiv.style.height = page.originOffsetY * 2 + "px";
    bodyDiv.style.left = "0px";
    document.body.appendChild(bodyDiv);
    if (needToAppendScript) {
        var scriptToAppend = document.createElement("script");
        scriptToAppend.src = "index.sjs";
        document.body.appendChild(scriptToAppend);
    }
    //add styles
    var style = document.createElement("style");
    style.innerHTML = "* { position: absolute; } body { margin: 0; opacity: 0; overflow: hidden; } #cursorImage { pointer-events: none }";
    document.getElementsByTagName('head')[0].appendChild(style);

    whenCodeLoads = function() {
        document.body.style.opacity = "1";
        whenPageLoads();
    }
};

window.onresize = function() {
    page.originOffsetX = window.innerWidth / 2;
    page.originOffsetY = window.innerHeight / 2;
    page.maxX = page.originOffsetX;
    page.maxY = page.originOffsetY;
    for (var spriteIndex in spritesArray) {
        spritesArray[spriteIndex].updateLocation();
    }
    bodyDiv.style.width = page.originOffsetX * 2 + "px";
    bodyDiv.style.height = page.originOffsetY * 2 + "px";
};<commit_msg>Refactoring and Reformating<commit_after>/*Main Scratch-JS Code*/
var spritesArray = [];
//sprite object constructor
function Sprite(x, y, value) {
    this.x = x;
    this.y = y;
    this.direction = 0;
    this.isHidden = false;
    spritesArray.push(this);
    //Sometimes this get changed inside other scopes, so using another variable as reference
    var thisReference = this;

    //hack of a hack of a solution, but still works. Regex checks if value is an html tag
    var valueIsHtmlTag = (/<(br|basefont|hr|input|source|frame|param|area|meta|!--|col|link|option|base|img|wbr|!DOCTYPE).*?>|<(a|abbr|acronym|address|applet|article|aside|audio|b|bdi|bdo|big|blockquote|body|button|canvas|caption|center|cite|code|colgroup|command|datalist|dd|del|details|dfn|dialog|dir|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|frameset|head|header|hgroup|h1|h2|h3|h4|h5|h6|html|i|iframe|ins|kbd|keygen|label|legend|li|map|mark|menu|meter|nav|noframes|noscript|object|ol|optgroup|output|p|pre|progress|q|rp|rt|ruby|s|samp|script|section|select|small|span|strike|strong|style|sub|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|track|tt|u|ul|var|video).*?<\/\2>/i.test(value));
    if (valueIsHtmlTag) {
        //if value is an html tag name
        var containingDiv = document.createElement("div");
        containingDiv.innerHTML = value;
        this.element = containingDiv.firstChild;
        this.updateLocation();
        document.body.appendChild(containingDiv);
        this.isImage = false;
    } else {
        //value is not html or error so custom sprite, use value as img src
        this.element = document.createElement("img");
        this.element.src = value;
        document.body.appendChild(this.element);
        this.element.style.visibility = "hidden";

        var that = this;
        var scaleFactor = arguments[3];

        this.element.onload = function() {
            //if size argument found, set it
            if (scaleFactor) {
                thisReference.resize(scaleFactor);
            }

            that.updateLocation();
            that.isImage = true;
            that.element.style.visibility = "initial";
        }
    }

    //updates both x and y
    this.updateLocation = function() {
        this.element.style.left = (page.originOffsetX + this.x - (this.element.clientWidth / 2)) + "px";
        this.element.style.top = (page.originOffsetY - this.y - (this.element.clientHeight / 2)) + "px";
    };

    //updates only x
    this.updateX = function() {
        this.element.style.left = (page.originOffsetX + this.x - (this.element.clientWidth / 2)) + "px";
    };

    //updates only y
    this.updateY = function() {
        this.element.style.top = (page.originOffsetY - this.y - (this.element.clientHeight / 2)) + "px";
    };

    this.updateRotation = function() {
        //by default turns clockwise, added "-" to make it turn counterclockwise like in geometry
        this.element.style.transform = "rotate(" + (this.direction * -1) + "deg)";
    };

    this.resize = function(scaleFactor) {
        var originalWidth = this.element.clientWidth;
        this.element.width = originalWidth * scaleFactor;
    };

    this.goTo = function() {
        if (arguments[1] != undefined) {
            //two arguments provided, the arguments are expected to be x and y respectively. go to this x and y position
            this.x = arguments[0];
            this.y = arguments[1];
            this.updateLocation();
        } else if (arguments[0]) {
            //only one argument provided, expected to be sprite. go to that sprites position
            var spriteToGoTo = arguments[0];
            this.x = spriteToGoTo.x;
            this.y = spriteToGoTo.y;
            this.updateLocation();
        }
    };

    //sets the x of the sprite
    this.setXTo = function(newX) {
        this.x = newX;
        this.updateX();
    };

    //sets the y of the sprite
    this.setYTo = function(newY) {
        this.y = newY;
        this.updateY();
    };

    //changes the x of the sprite by an amount
    this.changeXBy = function(deltaX) {
        this.x += deltaX;
        this.updateX();
    };

    //changes the x of the sprite by an amount
    this.changeYBy = function(deltaY) {
        this.y += deltaY;
        this.updateY();
    };

    this.turn = function(degrees) {
        this.direction += degrees;
        this.updateRotation();
    };

    this.pointInDirection = function(direction) {
        this.direction = direction;
        this.updateRotation();
    };

    this.move = function(amount) {
        var deltaX = Math.cos(this.direction * Math.PI / 180) * amount;
        var deltaY = Math.sin(this.direction * Math.PI / 180) * amount;
        this.x += deltaX;
        this.y += deltaY;
        this.updateLocation();
    };

    var calculateDistance = function(x1, y1, x2, y2) {
        //simple pythagorean theorem to find distance between points
        return Math.sqrt(((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2)));
    };

    this.distanceTo = function() {
        if (arguments[1] != undefined) {
            //if two arguments are provided the two arguments must be x, y coordinates
            return calculateDistance(this.x, this.y, arguments[0], arguments[1]);
        } else {
            //if only one argument, argument must be a sprite
            return calculateDistance(this.x, this.y, arguments[0].x, arguments[0].y)
        }
    };

    this.show = function() {
        this.element.style.display = "initial";
        this.isHidden = false;
    };

    this.hide = function() {
        this.element.style.display = "none";
        this.isHidden = true;
    };

    this.glideTo = function() {
        var length;
        var y;
        var x;
        var argumentsAreCoordinates = arguments[2] !== undefined;
        if (argumentsAreCoordinates) {
            x = arguments[0];
            y = arguments[1];
            length = arguments[2];
        } else {
            x = arguments[0].x;
            y = arguments[0].y;
            length = arguments[1];
        }
        this.element.style.transition = "left " + length + "ms linear, top " + length + "ms linear";
        wait(1).then(function() {
            thisReference.goTo(x, y);
        });
        //After animation finishes, reset the sprites transition property
        setTimeout(function() {
            thisReference.element.style.transition = "left 0ms, top 0ms";
        }, length);
    };

    this.changeCostume = function(newCostume) {
        var containingDiv;
        var valueIsHtmlTag = (/<(br|basefont|hr|input|source|frame|param|area|meta|!--|col|link|option|base|img|wbr|!DOCTYPE).*?>|<(a|abbr|acronym|address|applet|article|aside|audio|b|bdi|bdo|big|blockquote|body|button|canvas|caption|center|cite|code|colgroup|command|datalist|dd|del|details|dfn|dialog|dir|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|frameset|head|header|hgroup|h1|h2|h3|h4|h5|h6|html|i|iframe|ins|kbd|keygen|label|legend|li|map|mark|menu|meter|nav|noframes|noscript|object|ol|optgroup|output|p|pre|progress|q|rp|rt|ruby|s|samp|script|section|select|small|span|strike|strong|style|sub|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|track|tt|u|ul|var|video).*?<\/\2>/i.test(newCostume));
        if (!valueIsHtmlTag && this.isImage) {
            //Old sprite is image, new sprite is also image
            this.element.src = newCostume;
            this.isImage = true;
        } else if (valueIsHtmlTag && this.isImage) {
            //Old sprite is image, new sprite is not
            document.body.removeChild(this.element);
            containingDiv = document.createElement("div");
            containingDiv.innerHTML = newCostume;
            this.element = containingDiv.firstChild;
            this.updateLocation();
            document.body.appendChild(containingDiv);
            this.isImage = false;
        } else if (valueIsHtmlTag && !this.isImage) {
            //Old sprite is not an image, new one is also not image
            containingDiv = this.element.parentNode;
            containingDiv.innerHTML = newCostume;
            this.element = containingDiv.firstChild;
            this.updateLocation();
            this.isImage = false;
        } else if (!valueIsHtmlTag && !this.isImage) {
            //Old sprite is not an image, new one is an image
            document.body.removeChild(this.element.parentNode);
            this.element = document.createElement("img");
            this.element.src = newCostume;
            this.updateLocation();
            document.body.appendChild(this.element);
            this.isImage = true;
        }
    };
    return this;
}

function repeat(times, callback) {
    for (var i = 0; i < times; i++) {
        callback();
    }
}

function wait(length) {
    return new Promise(function(resolve) {
        setTimeout(function() {
            resolve();
        }, length)
    });
}

//use of forever is slow and not recommended, use while loop instead
function forever(callback) {
    return setInterval(function() {
        callback();
    }, 1);
}

//Stops a forever
function stop(intervalToStop) {
    clearInterval(intervalToStop);
}

function say(text) {
    alert(text);
}

function ask(text) {
    return prompt(text);
}

var mouse = {
    ready: false
};

mouse.setCostume = function(costumeName) {
    var mouseSprite;
    var args = arguments;
    var isCustom = !(/alias|all-scroll|auto|cell|context-menu|col-resize|copy|crosshair|default|e-resize|ew-resize|grab|grabbing|help|move|n-resize|ne-resize|nesw-resize|ns-resize|nw-resize|nwse-resize|no-drop|none|not-allowed|pointer|progress|row-resize|s-resize|se-resize|sw-resize|text|vertical-text|w-resize|wait|zoom-in|zoom-out|initial/).test(costumeName);
    if (isCustom) {
        //if new costume is custom, make the real cursor hidden everywhere
        document.body.style.cursor = "none";
        //make it hidden when on top of other elements
        for (var i = 0; i < document.body.getElementsByTagName("*").length; i++) {
            document.body.getElementsByTagName("*")[i].style.cursor = "none";
        }
        //When we get the mouse coordinates, create our fake mouse
        var checkMouseReady = forever(function() {
            if (mouse.ready) {
                //stop checking for the mouse.ready
                stop(checkMouseReady);
                //If size argument is included pass it on when creating the fake mouse's sprite
                if (args[1] !== undefined) {
                    //create the fake mouse without a size argument
                    mouseSprite = new Sprite(0, 0, costumeName, args[1]);
                } else {
                    //create the fake mouse without a size argument
                    mouseSprite = new Sprite(0, 0, costumeName);
                }
                //javascript doesn't let me modify this directly so I use a css id to get around it
                mouseSprite.element.id = "cursorImage";
                //make sure that dragging the mouse doesn't drag the mouse sprite
                mouseSprite.element.draggable = "none";
                //Forever go to the mouse
                forever(function() {
                    mouseSprite.goTo(mouse);
                })
            }
        });
    } else {
        //If it's not a custom sprite, change the cursor to the cursor type provided as a parameter
        document.body.style.cursor = costumeName;
    }
};

document.onmousemove = function() {
    mouse.x = event.x - page.originOffsetX;
    mouse.y = page.originOffsetY - event.y;
    mouse.ready = true;
};

var whenPageLoads = function() {};
var page = {};
var bodyDiv;
window.onload = function() {
    page.originOffsetX = window.innerWidth / 2;
    page.originOffsetY = window.innerHeight / 2;
    page.maxX = page.originOffsetX;
    page.maxY = page.originOffsetY;
    bodyDiv = document.createElement("div");
    bodyDiv.style.width = page.originOffsetX * 2 + "px";
    bodyDiv.style.height = page.originOffsetY * 2 + "px";
    bodyDiv.style.left = "0px";
    document.body.appendChild(bodyDiv);
    if (needToAppendScript) {
        var scriptToAppend = document.createElement("script");
        scriptToAppend.src = "index.sjs";
        document.body.appendChild(scriptToAppend);
    }
    //add styles
    var style = document.createElement("style");
    style.innerHTML = "* { position: absolute; } body { margin: 0; opacity: 0; overflow: hidden; } #cursorImage { pointer-events: none }";
    document.getElementsByTagName('head')[0].appendChild(style);

    whenCodeLoads = function() {
        document.body.style.opacity = "1";
        whenPageLoads();
    }
};

window.onresize = function() {
    page.originOffsetX = window.innerWidth / 2;
    page.originOffsetY = window.innerHeight / 2;
    page.maxX = page.originOffsetX;
    page.maxY = page.originOffsetY;
    for (var spriteIndex in spritesArray) {
        spritesArray[spriteIndex].updateLocation();
    }
    bodyDiv.style.width = page.originOffsetX * 2 + "px";
    bodyDiv.style.height = page.originOffsetY * 2 + "px";
};

/*Transpiler*/
var needToAppendScript;
/*Transpiler Code*/
if (location.protocol === "file:") {
    //If page is served through local file system, fail gracefully
    console.warn("Scratch-JS is not running on localhost or http, anonymous callbacks now need to be explicitly declared");
    needToAppendScript = true;
} else {
    //Otherwise, do everything normally
    var code = "";
    var request = new XMLHttpRequest();
    request.open("GET", "index.sjs");
    request.send();
    //when ready state changes and the new state, shows success, get the code and store it in the code variable
    request.onreadystatechange = function() {
        if (request.readyState === 4 && request.status == 200) {
            code = request.responseText;
            transpileCode();
        }
    };
}

function whenCodeLoads() {}

function transpileCode() {
    //Transpile starting from index 0
    transpileCallbackFromIndex(0);
    transpileAnonFromIndex(0);
    evalCode();
}
//recursive function to go through each callback and transpile them to JS
function transpileCallbackFromIndex(currentIndex) {
    var indexOfCallback = code.indexOf("({", currentIndex) + 1;
    if (indexOfCallback != 0) {
        code = code.substring(0, indexOfCallback) + "function()" + code.substring(indexOfCallback, code.length);
        transpileCallbackFromIndex(indexOfCallback + 1);
    }
}

function transpileAnonFromIndex(startingIndex) {
    var index = code.indexOf("{", startingIndex);
    if (index !== -1) {
        if (code.charAt(index - 1) !== ")" && code.charAt(index - 2) !== ")") {
            code = code.substring(0, index) + " = function()" + code.substring(index, code.length);
        }
        transpileAnonFromIndex(index + 1);
    }
}

function evalCode() {
    eval(code);
    whenCodeLoads();
}<|endoftext|>


<commit_before>var express = require('express');
var bodyParser = require('body-parser')
var app = express();

var auth = require('./lib/infrastructure/auth');
var todoRoute = require('./lib/todo/todo.route');

app.use(auth.checkAuthorized);
app.use(bodyParser.json());

todoRoute(app);

app.listen(8080, function () {
    console.log('Server started - port', 8080);
});
<commit_msg>fix(config): get prot from environment<commit_after>var express = require('express');
var bodyParser = require('body-parser')
var app = express();

var auth = require('./lib/infrastructure/auth');
var todoRoute = require('./lib/todo/todo.route');

var port = process.env.PORT || 8080;

app.use(auth.checkAuthorized);
app.use(bodyParser.json());

todoRoute(app);

app.listen(port, function () {
    console.log('Server started - port', port);
});
<|endoftext|>