<commit_before>// JS Module
import * as KerasJS from 'keras-js'
import ndarray from 'ndarray'
import ops from 'ndarray-ops'
import Vue from 'vue'
import Clipboard from 'clipboard'

// JSON
import CharListFile from  '../char_list.json'

// clipboard.js
var clipboard = new Clipboard('.btn');

clipboard.on('success', function (e) {
    console.info('Action:', e.action);
    console.info('Text:', e.text);
    console.info('Trigger:', e.trigger);

    e.clearSelection();
});

clipboard.on('error', function (e) {
    console.error('Action:', e.action);
    console.error('Trigger:', e.trigger);
});

// Vue.js
new Vue({
    el: '#app',
    data: {
        modelLoading: true,
        modelRunning: false,
        modelInterrupt: false,
        model: new KerasJS.Model({
            filepaths: {
                model: 'model/model.json',
                weights: 'model/model_weights.buf',
                metadata: 'model/model_metadata.json',
            },
            gpu: true
        }),
        charListFile: CharListFile,
        inputImage: {
            URL: 'sample-data/test_image.png',
            width: 0,
            height: 0
        },
        grayscaleImage: {
            URL: '',
            width: 0,
            height: 0
        },
        previewLineImage: {
            width: 632,
            height: 64
        },
        outputAA: {
            maxLineNum: '-',
            currentLineNum: '-',
            totalPercentage: 0,
            linePercentage: 0,
            width: 550,
        },
        resultAA: ''
    },
    methods: {
        onFileChange: function(e) {
            let files = e.target.files || e.dataTransfer.files;
            if (!files.length) {
                return;
            }
            this.createImage(files[0]);
        },
        createImage: function(file) {
            var image = new Image();
            var reader = new FileReader();
            reader.onload = (e) => {
                this.inputImage.URL = e.target.result;
            };
            reader.readAsDataURL(file);
        },
        canvasAddMargin: function(canvas) {
            var img = new Image();
            img.onload = () => {
                var ctx = canvas.getContext("2d");
                canvas.width = (24 + 17) + img.width + (24 + 17);
                canvas.height = 24 + img.height + 24;
                ctx.fillStyle = "white";
                ctx.fillRect(0, 0, canvas.width, canvas.height);
                ctx.drawImage(img, 24 + 17, 24);
                this.grayscaleImage.URL = canvas.toDataURL();
            };
            img.src = canvas.toDataURL();
        },
        canvasResize: function(canvas, scale_ratio) {
            var img = new Image();
            img.onload = () => {
                var ctx = canvas.getContext("2d");
                canvas.width *= scale_ratio;
                canvas.height *= scale_ratio;
                ctx.scale(scale_ratio, scale_ratio);
                ctx.drawImage(img, 0, 0);
                this.canvasAddMargin(canvas);
            };
            img.src = canvas.toDataURL();
        },
        grayscale: function(imageURL) {
            try {
                // get image
                var canvas = document.createElement("canvas");
                var img = new Image();
                img.onload = () => {
                    var ctx = canvas.getContext("2d");
                    canvas.width = img.width;
                    canvas.height = img.height;
                    // draw image to canvas
                    ctx.drawImage(img, 0, 0);
                    // get image data
                    var pixels = ctx.getImageData(0, 0, canvas.width, canvas.height);
                    // update input image info
                    this.inputImage.width = img.width;
                    this.inputImage.height = img.height;
                    // grayscaling
                    for (var y = 0; y < pixels.height; y++) {
                        for (var x = 0; x < pixels.width; x++) {
                            // sampling pixel(RGBA)
                            var pos = (y * pixels.width + x) * 4;
                            // RGB to grayscale color
                            var grayScalePixelColor = parseInt(
                                0.299 * pixels.data[pos] +
                                0.587 * pixels.data[pos + 1] +
                                0.114 * pixels.data[pos + 2], 10);
                            // update pixel data
                            pixels.data[pos]     = grayScalePixelColor; // R
                            pixels.data[pos + 1] = grayScalePixelColor; // G
                            pixels.data[pos + 2] = grayScalePixelColor; // B
                            pixels.data[pos + 3] = 255;                 // A
                        }
                    }
                    ctx.putImageData(pixels, 0, 0);
                    // update scale
                    let scale_ratio = this.outputAA.width / img.width;
                    this.canvasResize(canvas, scale_ratio);
                };
                img.src = imageURL;
            } catch (err) {
                console.error('grayscale: ', err.message);
                this.resultAA = err.message;
            }
        },
        inputImageLoad: function() {
            this.grayscale(this.inputImage.URL);
        },
        grayscaleImageLoad: async function() {
            try {
                while (this.modelRunning) {
                    this.modelInterrupt = true;
                    console.log('wait until stopped current process')
                    await this.sleep(16);
                }
                this.modelRunning = true;
                this.modelInterrupt = false;
                // reset status
                this.resultAA = '';
                this.outputAA.totalPercentage = 0;
                this.outputAA.linePercentage = 0;

                // get input data
                let dataTensor = undefined;
                let MaxlineNum = undefined;
                {
                    const canvas = document.createElement("canvas");
                    const ctx = canvas.getContext("2d");
                    const img = new Image();
                    img.src = this.grayscaleImage.URL;
                    canvas.width = img.width;
                    canvas.height = img.height;
                    ctx.drawImage(img, 0, 0);
                    const pixels = ctx.getImageData(0, 0, canvas.width, canvas.height);
                    // update grayscaleImage info
                    this.grayscaleImage.width = img.width;
                    this.grayscaleImage.height = img.height;
                    // convert ndarray (width * height * RGBA)
                    const dataSourceTensor = ndarray(new Float32Array(pixels.data), [pixels.width, pixels.height, 4]);
                    dataTensor = ndarray(new Float32Array(pixels.width * pixels.height), [pixels.width, pixels.height, 1]);
                    // pick grayscale data
                    ops.assign(dataTensor.pick(null, null, 0), dataSourceTensor.pick(null, null, 0))
                    // normalization (0.0 ~ 1.0)
                    ops.divseq(dataTensor, 255);
                    // calc AA line count
                    MaxlineNum = Math.floor((pixels.height - 48) / 18);
                    this.outputAA.maxLineNum = MaxlineNum;
                }

                // wait until model is ready
                await this.model.ready();

                const float32concat = function(first, second) {
                    let result = new Float32Array(first.length + second.length);
                    result.set(first);
                    result.set(second, first.length);
                    return result;
                };

                const PERLINE = 1 / MaxlineNum;
                const updateProgress = (currentLineNum, end) => {
                    this.outputAA.currentLineNum = currentLineNum;
                    this.outputAA.linePercentage =
                        Math.floor(end / dataTensor.shape[0] * 100);
                    this.outputAA.totalPercentage =
                        Math.floor(PERLINE * (currentLineNum + end / dataTensor.shape[0]) * 100);
                };

                // loop each line
                for (var i = 0; i < MaxlineNum; i++) {
                    // reshape lineImage
                    let lineImage =
                        dataTensor.data.slice(
                            (i * 18) * dataTensor.shape[0],
                            ((i * 18) + 64) * dataTensor.shape[0]);

                    let start = 0;
                    let end = 64;
                    let penalty = 1;

                    let width = 64;
                    while (end <= dataTensor.shape[0]) {
                        // set timeout
                        let timeout = this.setInterval(16);
                        // update line progress
                        updateProgress(i, end);
                        // reshape
                        let patch_data = new Float32Array();
                        for (var j = 0; j < 64; j++) {
                            let line_data = lineImage.slice(
                                    (j * dataTensor.shape[0]) + start,
                                    (j * dataTensor.shape[0]) + end);
                            patch_data = float32concat(patch_data, line_data);
                        }
                        let patch = ndarray(new Float32Array(patch_data), [64, 64]);
                        // predict
                        const inputData = {
                            'input_1': patch.data
                        };
                        let y = await this.model.predict(inputData);
                        y = ndarray(y.dense_1);

                        if (penalty==1) {
                           y.set(1, 0);
                        }

                        let predict = ops.argmax(y);
                        let char = this.charListFile[predict][0];
                        let char_width = this.charListFile[predict][1];

                        this.resultAA += char;

                        start += char_width;
                        end += char_width;

                        if (predict==1) {
                            penalty = 1;
                        } else {
                            penalty = 0;
                        }
                        if (this.modelInterrupt) {
                            this.modelRunning = false;
                            return;
                        }

                        // update preview image
                        this.updatePreviewLineImage(lineImage, dataTensor.shape[0], 64, start);
                        this.updatePreviewPatchImage(patch.data);
                        // keep frame interval (16 msec)
                        await timeout;
                    }
                    this.resultAA += '\n';
                }
                // finish
                this.outputAA.currentLineNum = MaxlineNum;
                this.outputAA.linePercentage = 100;
                this.outputAA.totalPercentage = 100;
                this.modelRunning = false;
            } catch (err) {
                this.modelRunning = false;
                console.error('model: ', err.message);
                this.resultAA = err.message;
            }
        },
        updatePreviewLineImage: function(data, width, height, pos_x) {
            // update canvas and info
            this.previewLineImage.width = width;
            this.previewLineImage.height = height;
            // update canvas
            const canvas = this.$refs.lineImageCanvas;
            const ctx = canvas.getContext("2d");
            const pixels = ctx.getImageData(0, 0, canvas.width, canvas.height);
            for (let y = 0; y < pixels.height; y++) {
                for (let x = 0; x < pixels.width; x++) {
                    const i = (y * 4) * pixels.width + x * 4;
                    const rgb = data[x + y * pixels.width] * 255;
                    pixels.data[i] = rgb;
                    pixels.data[i + 1] = rgb;
                    pixels.data[i + 2] = rgb;
                    pixels.data[i + 3] = 255;
                }
            }
            ctx.putImageData(pixels, 0, 0);
            // write red rectangle (Convert AA Area)
            ctx.beginPath()
            ctx.strokeStyle = 'red';
            ctx.strokeRect(24, 24, width - (24 * 2), 16);
            // write blue rectangle (Patch Image Area)
            ctx.beginPath()
            ctx.strokeStyle = 'blue';
            ctx.strokeRect(pos_x, 0, 64, 64);
            ctx.strokeRect(pos_x + 24, 24, 16, 16);
        },
        updatePreviewPatchImage: function(data) {
            const canvas = this.$refs.patchImageCanvas;
            const ctx = canvas.getContext("2d");
            const pixels = ctx.getImageData(0, 0, canvas.width, canvas.height);
            for (let y = 0; y < pixels.height; y++) {
                for (let x = 0; x < pixels.width; x++) {
                    const i = (y * 4) * pixels.width + x * 4;
                    const rgb = data[x + y * pixels.width] * 255;
                    pixels.data[i] = rgb;
                    pixels.data[i + 1] = rgb;
                    pixels.data[i + 2] = rgb;
                    pixels.data[i + 3] = 255;
                }
            }
            ctx.putImageData(pixels, 0, 0);
            // write red rectangle (convert AA area)
            ctx.beginPath()
            ctx.strokeStyle = 'red';
            ctx.strokeRect(24, 24, 16, 16);
        },
        setInterval: async function (ms) {
            return new Promise(resolve => setTimeout(resolve, ms));
        }
    },
    computed: {
        loadingProgress: function() {
            return this.model.getLoadingProgress();
        },
        progressMessage: function() {
            if (this.outputAA.totalPercentage == 100) {
                return 'Complete!';
            } else {
                return this.outputAA.totalPercentage + '%';
            }
        }
    }
})<commit_msg>marginの値が間違ってたので修正<commit_after>// JS Module
import * as KerasJS from 'keras-js'
import ndarray from 'ndarray'
import ops from 'ndarray-ops'
import Vue from 'vue'
import Clipboard from 'clipboard'

// JSON
import CharListFile from  '../char_list.json'

// clipboard.js
var clipboard = new Clipboard('.btn');

clipboard.on('success', function (e) {
    console.info('Action:', e.action);
    console.info('Text:', e.text);
    console.info('Trigger:', e.trigger);

    e.clearSelection();
});

clipboard.on('error', function (e) {
    console.error('Action:', e.action);
    console.error('Trigger:', e.trigger);
});

// Vue.js
new Vue({
    el: '#app',
    data: {
        modelLoading: true,
        modelRunning: false,
        modelInterrupt: false,
        model: new KerasJS.Model({
            filepaths: {
                model: 'model/model.json',
                weights: 'model/model_weights.buf',
                metadata: 'model/model_metadata.json',
            },
            gpu: true
        }),
        charListFile: CharListFile,
        inputImage: {
            URL: 'sample-data/test_image.png',
            width: 0,
            height: 0
        },
        grayscaleImage: {
            URL: '',
            width: 0,
            height: 0
        },
        previewLineImage: {
            width: 632,
            height: 64
        },
        outputAA: {
            maxLineNum: '-',
            currentLineNum: '-',
            totalPercentage: 0,
            linePercentage: 0,
            width: 550,
        },
        resultAA: ''
    },
    methods: {
        onFileChange: function(e) {
            let files = e.target.files || e.dataTransfer.files;
            if (!files.length) {
                return;
            }
            this.createImage(files[0]);
        },
        createImage: function(file) {
            var image = new Image();
            var reader = new FileReader();
            reader.onload = (e) => {
                this.inputImage.URL = e.target.result;
            };
            reader.readAsDataURL(file);
        },
        canvasAddMargin: function(canvas) {
            var img = new Image();
            img.onload = () => {
                var ctx = canvas.getContext("2d");
                canvas.width = 24 + img.width + (15 + 24);
                canvas.height = 24 + img.height + (17 + 24);
                ctx.fillStyle = "white";
                ctx.fillRect(0, 0, canvas.width, canvas.height);
                ctx.drawImage(img, 24, 24);
                this.grayscaleImage.URL = canvas.toDataURL();
            };
            img.src = canvas.toDataURL();
        },
        canvasResize: function(canvas, scale_ratio) {
            var img = new Image();
            img.onload = () => {
                var ctx = canvas.getContext("2d");
                canvas.width *= scale_ratio;
                canvas.height *= scale_ratio;
                ctx.scale(scale_ratio, scale_ratio);
                ctx.drawImage(img, 0, 0);
                this.canvasAddMargin(canvas);
            };
            img.src = canvas.toDataURL();
        },
        grayscale: function(imageURL) {
            try {
                // get image
                var canvas = document.createElement("canvas");
                var img = new Image();
                img.onload = () => {
                    var ctx = canvas.getContext("2d");
                    canvas.width = img.width;
                    canvas.height = img.height;
                    // draw image to canvas
                    ctx.drawImage(img, 0, 0);
                    // get image data
                    var pixels = ctx.getImageData(0, 0, canvas.width, canvas.height);
                    // update input image info
                    this.inputImage.width = img.width;
                    this.inputImage.height = img.height;
                    // grayscaling
                    for (var y = 0; y < pixels.height; y++) {
                        for (var x = 0; x < pixels.width; x++) {
                            // sampling pixel(RGBA)
                            var pos = (y * pixels.width + x) * 4;
                            // RGB to grayscale color
                            var grayScalePixelColor = parseInt(
                                0.299 * pixels.data[pos] +
                                0.587 * pixels.data[pos + 1] +
                                0.114 * pixels.data[pos + 2], 10);
                            // update pixel data
                            pixels.data[pos]     = grayScalePixelColor; // R
                            pixels.data[pos + 1] = grayScalePixelColor; // G
                            pixels.data[pos + 2] = grayScalePixelColor; // B
                            pixels.data[pos + 3] = 255;                 // A
                        }
                    }
                    ctx.putImageData(pixels, 0, 0);
                    // update scale
                    let scale_ratio = this.outputAA.width / img.width;
                    this.canvasResize(canvas, scale_ratio);
                };
                img.src = imageURL;
            } catch (err) {
                console.error('grayscale: ', err.message);
                this.resultAA = err.message;
            }
        },
        inputImageLoad: function() {
            this.grayscale(this.inputImage.URL);
        },
        grayscaleImageLoad: async function() {
            try {
                while (this.modelRunning) {
                    this.modelInterrupt = true;
                    console.log('wait until stopped current process')
                    await this.sleep(16);
                }
                this.modelRunning = true;
                this.modelInterrupt = false;
                // reset status
                this.resultAA = '';
                this.outputAA.totalPercentage = 0;
                this.outputAA.linePercentage = 0;

                // get input data
                let dataTensor = undefined;
                let MaxlineNum = undefined;
                {
                    const canvas = document.createElement("canvas");
                    const ctx = canvas.getContext("2d");
                    const img = new Image();
                    img.src = this.grayscaleImage.URL;
                    canvas.width = img.width;
                    canvas.height = img.height;
                    ctx.drawImage(img, 0, 0);
                    const pixels = ctx.getImageData(0, 0, canvas.width, canvas.height);
                    // update grayscaleImage info
                    this.grayscaleImage.width = img.width;
                    this.grayscaleImage.height = img.height;
                    // convert ndarray (width * height * RGBA)
                    const dataSourceTensor = ndarray(new Float32Array(pixels.data), [pixels.width, pixels.height, 4]);
                    dataTensor = ndarray(new Float32Array(pixels.width * pixels.height), [pixels.width, pixels.height, 1]);
                    // pick grayscale data
                    ops.assign(dataTensor.pick(null, null, 0), dataSourceTensor.pick(null, null, 0))
                    // normalization (0.0 ~ 1.0)
                    ops.divseq(dataTensor, 255);
                    // calc AA line count
                    MaxlineNum = Math.floor((pixels.height - 48) / 18);
                    this.outputAA.maxLineNum = MaxlineNum;
                }

                // wait until model is ready
                await this.model.ready();

                const float32concat = function(first, second) {
                    let result = new Float32Array(first.length + second.length);
                    result.set(first);
                    result.set(second, first.length);
                    return result;
                };

                const PERLINE = 1 / MaxlineNum;
                const updateProgress = (currentLineNum, end) => {
                    this.outputAA.currentLineNum = currentLineNum;
                    this.outputAA.linePercentage =
                        Math.floor(end / dataTensor.shape[0] * 100);
                    this.outputAA.totalPercentage =
                        Math.floor(PERLINE * (currentLineNum + end / dataTensor.shape[0]) * 100);
                };

                // loop each line
                for (var i = 0; i < MaxlineNum; i++) {
                    // reshape lineImage
                    let lineImage =
                        dataTensor.data.slice(
                            (i * 18) * dataTensor.shape[0],
                            ((i * 18) + 64) * dataTensor.shape[0]);

                    let start = 0;
                    let end = 64;
                    let penalty = 1;

                    let width = 64;
                    while (end <= dataTensor.shape[0]) {
                        // set timeout
                        let timeout = this.setInterval(16);
                        // update line progress
                        updateProgress(i, end);
                        // reshape
                        let patch_data = new Float32Array();
                        for (var j = 0; j < 64; j++) {
                            let line_data = lineImage.slice(
                                    (j * dataTensor.shape[0]) + start,
                                    (j * dataTensor.shape[0]) + end);
                            patch_data = float32concat(patch_data, line_data);
                        }
                        let patch = ndarray(new Float32Array(patch_data), [64, 64]);
                        // predict
                        const inputData = {
                            'input_1': patch.data
                        };
                        let y = await this.model.predict(inputData);
                        y = ndarray(y.dense_1);

                        if (penalty==1) {
                           y.set(1, 0);
                        }

                        let predict = ops.argmax(y);
                        let char = this.charListFile[predict][0];
                        let char_width = this.charListFile[predict][1];

                        this.resultAA += char;

                        start += char_width;
                        end += char_width;

                        if (predict==1) {
                            penalty = 1;
                        } else {
                            penalty = 0;
                        }
                        if (this.modelInterrupt) {
                            this.modelRunning = false;
                            return;
                        }

                        // update preview image
                        this.updatePreviewLineImage(lineImage, dataTensor.shape[0], 64, start);
                        this.updatePreviewPatchImage(patch.data);
                        // keep frame interval (16 msec)
                        await timeout;
                    }
                    this.resultAA += '\n';
                }
                // finish
                this.outputAA.currentLineNum = MaxlineNum;
                this.outputAA.linePercentage = 100;
                this.outputAA.totalPercentage = 100;
                this.modelRunning = false;
            } catch (err) {
                this.modelRunning = false;
                console.error('model: ', err.message);
                this.resultAA = err.message;
            }
        },
        updatePreviewLineImage: function(data, width, height, pos_x) {
            // update canvas and info
            this.previewLineImage.width = width;
            this.previewLineImage.height = height;
            // update canvas
            const canvas = this.$refs.lineImageCanvas;
            const ctx = canvas.getContext("2d");
            const pixels = ctx.getImageData(0, 0, canvas.width, canvas.height);
            for (let y = 0; y < pixels.height; y++) {
                for (let x = 0; x < pixels.width; x++) {
                    const i = (y * 4) * pixels.width + x * 4;
                    const rgb = data[x + y * pixels.width] * 255;
                    pixels.data[i] = rgb;
                    pixels.data[i + 1] = rgb;
                    pixels.data[i + 2] = rgb;
                    pixels.data[i + 3] = 255;
                }
            }
            ctx.putImageData(pixels, 0, 0);
            // write red rectangle (Convert AA Area)
            ctx.beginPath()
            ctx.strokeStyle = 'red';
            ctx.strokeRect(24, 24, width - (24 * 2), 16);
            // write blue rectangle (Patch Image Area)
            ctx.beginPath()
            ctx.strokeStyle = 'blue';
            ctx.strokeRect(pos_x, 0, 64, 64);
            ctx.strokeRect(pos_x + 24, 24, 16, 16);
        },
        updatePreviewPatchImage: function(data) {
            const canvas = this.$refs.patchImageCanvas;
            const ctx = canvas.getContext("2d");
            const pixels = ctx.getImageData(0, 0, canvas.width, canvas.height);
            for (let y = 0; y < pixels.height; y++) {
                for (let x = 0; x < pixels.width; x++) {
                    const i = (y * 4) * pixels.width + x * 4;
                    const rgb = data[x + y * pixels.width] * 255;
                    pixels.data[i] = rgb;
                    pixels.data[i + 1] = rgb;
                    pixels.data[i + 2] = rgb;
                    pixels.data[i + 3] = 255;
                }
            }
            ctx.putImageData(pixels, 0, 0);
            // write red rectangle (convert AA area)
            ctx.beginPath()
            ctx.strokeStyle = 'red';
            ctx.strokeRect(24, 24, 16, 16);
        },
        setInterval: async function (ms) {
            return new Promise(resolve => setTimeout(resolve, ms));
        }
    },
    computed: {
        loadingProgress: function() {
            return this.model.getLoadingProgress();
        },
        progressMessage: function() {
            if (this.outputAA.totalPercentage == 100) {
                return 'Complete!';
            } else {
                return this.outputAA.totalPercentage + '%';
            }
        }
    }
})<|endoftext|>


<commit_before>import Holiday from '../models/holiday.model';

function load(req, res, next, id) {
    Holiday.get(id)
        .then((holiday) => {
            req.holiday = holiday;
            next();
            return null;
        })
        .catch(e => next(e));
}

function get(req, res) {
    return res.json(req.holiday);
}

function create(req, res, next) {
    const holiday = new Holiday({
        name: req.body.name,
        description: req.body.description,
        date: req.body.date,
    });

    holiday.save()
        .then(savedHoliday => res.json(savedHoliday))
        .catch(e => next(e));
}

function update(req, res, next) {
    const holiday = req.holiday;
    holiday.name = req.body.name;
    holiday.description = req.body.description;
    holiday.date = req.body.date;

    holiday.save()
        .then(savedHoliday => res.json(savedHoliday))
        .catch(e => next(e));
}

function list(req, res, next) {
    const { limit = 50, skip = 0 } = req.query;
    Holiday.list({ limit, skip })
        .then(holidays => res.json(holidays))
        .catch(e => next(e));
}

function remove(req, res, next) {
    const holiday = req.holiday;
    holiday.remove()
        .then(deletedHoliday => res.json(deletedHoliday))
        .catch(e => next(e));
}

export default { load, get, create, update, list, remove };
<commit_msg>add audit logs for legal holidays create/update/delete<commit_after>import Holiday from '../models/holiday.model';
import Audit from '../models/audit.model';

function load(req, res, next, id) {
    Holiday.get(id)
        .then((holiday) => {
            req.holiday = holiday;
            next();
            return null;
        })
        .catch(e => next(e));
}

function get(req, res) {
    return res.json(req.holiday);
}

function create(req, res, next) {
    const holiday = new Holiday({
        name: req.body.name,
        description: req.body.description,
        date: req.body.date,
    });

    holiday.save()
        .then(async savedHoliday => {
            const audit = new Audit({
                author: req.token.id,
                details: `Created Holiday: ${savedHoliday.name}|${savedHoliday.description}(${savedHoliday.date})`
            });
            await audit.save();
            return res.json(savedHoliday);
        })
        .catch(e => next(e));
}

function update(req, res, next) {
    const holiday = req.holiday;
    holiday.name = req.body.name;
    holiday.description = req.body.description;
    holiday.date = req.body.date;

    holiday.save()
        .then(async savedHoliday => {
            const audit = new Audit({
                author: req.token.id,
                details: `Updated Holiday:
                    ${req.body.name}|${req.body.description}|${req.body.date} with
                    ${savedHoliday.name}|${savedHoliday.description}(${savedHoliday.date})`
            });
            await audit.save();
            return res.json(savedHoliday);
        })
        .catch(e => next(e));
}

function list(req, res, next) {
    const { limit = 50, skip = 0 } = req.query;
    Holiday.list({ limit, skip })
        .then(holidays => res.json(holidays))
        .catch(e => next(e));
}

function remove(req, res, next) {
    const holiday = req.holiday;
    holiday.remove()
        .then(async deletedHoliday => {
            const audit = new Audit({
                author: req.token.id,
                details: `Deleted Holiday:
                    ${deletedHoliday.name}|${deletedHoliday.description}(${deletedHoliday.date})`
            });
            await audit.save();
            return res.json(deletedHoliday);
        })
        .catch(e => next(e));
}

export default { load, get, create, update, list, remove };
<|endoftext|>


<commit_before>const ghostBookshelf = require('./base');
const uuid = require('uuid');
const _ = require('lodash');
const config = require('../../shared/config');
const crypto = require('crypto');

const Member = ghostBookshelf.Model.extend({
    tableName: 'members',

    defaults() {
        return {
            status: 'free',
            subscribed: true,
            uuid: uuid.v4(),
            email_count: 0,
            email_opened_count: 0
        };
    },

    relationships: ['products', 'labels', 'stripeCustomers', 'email_recipients'],

    // do not delete email_recipients records when a member is destroyed. Recipient
    // records are used for analytics and historical records
    relationshipConfig: {
        email_recipients: {
            destroyRelated: false
        }
    },

    relationshipBelongsTo: {
        products: 'products',
        labels: 'labels',
        stripeCustomers: 'members_stripe_customers',
        email_recipients: 'email_recipients'
    },

    products() {
        return this.belongsToMany('Product', 'members_products', 'member_id', 'product_id')
            .withPivot('sort_order')
            .query('orderBy', 'sort_order', 'ASC')
            .query((qb) => {
                // avoids bookshelf adding a `DISTINCT` to the query
                // we know the result set will already be unique and DISTINCT hurts query performance
                qb.columns('products.*');
            });
    },

    labels: function labels() {
        return this.belongsToMany('Label', 'members_labels', 'member_id', 'label_id')
            .withPivot('sort_order')
            .query('orderBy', 'sort_order', 'ASC')
            .query((qb) => {
                // avoids bookshelf adding a `DISTINCT` to the query
                // we know the result set will already be unique and DISTINCT hurts query performance
                qb.columns('labels.*');
            });
    },

    stripeCustomers() {
        return this.hasMany('MemberStripeCustomer', 'member_id', 'id');
    },

    stripeSubscriptions() {
        return this.belongsToMany(
            'StripeCustomerSubscription',
            'members_stripe_customers',
            'member_id',
            'customer_id',
            'id',
            'customer_id'
        );
    },

    email_recipients() {
        return this.hasMany('EmailRecipient', 'member_id', 'id');
    },

    serialize(options) {
        const defaultSerializedObject = ghostBookshelf.Model.prototype.serialize.call(this, options);

        if (defaultSerializedObject.stripeSubscriptions) {
            defaultSerializedObject.subscriptions = defaultSerializedObject.stripeSubscriptions;
            delete defaultSerializedObject.stripeSubscriptions;
        }

        return defaultSerializedObject;
    },

    emitChange: function emitChange(event, options) {
        const eventToTrigger = 'member' + '.' + event;
        ghostBookshelf.Model.prototype.emitChange.bind(this)(this, eventToTrigger, options);
    },

    onCreated: function onCreated(model, attrs, options) {
        ghostBookshelf.Model.prototype.onCreated.apply(this, arguments);

        model.emitChange('added', options);
    },

    onUpdated: function onUpdated(model, attrs, options) {
        ghostBookshelf.Model.prototype.onUpdated.apply(this, arguments);

        model.emitChange('edited', options);
    },

    onDestroyed: function onDestroyed(model, options) {
        ghostBookshelf.Model.prototype.onDestroyed.apply(this, arguments);

        model.emitChange('deleted', options);
    },

    onDestroying: function onDestroyed(model) {
        ghostBookshelf.Model.prototype.onDestroying.apply(this, arguments);

        this.handleAttachedModels(model);
    },

    onSaving: function onSaving(model, attr, options) {
        let labelsToSave = [];

        if (_.isUndefined(this.get('labels'))) {
            this.unset('labels');
            return;
        }

        // CASE: detect lowercase/uppercase label slugs
        if (!_.isUndefined(this.get('labels')) && !_.isNull(this.get('labels'))) {
            labelsToSave = [];

            //  and deduplicate upper/lowercase tags
            _.each(this.get('labels'), function each(item) {
                item.name = item.name && item.name.trim();
                for (let i = 0; i < labelsToSave.length; i = i + 1) {
                    if (labelsToSave[i].name && item.name && labelsToSave[i].name.toLocaleLowerCase() === item.name.toLocaleLowerCase()) {
                        return;
                    }
                }

                labelsToSave.push(item);
            });

            this.set('labels', labelsToSave);
        }

        this.handleAttachedModels(model);

        // CASE: Detect existing labels with same case-insensitive name and replace
        return ghostBookshelf.model('Label')
            .findAll(Object.assign({
                columns: ['id', 'name']
            }, _.pick(options, 'transacting')))
            .then((labels) => {
                labelsToSave.forEach((label) => {
                    let existingLabel = labels.find((lab) => {
                        return label.name.toLowerCase() === lab.get('name').toLowerCase();
                    });
                    label.name = (existingLabel && existingLabel.get('name')) || label.name;
                });

                model.set('labels', labelsToSave);
            });
    },

    handleAttachedModels: function handleAttachedModels(model) {
        /**
         * @NOTE:
         * Bookshelf only exposes the object that is being detached on `detaching`.
         * For the reason above, `detached` handler is using the scope of `detaching`
         * to access the models that are not present in `detached`.
         */
        model.related('labels').once('detaching', function onDetaching(collection, label) {
            model.related('labels').once('detached', function onDetached(detachedCollection, response, options) {
                label.emitChange('detached', options);
                model.emitChange('label.detached', options);
            });
        });

        model.related('labels').once('attaching', function onDetaching(collection, labels) {
            model.related('labels').once('attached', function onDetached(detachedCollection, response, options) {
                labels.forEach((label) => {
                    label.emitChange('attached', options);
                    model.emitChange('label.attached', options);
                });
            });
        });
    },

    /**
     * The base model keeps only the columns, which are defined in the schema.
     * We have to add the relations on top, otherwise bookshelf-relations
     * has no access to the nested relations, which should be updated.
     */
    permittedAttributes: function permittedAttributes() {
        let filteredKeys = ghostBookshelf.Model.prototype.permittedAttributes.apply(this, arguments);

        this.relationships.forEach((key) => {
            filteredKeys.push(key);
        });

        return filteredKeys;
    },

    /**
     * We have to ensure consistency. If you listen on model events (e.g. `member.added`), you can expect that you always
     * receive all fields including relations. Otherwise you can't rely on a consistent flow. And we want to avoid
     * that event listeners have to re-fetch a resource. This function is used in the context of inserting
     * and updating resources. We won't return the relations by default for now.
     */
    defaultRelations: function defaultRelations(methodName, options) {
        if (['edit', 'add', 'destroy'].indexOf(methodName) !== -1) {
            options.withRelated = _.union(['labels'], options.withRelated || []);
        }

        return options;
    },

    searchQuery: function searchQuery(queryBuilder, query) {
        queryBuilder.where('members.name', 'like', `%${query}%`);
        queryBuilder.orWhere('members.email', 'like', `%${query}%`);
    },

    orderRawQuery(field, direction) {
        if (field === 'email_open_rate') {
            return {
                orderByRaw: `members.email_open_rate IS NOT NULL DESC, members.email_open_rate ${direction}`
            };
        }
    },

    toJSON(unfilteredOptions) {
        const options = Member.filterOptions(unfilteredOptions, 'toJSON');
        const attrs = ghostBookshelf.Model.prototype.toJSON.call(this, options);

        // Inject a computed avatar url. Uses gravatar's default ?d= query param
        // to serve a blank image if there is no gravatar for the member's email.
        // Will not use gravatar if privacy.useGravatar is false in config
        attrs.avatar_image = null;
        if (attrs.email && !config.isPrivacyDisabled('useGravatar')) {
            const emailHash = crypto.createHash('md5').update(attrs.email.toLowerCase().trim()).digest('hex');
            attrs.avatar_image = `https://gravatar.com/avatar/${emailHash}?s=250&d=blank`;
        }

        return attrs;
    }
}, {
    /**
     * Returns an array of keys permitted in a method's `options` hash, depending on the current method.
     * @param {String} methodName The name of the method to check valid options for.
     * @return {Array} Keys allowed in the `options` hash of the model's method.
     */
    permittedOptions: function permittedOptions(methodName) {
        let options = ghostBookshelf.Model.permittedOptions.call(this, methodName);

        if (['findPage', 'findAll'].includes(methodName)) {
            options = options.concat(['search']);
        }

        return options;
    },

    add(data, unfilteredOptions = {}) {
        if (!unfilteredOptions.transacting) {
            return ghostBookshelf.transaction((transacting) => {
                return this.add(data, Object.assign({transacting}, unfilteredOptions));
            });
        }
        return ghostBookshelf.Model.add.call(this, data, unfilteredOptions);
    },

    edit(data, unfilteredOptions = {}) {
        if (!unfilteredOptions.transacting) {
            return ghostBookshelf.transaction((transacting) => {
                return this.edit(data, Object.assign({transacting}, unfilteredOptions));
            });
        }
        return ghostBookshelf.Model.edit.call(this, data, unfilteredOptions);
    },

    destroy(unfilteredOptions = {}) {
        if (!unfilteredOptions.transacting) {
            return ghostBookshelf.transaction((transacting) => {
                return this.destroy(Object.assign({transacting}, unfilteredOptions));
            });
        }
        return ghostBookshelf.Model.destroy.call(this, unfilteredOptions);
    }
});

const Members = ghostBookshelf.Collection.extend({
    model: Member
});

module.exports = {
    Member: ghostBookshelf.model('Member', Member),
    Members: ghostBookshelf.collection('Members', Members)
};
<commit_msg>🐛 Fixed error on saving member with existing label<commit_after>const ghostBookshelf = require('./base');
const uuid = require('uuid');
const _ = require('lodash');
const config = require('../../shared/config');
const crypto = require('crypto');

const Member = ghostBookshelf.Model.extend({
    tableName: 'members',

    defaults() {
        return {
            status: 'free',
            subscribed: true,
            uuid: uuid.v4(),
            email_count: 0,
            email_opened_count: 0
        };
    },

    relationships: ['products', 'labels', 'stripeCustomers', 'email_recipients'],

    // do not delete email_recipients records when a member is destroyed. Recipient
    // records are used for analytics and historical records
    relationshipConfig: {
        email_recipients: {
            destroyRelated: false
        }
    },

    relationshipBelongsTo: {
        products: 'products',
        labels: 'labels',
        stripeCustomers: 'members_stripe_customers',
        email_recipients: 'email_recipients'
    },

    products() {
        return this.belongsToMany('Product', 'members_products', 'member_id', 'product_id')
            .withPivot('sort_order')
            .query('orderBy', 'sort_order', 'ASC')
            .query((qb) => {
                // avoids bookshelf adding a `DISTINCT` to the query
                // we know the result set will already be unique and DISTINCT hurts query performance
                qb.columns('products.*');
            });
    },

    labels: function labels() {
        return this.belongsToMany('Label', 'members_labels', 'member_id', 'label_id')
            .withPivot('sort_order')
            .query('orderBy', 'sort_order', 'ASC')
            .query((qb) => {
                // avoids bookshelf adding a `DISTINCT` to the query
                // we know the result set will already be unique and DISTINCT hurts query performance
                qb.columns('labels.*');
            });
    },

    stripeCustomers() {
        return this.hasMany('MemberStripeCustomer', 'member_id', 'id');
    },

    stripeSubscriptions() {
        return this.belongsToMany(
            'StripeCustomerSubscription',
            'members_stripe_customers',
            'member_id',
            'customer_id',
            'id',
            'customer_id'
        );
    },

    email_recipients() {
        return this.hasMany('EmailRecipient', 'member_id', 'id');
    },

    serialize(options) {
        const defaultSerializedObject = ghostBookshelf.Model.prototype.serialize.call(this, options);

        if (defaultSerializedObject.stripeSubscriptions) {
            defaultSerializedObject.subscriptions = defaultSerializedObject.stripeSubscriptions;
            delete defaultSerializedObject.stripeSubscriptions;
        }

        return defaultSerializedObject;
    },

    emitChange: function emitChange(event, options) {
        const eventToTrigger = 'member' + '.' + event;
        ghostBookshelf.Model.prototype.emitChange.bind(this)(this, eventToTrigger, options);
    },

    onCreated: function onCreated(model, attrs, options) {
        ghostBookshelf.Model.prototype.onCreated.apply(this, arguments);

        model.emitChange('added', options);
    },

    onUpdated: function onUpdated(model, attrs, options) {
        ghostBookshelf.Model.prototype.onUpdated.apply(this, arguments);

        model.emitChange('edited', options);
    },

    onDestroyed: function onDestroyed(model, options) {
        ghostBookshelf.Model.prototype.onDestroyed.apply(this, arguments);

        model.emitChange('deleted', options);
    },

    onDestroying: function onDestroyed(model) {
        ghostBookshelf.Model.prototype.onDestroying.apply(this, arguments);

        this.handleAttachedModels(model);
    },

    onSaving: function onSaving(model, attr, options) {
        let labelsToSave = [];

        if (_.isUndefined(this.get('labels'))) {
            this.unset('labels');
            return;
        }

        // CASE: detect lowercase/uppercase label slugs
        if (!_.isUndefined(this.get('labels')) && !_.isNull(this.get('labels'))) {
            labelsToSave = [];

            //  and deduplicate upper/lowercase tags
            _.each(this.get('labels'), function each(item) {
                item.name = item.name && item.name.trim();
                for (let i = 0; i < labelsToSave.length; i = i + 1) {
                    if (labelsToSave[i].name && item.name && labelsToSave[i].name.toLocaleLowerCase() === item.name.toLocaleLowerCase()) {
                        return;
                    }
                }

                labelsToSave.push(item);
            });

            this.set('labels', labelsToSave);
        }

        this.handleAttachedModels(model);

        // CASE: Detect existing labels with same case-insensitive name and replace
        return ghostBookshelf.model('Label')
            .findAll(Object.assign({
                columns: ['id', 'name']
            }, _.pick(options, 'transacting')))
            .then((labels) => {
                labelsToSave.forEach((label) => {
                    let existingLabel = labels.find((lab) => {
                        return label.name.toLowerCase() === lab.get('name').toLowerCase();
                    });
                    label.name = (existingLabel && existingLabel.get('name')) || label.name;
                    label.id = (existingLabel && existingLabel.id) || label.id;
                });

                model.set('labels', labelsToSave);
            });
    },

    handleAttachedModels: function handleAttachedModels(model) {
        /**
         * @NOTE:
         * Bookshelf only exposes the object that is being detached on `detaching`.
         * For the reason above, `detached` handler is using the scope of `detaching`
         * to access the models that are not present in `detached`.
         */
        model.related('labels').once('detaching', function onDetaching(collection, label) {
            model.related('labels').once('detached', function onDetached(detachedCollection, response, options) {
                label.emitChange('detached', options);
                model.emitChange('label.detached', options);
            });
        });

        model.related('labels').once('attaching', function onDetaching(collection, labels) {
            model.related('labels').once('attached', function onDetached(detachedCollection, response, options) {
                labels.forEach((label) => {
                    label.emitChange('attached', options);
                    model.emitChange('label.attached', options);
                });
            });
        });
    },

    /**
     * The base model keeps only the columns, which are defined in the schema.
     * We have to add the relations on top, otherwise bookshelf-relations
     * has no access to the nested relations, which should be updated.
     */
    permittedAttributes: function permittedAttributes() {
        let filteredKeys = ghostBookshelf.Model.prototype.permittedAttributes.apply(this, arguments);

        this.relationships.forEach((key) => {
            filteredKeys.push(key);
        });

        return filteredKeys;
    },

    /**
     * We have to ensure consistency. If you listen on model events (e.g. `member.added`), you can expect that you always
     * receive all fields including relations. Otherwise you can't rely on a consistent flow. And we want to avoid
     * that event listeners have to re-fetch a resource. This function is used in the context of inserting
     * and updating resources. We won't return the relations by default for now.
     */
    defaultRelations: function defaultRelations(methodName, options) {
        if (['edit', 'add', 'destroy'].indexOf(methodName) !== -1) {
            options.withRelated = _.union(['labels'], options.withRelated || []);
        }

        return options;
    },

    searchQuery: function searchQuery(queryBuilder, query) {
        queryBuilder.where('members.name', 'like', `%${query}%`);
        queryBuilder.orWhere('members.email', 'like', `%${query}%`);
    },

    orderRawQuery(field, direction) {
        if (field === 'email_open_rate') {
            return {
                orderByRaw: `members.email_open_rate IS NOT NULL DESC, members.email_open_rate ${direction}`
            };
        }
    },

    toJSON(unfilteredOptions) {
        const options = Member.filterOptions(unfilteredOptions, 'toJSON');
        const attrs = ghostBookshelf.Model.prototype.toJSON.call(this, options);

        // Inject a computed avatar url. Uses gravatar's default ?d= query param
        // to serve a blank image if there is no gravatar for the member's email.
        // Will not use gravatar if privacy.useGravatar is false in config
        attrs.avatar_image = null;
        if (attrs.email && !config.isPrivacyDisabled('useGravatar')) {
            const emailHash = crypto.createHash('md5').update(attrs.email.toLowerCase().trim()).digest('hex');
            attrs.avatar_image = `https://gravatar.com/avatar/${emailHash}?s=250&d=blank`;
        }

        return attrs;
    }
}, {
    /**
     * Returns an array of keys permitted in a method's `options` hash, depending on the current method.
     * @param {String} methodName The name of the method to check valid options for.
     * @return {Array} Keys allowed in the `options` hash of the model's method.
     */
    permittedOptions: function permittedOptions(methodName) {
        let options = ghostBookshelf.Model.permittedOptions.call(this, methodName);

        if (['findPage', 'findAll'].includes(methodName)) {
            options = options.concat(['search']);
        }

        return options;
    },

    add(data, unfilteredOptions = {}) {
        if (!unfilteredOptions.transacting) {
            return ghostBookshelf.transaction((transacting) => {
                return this.add(data, Object.assign({transacting}, unfilteredOptions));
            });
        }
        return ghostBookshelf.Model.add.call(this, data, unfilteredOptions);
    },

    edit(data, unfilteredOptions = {}) {
        if (!unfilteredOptions.transacting) {
            return ghostBookshelf.transaction((transacting) => {
                return this.edit(data, Object.assign({transacting}, unfilteredOptions));
            });
        }
        return ghostBookshelf.Model.edit.call(this, data, unfilteredOptions);
    },

    destroy(unfilteredOptions = {}) {
        if (!unfilteredOptions.transacting) {
            return ghostBookshelf.transaction((transacting) => {
                return this.destroy(Object.assign({transacting}, unfilteredOptions));
            });
        }
        return ghostBookshelf.Model.destroy.call(this, unfilteredOptions);
    }
});

const Members = ghostBookshelf.Collection.extend({
    model: Member
});

module.exports = {
    Member: ghostBookshelf.model('Member', Member),
    Members: ghostBookshelf.collection('Members', Members)
};
<|endoftext|>


<commit_before>// Creates memoized soap client using provided wsdl for endpoint url.
// Promisifies each wsdl defined method on client, eg. for client.connect method it provides
// client.connectP promisified methos.

'use strict';

var memoize          = require('memoizee')
  , promisify        = require('deferred').promisify
  , soap             = require('soap')
  , debug            = require('debug-ext')('soap')
  , assign           = require('es5-ext/object/assign')
  , env              = require('mano').env
  , soapClientOptions = env.soapClientOptions

  , createSoapClient = promisify(soap.createClient);

var getSoapClientImpl = memoize(function (wsdlUrl/*, options*/) {
	var options = Object(arguments[1]);
	if (soapClientOptions) {
		options = assign({}, soapClientOptions, options);
	}
	debug('creating soap client (%s)', options.endpoint);

	return createSoapClient(wsdlUrl, options)(function (client) {
		var services = client.wsdl.definitions.services
		  , ports, methods, method, promisifiedMethodName;

		Object.getOwnPropertyNames(services).forEach(function (serviceName) {
			ports = services[serviceName].ports;

			Object.getOwnPropertyNames(ports).forEach(function (portName) {
				methods = ports[portName].binding.methods;

				Object.getOwnPropertyNames(methods).forEach(function (methodName) {
					method = client[methodName];
					promisifiedMethodName = methodName + 'P';

					if (method && !client[promisifiedMethodName]) {
						client[promisifiedMethodName] = promisify(method);
					}
				});
			});
		});

		return client;
	});
}, { primitive: true });

module.exports = function (wsdlUrl, soapUrl/*, options*/) {
	var options = Object(arguments[2]);
	options.endpoint = soapUrl;
	return getSoapClientImpl(wsdlUrl, options).aside(null, function (err) {
		getSoapClientImpl.clear(wsdlUrl, options);
	});
};
<commit_msg>#1930 avoided failing test<commit_after>// Creates memoized soap client using provided wsdl for endpoint url.
// Promisifies each wsdl defined method on client, eg. for client.connect method it provides
// client.connectP promisified methos.

'use strict';

var memoize          = require('memoizee')
  , promisify        = require('deferred').promisify
  , soap             = require('soap')
  , debug            = require('debug-ext')('soap')
  , assign           = require('es5-ext/object/assign')
  , env              = require('mano').env
  , soapClientOptions = env && env.soapClientOptions

  , createSoapClient = promisify(soap.createClient);

var getSoapClientImpl = memoize(function (wsdlUrl/*, options*/) {
	var options = Object(arguments[1]);
	if (soapClientOptions) {
		options = assign({}, soapClientOptions, options);
	}
	debug('creating soap client (%s)', options.endpoint);

	return createSoapClient(wsdlUrl, options)(function (client) {
		var services = client.wsdl.definitions.services
		  , ports, methods, method, promisifiedMethodName;

		Object.getOwnPropertyNames(services).forEach(function (serviceName) {
			ports = services[serviceName].ports;

			Object.getOwnPropertyNames(ports).forEach(function (portName) {
				methods = ports[portName].binding.methods;

				Object.getOwnPropertyNames(methods).forEach(function (methodName) {
					method = client[methodName];
					promisifiedMethodName = methodName + 'P';

					if (method && !client[promisifiedMethodName]) {
						client[promisifiedMethodName] = promisify(method);
					}
				});
			});
		});

		return client;
	});
}, { primitive: true });

module.exports = function (wsdlUrl, soapUrl/*, options*/) {
	var options = Object(arguments[2]);
	options.endpoint = soapUrl;
	return getSoapClientImpl(wsdlUrl, options).aside(null, function (err) {
		getSoapClientImpl.clear(wsdlUrl, options);
	});
};
<|endoftext|>


<commit_before>var debug = require('debug');
var fs = require('fs');
var passport = require('passport');
var config = require('config');

//My functions
var functions = require('./functions');

module.exports = function(app) {

app.get('/query',function(request,response) {
	var query = request.query.query;
	if (query == undefined)
		{
			response.redirect(301, '/sparql');
		} else {
			response.redirect(301, '/sparql?query=' + query);
	}
});

app.get('/sparql',function(request,response) {
	var query = request.query.query;
	debug(query);
	if (query == undefined)
		{
			response.status(400).send("With GET, you must pass your SPARQL query in the 'query' parameter");
		} else {
			functions.getQuery(request, response, query);
	}
});

app.post('/query',function(request,response) {
	response.redirect(301, '/sparql');
});

app.post('/sparql',function(request,response) {
	var query = '';

   	request.on('data', function (data) {
     	 // Append data.
      	query += data;
  	});

   	request.on('end', function () {
      	if (query == undefined) {
			response.status(400).send("With POST, you must pass your SPARQL query as data.");
		} else {
			functions.getQuery(request, response, query);
	}
   	});

});

app.get('/:type(tables|graphs)/:name([\\w-]+):dot(\.)?:extension(\\w+)?',function(request,response) {
	var sparqlPath = "public/" + request.params.type;
	var sparqlFiles = fs.readdirSync(sparqlPath);
	var name = request.params.name;
	var foundQuery = false;

	for (key in sparqlFiles) {

		//If the URL fragment matches a query file name, return the query text
		if (functions.stringBefore(sparqlFiles[key],'.') == name) {
			fs.readFile(sparqlPath + '/' + sparqlFiles[key],'utf8', function (err, data) {
	  			if (err) throw err;
	  			if (request.params.extension) {
					var foundExtension = false;
					for (var extension in config.get('typeByExtension.' + request.params.type)) {
						if (extension == request.params.extension) {
							request.headers.accept = config.get('typeByExtension.' + request.params.type + '.' + extension);
							foundExtension = true;
						}
					}
					if (foundExtension == false) {
						var extensions = '';
						for (var i = 0; config.get('typeByExtension.' + request.params.type).length; i++) {
						extensions += config.get('typeByExtension.' + request.params.type) + " ";
					}
						response.status(400).send(request.path + ': extension "'
							+ request.params.extension + '" not valid for ' + [request.params.type] + '. Authorized extensions: '
							+ extensions + '\n');
					} else {
						functions.getQuery(request, response, data);
					}
	  			} else {
	  				functions.getQuery(request, response, data);
	  			}
			});
			foundQuery = true;
		}
	};
	//Otherwise, return 404
	if (foundQuery == false) {
		response.status(404).send(request.params.type + "/" + request.params.name + ': This query does not exist.\n');
	}
});

app.post('/:type(tables|graphs)/:name([\\w-]+)',
passport.authenticate('basic', { session: false }),
function(request,response) {
	var sparqlPath = "public/" + request.params.type;
	var name = request.params.name;
	var filepath = sparqlPath + '/' + name + '.rq';
	var query = '';
	var fileExists = false;

	request.on('data', function (data) {
     	 // Append data.
      	query += data;
  	});

  	request.on('end', function () {
      	if (query == undefined) {
			response.status(400)
			.send("You must pass your SPARQL query as data to create or modify a query.\n");
		} else if (query.length > config.get('app.maxQueryLength')) {
			response.status(413)
			.send("Query text over " + config.get('app.maxQueryLength') + " bytes is not allowed.\n");
		}
		else {
			fs.stat(filepath,function(err, stats){
				if (!err) {
					fileExists = true;
				}
			});
			fs.writeFile(filepath,query,'utf8', function (err) {
				if (err) {
					console.log(err);
					response.status(500).send("There was an error writing the query.\n");
				} else {
					if (fileExists === true) {
						response.status(200)
						.send("The query /" + request.params.type + "/" + name + " was updated.\n");
					} else {
						response.status(201)
						.send("The query /" + request.params.type + "/" + name + " was created.\n");
					}
				}
			});
	}
   	});
});

app.delete('/:type(tables|graphs)/:name([\\w-]+)',
passport.authenticate('basic', { session: false }),
function(request,response) {
	var sparqlPath = "public/" + request.params.type;
	var name = request.params.name;
	var filepath = sparqlPath + '/' + name + '.rq';

	fs.stat(filepath,function(err, stats){
		if (err) {
			response.status(404).send(name + ": This query doesn't exist.\n");
		} else {
			fs.unlink(filepath, function (err) {
				if (err) {
					console.log(err);
					response.status(500).send(name + ": There was an error deleting the query.\n");
				} else {
					response.status(200).send(name + ": The query was deleted.\n");
				}
			});
		}
	});
});

app.post('/', function(request,response) {
	if (request.get('Content-Type') == "application/coffee-pot-command") {
		response.sendStatus(418);
	} else {
		response.sendStatus(405);
	}
});

app.get('/',function(request,response) {
	response.sendStatus(200);
});

}<commit_msg>Added refactored routes to add UPDATE endpoint #20<commit_after>var debug = require('debug');
var fs = require('fs');
var passport = require('passport');
var config = require('config');

//My functions
var functions = require('./functions');

module.exports = function(app) {

app.get('/query',function(request,response) {
	var query = request.query.query;
	if (query == undefined)
		{
			response.redirect(301, '/sparql');
		} else {
			response.redirect(301, '/sparql?query=' + query);
	}
});

app.get('/sparql',function(request,response) {
	var query = request.query.query;
	debug(query);
	if (query == undefined)
		{
			response.status(400).send("With GET, you must pass your SPARQL query in the 'query' parameter");
		} else {
			request.queryText = query;
			functions.sparqlQuery(request, response);
	}
});

app.post('/query',function(request,response) {
	response.redirect(301, '/sparql');
});

app.post('/sparql',function(request,response) {
	var query = '';

   	request.on('data', function (data) {
     	 // Append data.
      	query += data;
  	});

   	request.on('end', function () {
      	if (query == undefined) {
			response.status(400).send("With POST, you must pass your SPARQL query as data.");
		} else {
			request.queryText = query;
			functions.sparqlQuery(request, response);
	}
   	});

});

app.get('/update/*',
passport.authenticate('basic', { session: false }),
function(request,response, next) {
	console.log("UPDATE query.");
	next();
});

app.get('/:type(tables|graphs|update)/:name([\\w-]+):dot(\.)?:extension(\\w+)?',function(request,response, next) {
	var sparqlPath = "public/" + request.params.type;
	var sparqlFiles = fs.readdirSync(sparqlPath);
	var name = request.params.name;
	var foundQuery = false;

	for (key in sparqlFiles) {

		//If the URL fragment matches a query file name, return the query text
		if (functions.stringBefore(sparqlFiles[key],'.') == name) {
			fs.readFile(sparqlPath + '/' + sparqlFiles[key],'utf8', function (err, data) {
	  			if (err) {throw err} else {request.queryText = data};

	  			if (request.params.extension) {
					var foundExtension = false;
					for (var extension in config.get('typeByExtension.' + request.params.type)) {
						if (extension == request.params.extension) {
							request.headers.accept = config.get('typeByExtension.' + request.params.type + '.' + extension);
							foundExtension = true;
						}
					}
					if (foundExtension == false) {
						var extensions = '';
						for (var i = 0; config.get('typeByExtension.' + request.params.type).length; i++) {
						extensions += config.get('typeByExtension.' + request.params.type) + " ";
					}
						response.status(400).send(request.path + ': extension "'
							+ request.params.extension + '" not valid for ' + [request.params.type] + '. Authorized extensions: '
							+ extensions + '\n');
					} else {
						functions.sparqlQuery(request, response);
					}
	  			} else {
	  				functions.sparqlQuery(request, response);
	  			}
			});
			foundQuery = true;
		}
	};
	//Otherwise, return 404
	if (foundQuery == false) {
		response.status(404).send(request.params.type + "/" + request.params.name + ': This query does not exist.\n');
	}
});

app.post('/:type(tables|graphs)/:name([\\w-]+)',
passport.authenticate('basic', { session: false }),
function(request,response) {
	var sparqlPath = "public/" + request.params.type;
	var name = request.params.name;
	var filepath = sparqlPath + '/' + name + '.rq';
	var query = '';
	var fileExists = false;

	request.on('data', function (data) {
     	 // Append data.
      	query += data;
  	});

  	request.on('end', function () {
      	if (query == undefined) {
			response.status(400)
			.send("You must pass your SPARQL query as data to create or modify a query.\n");
		} else if (query.length > config.get('app.maxQueryLength')) {
			response.status(413)
			.send("Query text over " + config.get('app.maxQueryLength') + " bytes is not allowed.\n");
		}
		else {
			fs.stat(filepath,function(err, stats){
				if (!err) {
					fileExists = true;
				}
			});
			fs.writeFile(filepath,query,'utf8', function (err) {
				if (err) {
					console.log(err);
					response.status(500).send("There was an error writing the query.\n");
				} else {
					if (fileExists === true) {
						response.status(200)
						.send("The query /" + request.params.type + "/" + name + " was updated.\n");
					} else {
						response.status(201)
						.send("The query /" + request.params.type + "/" + name + " was created.\n");
					}
				}
			});
	}
   	});
});

app.delete('/:type(tables|graphs)/:name([\\w-]+)',
passport.authenticate('basic', { session: false }),
function(request,response) {
	var sparqlPath = "public/" + request.params.type;
	var name = request.params.name;
	var filepath = sparqlPath + '/' + name + '.rq';

	fs.stat(filepath,function(err, stats){
		if (err) {
			response.status(404).send(name + ": This query doesn't exist.\n");
		} else {
			fs.unlink(filepath, function (err) {
				if (err) {
					console.log(err);
					response.status(500).send(name + ": There was an error deleting the query.\n");
				} else {
					response.status(200).send(name + ": The query was deleted.\n");
				}
			});
		}
	});
});

app.post('/', function(request,response) {
	if (request.get('Content-Type') == "application/coffee-pot-command") {
		response.sendStatus(418);
	} else {
		response.sendStatus(405);
	}
});

app.get('/',function(request,response) {
	response.sendStatus(200);
});

}<|endoftext|>


<commit_before>/**
 * A Wysihtml RTE, properties:
 *
 * - label
 * - name
 * - value will be entered as value
 * - .. any other allowed by input
 *
 * @jsx React.DOM
 */
define(['react', 'build/Components/Panels/Page/Form/label.react', 'tinymcejq'], function (React, Label) {
    return React.createClass({
        getInstanceId: function () {
            if (!this.instanceId) {
                this.instanceId = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                    var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
                    return v.toString(16);
                });
            }
            return this.instanceId;
        },
        getIdentifier: function () {
            return 'tinymce-editor-' + this.getInstanceId();
        },
        componentDidMount: function () {

            // Tell tinyMCE that the dom has already loaded since it wasn't there when
            // the event triggered
            tinymce.dom.Event.domLoaded = true;

            //Actually initialize the editor
            tinymce.init({
                selector: '#' + this.getIdentifier(),
                theme: "modern",
                schema: "html5",
                height: 300,
                plugins: [
                    "advlist autolink link image lists charmap preview",
                    "searchreplace visualblocks visualchars code fullscreen insertdatetime media nonbreaking",
                    "save table contextmenu directionality textcolor"
                ],
                toolbar: "styleselect | bold italic underline | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | preview"
            });

            //Bind beforeSubmit event to do stuff with tinymce
            $(this.refs.textarea).closest('form').bind('beforeSubmit', function () {
                for (var i in tinymce.editors) {
                    try {
                        tinymce.editors[i].save();
                    } catch (e) {
                    }
                }
            });

        },
        componentWillUnmount: function () {
            tinymce.remove('#' + this.getIdentifier());
        },
        render: function () {

            var attributes = this.props.item.getAttributes();

            Object.merge(attributes, {
                id: this.getIdentifier(),
                type: 'textarea',
                ref: 'textarea',
                className: 'tinymce-editor'
            });

            return (
                React.DOM.div(null,
                    Label({item: this.props.item, name: this.props.item.getLabel()}),
                    React.DOM.textarea(attributes, this.props.item.getValue())
                )
            );
        }
    });

});<commit_msg>added inline styling for images in tinymce<commit_after>/**
 * A Wysihtml RTE, properties:
 *
 * - label
 * - name
 * - value will be entered as value
 * - .. any other allowed by input
 *
 * @jsx React.DOM
 */
define(['react', 'build/Components/Panels/Page/Form/label.react', 'tinymcejq'], function (React, Label) {
    return React.createClass({
        getInstanceId: function () {
            if (!this.instanceId) {
                this.instanceId = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                    var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
                    return v.toString(16);
                });
            }
            return this.instanceId;
        },
        getIdentifier: function () {
            return 'tinymce-editor-' + this.getInstanceId();
        },
        componentDidMount: function () {

            // Tell tinyMCE that the dom has already loaded since it wasn't there when
            // the event triggered
            tinymce.dom.Event.domLoaded = true;

            //Actually initialize the editor
            tinymce.init({
                selector: '#' + this.getIdentifier(),
                theme: "modern",
                schema: "html5",
                height: 300,
                content_style: "img{max-width: 100%; display: block;}",
                plugins: [
                    "advlist autolink link image lists charmap preview",
                    "searchreplace visualblocks visualchars code fullscreen insertdatetime media nonbreaking",
                    "save table contextmenu directionality textcolor"
                ],
                toolbar: "styleselect | bold italic underline | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | preview"
            });

            //Bind beforeSubmit event to do stuff with tinymce
            $(this.refs.textarea).closest('form').bind('beforeSubmit', function () {
                for (var i in tinymce.editors) {
                    try {
                        tinymce.editors[i].save();
                    } catch (e) {
                    }
                }
            });

        },
        componentWillUnmount: function () {
            tinymce.remove('#' + this.getIdentifier());
        },
        render: function () {

            var attributes = this.props.item.getAttributes();

            Object.merge(attributes, {
                id: this.getIdentifier(),
                type: 'textarea',
                ref: 'textarea',
                className: 'tinymce-editor'
            });

            return (
                React.DOM.div(null,
                    Label({item: this.props.item, name: this.props.item.getLabel()}),
                    React.DOM.textarea(attributes, this.props.item.getValue())
                )
            );
        }
    });

});<|endoftext|>


<commit_before><commit_msg>init player<commit_after>import YoutubePlayer from './youtube-player';

let yp = new YoutubePlayer();

console.log(yp);
<|endoftext|>


<commit_before>// # Upyun CDN support
// GhostChina.com

var _       = require('lodash'),
    express = require('express'),
    fs      = require('fs-extra'),
    path    = require('path'),
    util    = require('util'),
    utils   = require('../utils'),
    Promise = require('bluebird'),
    errors  = require('../errors'),
    config  = require('../config'),
    baseStore   = require('./base'),
    crypto  = require('crypto');
    upyunConfig = config.storage,
    UPYun   = require('upyun'),
    upyun   = new UPYun(upyunConfig.bucketname, upyunConfig.username, upyunConfig.password, 'v0');

function UpyunStore () {
}

util.inherits(UpyunStore, baseStore);

UpyunStore.prototype.save = function (image) {
    var md5sum = crypto.createHash('md5'),
        ext = path.extname(image.name),
        targetDirRoot = upyunConfig.root,
        targetFilename;

    var savedpath = path.join(config.paths.imagesPath, image.name);

    return Promise.promisify(fs.copy)(image.path, savedpath).then(function(){
        return Promise.promisify(fs.readFile)(savedpath);
    }).then(function(data){
        md5 = md5sum.update(data).digest('hex');

        targetFilename = path.join(targetDirRoot, md5.replace(/^(\w{1})(\w{2})(\w+)$/, '$1/$2/$3')) + ext;
        targetFilename = targetFilename.replace(/\\/g, '/');

        return Promise.promisify(upyun.uploadFile)(targetFilename, data, '', false, {});
    }).then(function(response) {
        console.log('Response code: ' + response.statusCode);
        // Remove temp file
        return Promise.promisify(fs.unlink)(savedpath);
    }).then(function () {
        // prefix + upyunFilename
        var fullUrl = upyunConfig.prefix + targetFilename;
        return fullUrl;
    }).catch(function (e) {
        errors.logError(e);
        return Promise.reject(e);
    });
};

UpyunStore.prototype.exists = function (filename) {
    return new Promise(function (resolve) {
        fs.exists(filename, function (exists) {
            resolve(exists);
        });
    });
};

UpyunStore.prototype.serve = function (){
    // For some reason send divides the max age number by 1000
    return express['static'](config.paths.imagesPath, {maxAge: utils.ONE_YEAR_MS});
};

module.exports = UpyunStore;<commit_msg>修正将upyun api promise化时少传入upyun对象<commit_after>// # Upyun CDN support
// GhostChina.com

var _       = require('lodash'),
    express = require('express'),
    fs      = require('fs-extra'),
    path    = require('path'),
    util    = require('util'),
    utils   = require('../utils'),
    Promise = require('bluebird'),
    errors  = require('../errors'),
    config  = require('../config'),
    baseStore   = require('./base'),
    crypto  = require('crypto');
    upyunConfig = config.storage,
    UPYun   = require('upyun'),
    upyun   = new UPYun(upyunConfig.bucketname, upyunConfig.username, upyunConfig.password, 'v0');

function UpyunStore () {
}

util.inherits(UpyunStore, baseStore);

UpyunStore.prototype.save = function (image) {
    var md5sum = crypto.createHash('md5'),
        ext = path.extname(image.name),
        targetDirRoot = upyunConfig.root,
        targetFilename;

    var savedpath = path.join(config.paths.imagesPath, image.name);

    return Promise.promisify(fs.copy)(image.path, savedpath).then(function(){
        return Promise.promisify(fs.readFile)(savedpath);
    }).then(function(data){
        md5 = md5sum.update(data).digest('hex');

        targetFilename = path.join(targetDirRoot, md5.replace(/^(\w{1})(\w{2})(\w+)$/, '$1/$2/$3')) + ext;
        targetFilename = targetFilename.replace(/\\/g, '/');

        return Promise.promisify(upyun.uploadFile, upyun)(targetFilename, data, '', false, {});
    }).then(function(response) {
        console.log('Response code: ' + response.statusCode);
        // Remove temp file
        return Promise.promisify(fs.unlink)(savedpath);
    }).then(function () {
        // prefix + upyunFilename
        var fullUrl = upyunConfig.prefix + targetFilename;
        return fullUrl;
    }).catch(function (e) {
        errors.logError(e);
        return Promise.reject(e);
    });
};

UpyunStore.prototype.exists = function (filename) {
    return new Promise(function (resolve) {
        fs.exists(filename, function (exists) {
            resolve(exists);
        });
    });
};

UpyunStore.prototype.serve = function (){
    // For some reason send divides the max age number by 1000
    return express['static'](config.paths.imagesPath, {maxAge: utils.ONE_YEAR_MS});
};

module.exports = UpyunStore;<|endoftext|>


<commit_before>'use strict';
const fs = require('fs');
const path = require('path');
const currentYear = (new Date()).getFullYear();

const members = require('./board.json');
Object.keys(members).forEach(i => {return members[i].id = i;});

const banners = fs.readdirSync(path.join(__dirname, '../static/img/banner')).map((item) => {
    return `/static/img/banner/${item}`;
});

const getBanners = () => {
    const items = [].concat(banners).sort(() => {return Math.random() * 2 - 1;});
    items.forEach((file, key) => {
        const show = key === 0 ? ' show' : '';
        items[key] = `<img src="${file}" class="banner-img${show}">`;
    });
    return items.join('\n');
};

module.exports = [
    {
        method: 'GET',
        path: '/favicon.ico',
        handler: {
            file: './static/favicon.ico'
        },
        config: {
            cache: {
                expiresIn: 86400000,
                privacy: 'public'
            }
        }
    },
    {
        method: 'GET',
        path: '/static/{path*}',
        handler: {
            directory: {
                path: './static',
                listing: false,
                index: false
            }
        }
    },
    {
        method: 'GET',
        path: '/',
        handler: (req, res) => {
            res.view('home', { title: 'home', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/register',
        handler: (req, res) => {
            res.redirect('https://hacksi2017.eventbrite.com');
        }
    },
    {
        method: 'GET',
        path: '/register/forms',
        handler: (req, res) => {
            res.view('register', { title: 'register', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/register/forms/pdf',
        handler: (req, res) => {
            res.file('./static/forms/HackSIAssumptionofRiskForm.pdf');
        }
    },
    {
        method: 'GET',
        path: '/volunteer',
        handler: (req, res) => {
            res.redirect('https://hacksi2017.eventbrite.com');
        }
    },
    {
        method: 'GET',
        path: '/cfp',
        handler: (req, res) => {
            res.redirect('http://goo.gl/forms/SUR5XDWcQWQt6ekf2');
        }
    },
    {
        method: 'GET',
        path: '/about',
        handler: (req, res) => {
            res.view('about', { title: 'about', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/board',
        handler: (req, res) => {
            const board = [];
            Object.keys(members).forEach(i => {
                board.push(members[i]);
            });
            board.splice(board.length - 1, 0, {}); //Insert empty spot to skip a grid
            res.view('board', { title: 'foundation board', currentYear: currentYear, members: board, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/board/{user}',
        handler: (req, res) => {
            const u = req.params.user;
            if (!members[u]) {
                return res.redirect('/board');
            }
            if (!members[u].bio) {
                members[u].bio = fs.readFileSync(path.join(__dirname, `../views/board-${u}.handlebars`), 'utf8');
            }
            res.view(`board-profile`, { title: 'foundation board', currentYear: currentYear, member: members[u], bio: members[u].bio, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/sponsors',
        handler: (req, res) => {
            res.view('sponsors', { title: 'sponsors', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/sponsors/levels',
        handler: (req, res) => {
            res.view('sponsors-levels', { title: 'sponsors', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/sponsors/letter',
        handler: (req, res) => {
            res.view('sponsor-letter', { title: 'sponsors', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/sponsors/letter/print',
        handler: (req, res) => {
            res.view('letter', { title: 'sponsors', currentYear: currentYear, banners: getBanners() }, { layout: 'print' });
        }
    },
    {
        method: 'GET',
        path: '/contact',
        handler: (req, res) => {
            res.view('contact', { title: 'contact', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/schedule',
        handler: (req, res) => {
            res.view('schedule', { title: 'schedule', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/faq',
        handler: (req, res) => {
            res.view('faq', { title: 'faq', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/code',
        handler: (req, res) => {
            res.view('code', { title: 'code of conduct', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/press',
        handler: (req, res) => {
            fs.readFile('./views/data/news.json', (err, data) => {
                var content = JSON.parse(data);
                res.view('press', content);
            });
        }
    },
    {
        method: 'GET',
        path: '/twitter',
        handler: (req, res) => {
            res.redirect('https://twitter.com/hacksi');
        }
    },
    {
        method: 'GET',
        path: '/github',
        handler: (req, res) => {
            res.redirect('https://github.com/HackSI');
        }
    },
    {
        method: 'GET',
        path: '/facebook',
        handler: (req, res) => {
            res.redirect('https://facebook.com/HackSouthernIllinois');
        }
    },
    {
        method: 'GET',
        path: '/google',
        handler: (req, res) => {
            res.redirect('https://plus.google.com/communities/108109882393061170322');
        }
    },
    {
        method: 'GET',
        path: '/flickr',
        handler: (req, res) => {
            res.redirect('https://www.flickr.com/groups/hacksi/pool/');
        }
    },
    {
        method: 'GET',
        path: '/live',
        handler: (req, res) => {
            res.view('live', { title: 'live', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/ssg',
        handler: (req, res) => {
            res.view('ssg', { title: 'ssg', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/{path*}',
        handler: (req, res) => {
            //console.log(req);
            res.redirect('/');
        }
    },
    {
        method: 'GET',
        path: '/tracker',
        handler: (req, res) => {
            res.redirect('/');
        }
    },
    {
        method: 'GET',
        path: '/timer',
        handler: (req, res) => {
            let duration = req.query.duration || 120;
            const min = req.query.minutes;
            const sec = req.query.seconds;

            /*istanbul ignore next*/
            if (min || sec) {
                duration = 0;
                if (min) {
                    duration = Number(min) * 60;
                }
                if (sec) {
                    duration += Number(sec);
                }
            }

            res.view('timer', { title: 'timer', duration: duration, currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/survey',
        handler: (req, res) => {
            res.redirect('https://www.surveymonkey.com/r/6Z2K3SG');
        }
    },
    {
        method: 'GET',
        path: '/donate',
        handler: (req, res) => {
            res.redirect('https://paypal.me/HackSIOrg');
        }
    },
    {
        method: 'GET',
        path: '/steam',
        handler: (req, res) => {
            res.redirect('https://support.steampowered.com/kb_article.php?ref=5414-TFBN-1352');
        }
    },
    {
        method: "POST",
        path: "/api/pi",
        handler: require('./api').post,
        config: {
            validate: require('./api').validate
        }
    },
    {
        method: "DELETE",
        path: "/api/pi",
        handler: require('./api').delete,
        config: {
            validate: require('./api').deleteValidate
        }
    },
    {
        method: "GET",
        path: "/api/pi",
        handler: require('./api').get
    },
    {
        method: "GET",
        path: "/hashtag",
        handler: (req, res) => {
            res.redirect('https://twitter.com/search?f=tweets&vertical=default&q=%23HackSI&src=typd');
        }
    },
    {
        method: "GET",
        path: "/wallpaper",
        handler: (req, res) => {
            res.view('wallpaper', { title: 'wallpaper' });
        }
    },
    {
        method: "GET",
        path: "/machines",
        handler: (req, res) => {
            const list = require('./api').list;
            list((items) => {
                items.forEach((o) => {
                    o.stamp = (new Date(o.stamp)).toISOString();
                });
                res.view('machines', { title: 'known machines', machines: items });
            });
        }
    }
];
<commit_msg>added /shop redirect<commit_after>'use strict';
const fs = require('fs');
const path = require('path');
const currentYear = (new Date()).getFullYear();

const members = require('./board.json');
Object.keys(members).forEach(i => {return members[i].id = i;});

const banners = fs.readdirSync(path.join(__dirname, '../static/img/banner')).map((item) => {
    return `/static/img/banner/${item}`;
});

const getBanners = () => {
    const items = [].concat(banners).sort(() => {return Math.random() * 2 - 1;});
    items.forEach((file, key) => {
        const show = key === 0 ? ' show' : '';
        items[key] = `<img src="${file}" class="banner-img${show}">`;
    });
    return items.join('\n');
};

module.exports = [
    {
        method: 'GET',
        path: '/favicon.ico',
        handler: {
            file: './static/favicon.ico'
        },
        config: {
            cache: {
                expiresIn: 86400000,
                privacy: 'public'
            }
        }
    },
    {
        method: 'GET',
        path: '/static/{path*}',
        handler: {
            directory: {
                path: './static',
                listing: false,
                index: false
            }
        }
    },
    {
        method: 'GET',
        path: '/',
        handler: (req, res) => {
            res.view('home', { title: 'home', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/register',
        handler: (req, res) => {
            res.redirect('https://hacksi2017.eventbrite.com');
        }
    },
    {
        method: 'GET',
        path: '/register/forms',
        handler: (req, res) => {
            res.view('register', { title: 'register', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/register/forms/pdf',
        handler: (req, res) => {
            res.file('./static/forms/HackSIAssumptionofRiskForm.pdf');
        }
    },
    {
        method: 'GET',
        path: '/volunteer',
        handler: (req, res) => {
            res.redirect('https://hacksi2017.eventbrite.com');
        }
    },
    {
        method: 'GET',
        path: '/cfp',
        handler: (req, res) => {
            res.redirect('http://goo.gl/forms/SUR5XDWcQWQt6ekf2');
        }
    },
    {
        method: 'GET',
        path: '/about',
        handler: (req, res) => {
            res.view('about', { title: 'about', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/board',
        handler: (req, res) => {
            const board = [];
            Object.keys(members).forEach(i => {
                board.push(members[i]);
            });
            board.splice(board.length - 1, 0, {}); //Insert empty spot to skip a grid
            res.view('board', { title: 'foundation board', currentYear: currentYear, members: board, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/board/{user}',
        handler: (req, res) => {
            const u = req.params.user;
            if (!members[u]) {
                return res.redirect('/board');
            }
            if (!members[u].bio) {
                members[u].bio = fs.readFileSync(path.join(__dirname, `../views/board-${u}.handlebars`), 'utf8');
            }
            res.view(`board-profile`, { title: 'foundation board', currentYear: currentYear, member: members[u], bio: members[u].bio, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/sponsors',
        handler: (req, res) => {
            res.view('sponsors', { title: 'sponsors', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/sponsors/levels',
        handler: (req, res) => {
            res.view('sponsors-levels', { title: 'sponsors', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/sponsors/letter',
        handler: (req, res) => {
            res.view('sponsor-letter', { title: 'sponsors', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/sponsors/letter/print',
        handler: (req, res) => {
            res.view('letter', { title: 'sponsors', currentYear: currentYear, banners: getBanners() }, { layout: 'print' });
        }
    },
    {
        method: 'GET',
        path: '/contact',
        handler: (req, res) => {
            res.view('contact', { title: 'contact', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/schedule',
        handler: (req, res) => {
            res.view('schedule', { title: 'schedule', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/faq',
        handler: (req, res) => {
            res.view('faq', { title: 'faq', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/code',
        handler: (req, res) => {
            res.view('code', { title: 'code of conduct', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/press',
        handler: (req, res) => {
            fs.readFile('./views/data/news.json', (err, data) => {
                var content = JSON.parse(data);
                res.view('press', content);
            });
        }
    },
    {
        method: 'GET',
        path: '/twitter',
        handler: (req, res) => {
            res.redirect('https://twitter.com/hacksi');
        }
    },
    {
        method: 'GET',
        path: '/github',
        handler: (req, res) => {
            res.redirect('https://github.com/HackSI');
        }
    },
    {
        method: 'GET',
        path: '/facebook',
        handler: (req, res) => {
            res.redirect('https://facebook.com/HackSouthernIllinois');
        }
    },
    {
        method: 'GET',
        path: '/google',
        handler: (req, res) => {
            res.redirect('https://plus.google.com/communities/108109882393061170322');
        }
    },
    {
        method: 'GET',
        path: '/flickr',
        handler: (req, res) => {
            res.redirect('https://www.flickr.com/groups/hacksi/pool/');
        }
    },
    {
        method: 'GET',
        path: '/live',
        handler: (req, res) => {
            res.view('live', { title: 'live', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/ssg',
        handler: (req, res) => {
            res.view('ssg', { title: 'ssg', currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/{path*}',
        handler: (req, res) => {
            //console.log(req);
            res.redirect('/');
        }
    },
    {
        method: 'GET',
        path: '/tracker',
        handler: (req, res) => {
            res.redirect('/');
        }
    },
    {
        method: 'GET',
        path: '/timer',
        handler: (req, res) => {
            let duration = req.query.duration || 120;
            const min = req.query.minutes;
            const sec = req.query.seconds;

            /*istanbul ignore next*/
            if (min || sec) {
                duration = 0;
                if (min) {
                    duration = Number(min) * 60;
                }
                if (sec) {
                    duration += Number(sec);
                }
            }

            res.view('timer', { title: 'timer', duration: duration, currentYear: currentYear, banners: getBanners() });
        }
    },
    {
        method: 'GET',
        path: '/survey',
        handler: (req, res) => {
            res.redirect('https://www.surveymonkey.com/r/6Z2K3SG');
        }
    },
    {
        method: 'GET',
        path: '/donate',
        handler: (req, res) => {
            res.redirect('https://paypal.me/HackSIOrg');
        }
    },
    {
        method: 'GET',
        path: '/shop',
        handler: (req, res) => {
            res.redirect('https://hacksi.threadless.com/');
        }
    },
    {
        method: 'GET',
        path: '/steam',
        handler: (req, res) => {
            res.redirect('https://support.steampowered.com/kb_article.php?ref=5414-TFBN-1352');
        }
    },
    {
        method: "POST",
        path: "/api/pi",
        handler: require('./api').post,
        config: {
            validate: require('./api').validate
        }
    },
    {
        method: "DELETE",
        path: "/api/pi",
        handler: require('./api').delete,
        config: {
            validate: require('./api').deleteValidate
        }
    },
    {
        method: "GET",
        path: "/api/pi",
        handler: require('./api').get
    },
    {
        method: "GET",
        path: "/hashtag",
        handler: (req, res) => {
            res.redirect('https://twitter.com/search?f=tweets&vertical=default&q=%23HackSI&src=typd');
        }
    },
    {
        method: "GET",
        path: "/wallpaper",
        handler: (req, res) => {
            res.view('wallpaper', { title: 'wallpaper' });
        }
    },
    {
        method: "GET",
        path: "/machines",
        handler: (req, res) => {
            const list = require('./api').list;
            list((items) => {
                items.forEach((o) => {
                    o.stamp = (new Date(o.stamp)).toISOString();
                });
                res.view('machines', { title: 'known machines', machines: items });
            });
        }
    }
];
<|endoftext|>