<commit_before>var express = require('express');

var bodyParser = require('body-parser');
var path = require('path');

var config = require('./db-config');
var mysql = require('mysql');

var app = express();

// map static resources to url
app.use('/anime/', express.static('static'));
app.use('/anime/iisnode', express.static('iisnode')); // debug only

// parse application/json
// app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));

// set up template engine
app.engine('html', require('hogan-express'));
app.set('view engine', 'html');

app.enable('trust proxy');
const sqlQueryNext = 'SELECT image_id,filename,series.title,series.folder FROM images AS r1 ' +
    'JOIN series USING(series_id) ' +
    'JOIN (SELECT CEIL(RAND() * (SELECT MAX(image_id) FROM images)) AS id) AS r2 ' +
    'WHERE r1.image_id >= r2.id ' +
    'ORDER BY r1.image_id ASC ' +
    'LIMIT 1'; // fixme remove 0 byte images
/*
 const sqlQueryNext = 'SELECT images.image_id, series.title, series.folder, images.filename FROM images ' +
 'LEFT JOIN ratings USING(image_id) ' +
 'LEFT JOIN series USING(series_id) ' +
 'GROUP BY ratings.image_id ORDER BY COUNT(ratings.rating_id) LIMIT 1';
 */
const sqlQueryLink = 'SELECT title, filename, folder FROM images ' +
    'LEFT JOIN series ON images.series_id=series.series_id ' +
    'WHERE `image_id` = ?';
const sqlInsertUser = 'INSERT IGNORE INTO users SET ?';
const sqlInsertRating = 'INSERT INTO ratings SET ?';

function strEndsWith(str, suffix) {
    return str.match(suffix + "$") == suffix;
}

function insertUser(ip) {
    var vars = {ip: ip};
    var connection = mysql.createConnection(config);
    try {
        connection.connect();
        var query = connection.query(sqlInsertUser, vars, function (err, results, fields) {
            if (err) throw err;
        });
        console.log(query.sql);

    } catch (e) {
        console.error(e);
    } finally {
        connection.end();
    }
}

// url handling
var cardFunc = function (req, res) {
    // add new user
    var ip = req.headers['x-forwarded-for'];
    console.log(ip);
    insertUser(ip);

    // init session

    // save session cookie to db

    // load image from url
    var imageId = req.params.image_id;
    if (imageId !== undefined) {
        var connection = mysql.createConnection(config);
        try {
            connection.connect();
            connection.query(sqlQueryLink, [imageId], function (err, rows, fields) {
                if (err) throw err;
                var imgLink, source;
                if (rows[0] === undefined) {
                    imgLink = 'img/404.jpg';
                    source = 'http://mangaonlinehere.com';
                } else {
                    imgLink = 'img/' + rows[0].folder + '/' + rows[0].filename;
                    source = rows[0].title;
                }
                res.render('card', {
                    head_title: 'Image Labeling',
                    card_title: 'Introduction',
                    image_id: imageId,
                    img: imgLink,
                    source: source
                });

            });
        } catch (e) {
            console.error(e);
        } finally {
            connection.end();
        }
    } else {
        res.render('card', {
            head_title: 'Image Labeling',
            card_title: 'Anime or Cartoon',
            image_id: '0',
            img: '/anime/img/aang.jpg',
            source: 'Avatar: The Last Airbender'
        });
    }
};
app.get('/anime/', cardFunc); // intro, start
// app.get('/anime/tutorial/:tutorial_id(\\d+)/', cardFunc); // tutorial
app.get('/anime/:image_id(\\d+)/', cardFunc);

var domain = "/anime/";
/**
 *
 * @param req
 * @param res
 * @param success if the previous update was successful
 */
var nextImageFunc = function (req, res, success) {
    var connection = mysql.createConnection(config);
    try {
        connection.connect();
        var query = connection.query(sqlQueryNext, function (err, rows, fields) {
            if (err) throw err;
            var imgLink = 'img/' + rows[0].folder + '/' + rows[0].filename;

            console.log('Image sent: ', imgLink);

            res.send({
                id: rows[0].image_id,
                imgURL: domain + imgLink,
                imgSrc: rows[0].title,
                prevSave: success
            })
        });
        console.log(query.sql);
    } catch (e) {
        console.error(e);
    } finally {
        connection.end();
    }
};

app.get('/anime/next_image', nextImageFunc);
app.post('/anime/next_image', function (req, res) {
    console.log("post request");

    var id = req.body.id; // image_id
    var text = req.body.text;
    var char = req.body.char;
    var empty = req.body.empty;
    var logo = req.body.logo;

    if (id === undefined) {
        console.error('id undefined in nextImage Post');
        res.statusCode(404);
        return;
    }
    if (parseInt(id) < 0) { // tutorial
        console.log('Tutorial: ', id);

        res.send({
            id: id,
            imgURL: domain + 'img/tutorial' + (-id) + '.jpg',
            imgSrc: "Fullmetal Alchemist: Brotherhood",
            prevSave: true
        })
    } else if (parseInt(id) === 0) { // don't save
        nextImageFunc(req, res, true); // redirect to get impl
    } else { // save and next

        // check session

        // convert values
        var vars = {
            image_id: id,
            text: text === 'true' ? 1 : 0,
            person: char === 'true' ? 1 : 0,
            logo: logo === 'true' ? 1 : 0,
            empty: empty === 'true' ? 1 : 0
        };

        // todo save user_id
        // save to db
        var connection = mysql.createConnection(config);
        try {
            connection.connect();
            var query = connection.query(sqlInsertRating, vars, function (err, results, fields) {
                if (err) throw err;
                // console.log('Inserted ', results.affectedRows, 'rows');

                nextImageFunc(req, res, parseInt(results.affectedRows) > 0); // redirect to get impl
            });
            console.log(query.sql);

        } catch (e) {
            console.error(e);
        } finally {
            connection.end();
        }
    }
});

const root = './static/img/';
const fs = require('fs');

function uploadDir(dir) {
    var sqlInsertImages = 'INSERT INTO images (series_id, filename) VALUES ';

    // create series row
    console.log('processing dir ' + dir);
    fs.readdir(root + dir, function (err, files) {
        for (var i = 0; i < files.length; i++) {
            var file = files[i];
            if (strEndsWith(file.toLowerCase(), '.jpg') || strEndsWith(file.toLowerCase(), '.jpeg')) {
                sqlInsertImages += "(LAST_INSERT_ID(),'" + file + "')";
                if (i !== files.length - 1) {
                    sqlInsertImages += ',';
                }
            }
        }

        var insertSeries = "INSERT IGNORE INTO series (title, folder) VALUES('" + dir + "','" + dir + "');";
        // write query
        /* fs.writeFile(root + dir + "/_insert.sql", insertSeries + sqlInsertImages, function (err) {
         if (err) {
         console.log(err);
         return;
         }
         console.log(dir + " query saved");
         });*/
        var multiConfig = require('./db-config');
        multiConfig.multipleStatements = true;
        var connection = mysql.createConnection(multiConfig);
        try {
            connection.connect();
            var query = connection.query(insertSeries + sqlInsertImages, function (err, results, fields) {
                if (err) throw err;

                console.log(dir, ' inserted (probably) ', files.length, ' rows');
                // add check file when dir/file already done
                fs.appendFileSync(root + 'saved_series.txt', dir);

                console.log(dir + " saved");

            });
            // console.log(query.sql);
        } catch (e) {
            console.error(e);
        } finally {
            connection.end();
        }
    });
}
app.get('/anime/update', function (req, res) {

    // read done file in sync
    var done = fs.readFileSync(root + 'saved_series.txt').toString().split("\n");

    fs.readdir(root, function (err, files) {
        for (var i = 0; i < files.length; i++) {
            var dir = files[i];
            if (done.indexOf(dir) === -1) { // not already processed
                if (fs.statSync(root + dir).isDirectory()) {
                    uploadDir(dir);
                }
            }
        }
    });
    res.send('done');
});

var port = process.env.PORT || 3000;
app.listen(port, function () {
    console.log('Example app listening on port ' + port)
});
<commit_msg>404 page card title<commit_after>var express = require('express');

var bodyParser = require('body-parser');
var path = require('path');

var config = require('./db-config');
var mysql = require('mysql');

var app = express();

// map static resources to url
app.use('/anime/', express.static('static'));
app.use('/anime/iisnode', express.static('iisnode')); // debug only

// parse application/json
// app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));

// set up template engine
app.engine('html', require('hogan-express'));
app.set('view engine', 'html');

app.enable('trust proxy');
const sqlQueryNext = 'SELECT image_id,filename,series.title,series.folder FROM images AS r1 ' +
    'JOIN series USING(series_id) ' +
    'JOIN (SELECT CEIL(RAND() * (SELECT MAX(image_id) FROM images)) AS id) AS r2 ' +
    'WHERE r1.image_id >= r2.id ' +
    'ORDER BY r1.image_id ASC ' +
    'LIMIT 1'; // fixme remove 0 byte images
/*
 const sqlQueryNext = 'SELECT images.image_id, series.title, series.folder, images.filename FROM images ' +
 'LEFT JOIN ratings USING(image_id) ' +
 'LEFT JOIN series USING(series_id) ' +
 'GROUP BY ratings.image_id ORDER BY COUNT(ratings.rating_id) LIMIT 1';
 */
const sqlQueryLink = 'SELECT title, filename, folder FROM images ' +
    'LEFT JOIN series ON images.series_id=series.series_id ' +
    'WHERE `image_id` = ?';
const sqlInsertUser = 'INSERT IGNORE INTO users SET ?';
const sqlInsertRating = 'INSERT INTO ratings SET ?';

function strEndsWith(str, suffix) {
    return str.match(suffix + "$") == suffix;
}

function insertUser(ip) {
    var vars = {ip: ip};
    var connection = mysql.createConnection(config);
    try {
        connection.connect();
        var query = connection.query(sqlInsertUser, vars, function (err, results, fields) {
            if (err) throw err;
        });
        console.log(query.sql);

    } catch (e) {
        console.error(e);
    } finally {
        connection.end();
    }
}

// url handling
var cardFunc = function (req, res) {
    // add new user
    var ip = req.headers['x-forwarded-for'];
    console.log(ip);
    insertUser(ip);

    // init session

    // save session cookie to db

    // load image from url
    var imageId = req.params.image_id;
    if (imageId !== undefined) {
        var connection = mysql.createConnection(config);
        try {
            connection.connect();
            connection.query(sqlQueryLink, [imageId], function (err, rows, fields) {
                if (err) throw err;
                var imgLink, source;
                var cardTitle = 'Introduction';

                if (rows[0] === undefined) {
                    imgLink = 'img/404.jpg';
                    source = 'http://mangaonlinehere.com';
                    cardTitle ='Image not found'
                } else {
                    imgLink = 'img/' + rows[0].folder + '/' + rows[0].filename;
                    source = rows[0].title;
                }res.render('card', {
                    head_title: 'Image Labeling',
                    card_title: cardTitle,
                    image_id: imageId,
                    img: imgLink,
                    source: source
                });

            });
        } catch (e) {
            console.error(e);
        } finally {
            connection.end();
        }
    } else {
        res.render('card', {
            head_title: 'Image Labeling',
            card_title: 'Anime or Cartoon',
            image_id: '0',
            img: '/anime/img/aang.jpg',
            source: 'Avatar: The Last Airbender'
        });
    }
};
app.get('/anime/', cardFunc); // intro, start
// app.get('/anime/tutorial/:tutorial_id(\\d+)/', cardFunc); // tutorial
app.get('/anime/:image_id(\\d+)/', cardFunc);

var domain = "/anime/";
/**
 *
 * @param req
 * @param res
 * @param success if the previous update was successful
 */
var nextImageFunc = function (req, res, success) {
    var connection = mysql.createConnection(config);
    try {
        connection.connect();
        var query = connection.query(sqlQueryNext, function (err, rows, fields) {
            if (err) throw err;
            var imgLink = 'img/' + rows[0].folder + '/' + rows[0].filename;

            console.log('Image sent: ', imgLink);

            res.send({
                id: rows[0].image_id,
                imgURL: domain + imgLink,
                imgSrc: rows[0].title,
                prevSave: success
            })
        });
        console.log(query.sql);
    } catch (e) {
        console.error(e);
    } finally {
        connection.end();
    }
};

app.get('/anime/next_image', nextImageFunc);
app.post('/anime/next_image', function (req, res) {
    console.log("post request");

    var id = req.body.id; // image_id
    var text = req.body.text;
    var char = req.body.char;
    var empty = req.body.empty;
    var logo = req.body.logo;

    if (id === undefined) {
        console.error('id undefined in nextImage Post');
        res.statusCode(404);
        return;
    }
    if (parseInt(id) < 0) { // tutorial
        console.log('Tutorial: ', id);

        res.send({
            id: id,
            imgURL: domain + 'img/tutorial' + (-id) + '.jpg',
            imgSrc: "Fullmetal Alchemist: Brotherhood",
            prevSave: true
        })
    } else if (parseInt(id) === 0) { // don't save
        nextImageFunc(req, res, true); // redirect to get impl
    } else { // save and next

        // check session

        // convert values
        var vars = {
            image_id: id,
            text: text === 'true' ? 1 : 0,
            person: char === 'true' ? 1 : 0,
            logo: logo === 'true' ? 1 : 0,
            empty: empty === 'true' ? 1 : 0
        };

        // todo save user_id
        // save to db
        var connection = mysql.createConnection(config);
        try {
            connection.connect();
            var query = connection.query(sqlInsertRating, vars, function (err, results, fields) {
                if (err) throw err;
                // console.log('Inserted ', results.affectedRows, 'rows');

                nextImageFunc(req, res, parseInt(results.affectedRows) > 0); // redirect to get impl
            });
            console.log(query.sql);

        } catch (e) {
            console.error(e);
        } finally {
            connection.end();
        }
    }
});

const root = './static/img/';
const fs = require('fs');

function uploadDir(dir) {
    var sqlInsertImages = 'INSERT INTO images (series_id, filename) VALUES ';

    // create series row
    console.log('processing dir ' + dir);
    fs.readdir(root + dir, function (err, files) {
        for (var i = 0; i < files.length; i++) {
            var file = files[i];
            if (strEndsWith(file.toLowerCase(), '.jpg') || strEndsWith(file.toLowerCase(), '.jpeg')) {
                sqlInsertImages += "(LAST_INSERT_ID(),'" + file + "')";
                if (i !== files.length - 1) {
                    sqlInsertImages += ',';
                }
            }
        }

        var insertSeries = "INSERT IGNORE INTO series (title, folder) VALUES('" + dir + "','" + dir + "');";
        // write query
        /* fs.writeFile(root + dir + "/_insert.sql", insertSeries + sqlInsertImages, function (err) {
         if (err) {
         console.log(err);
         return;
         }
         console.log(dir + " query saved");
         });*/
        var multiConfig = require('./db-config');
        multiConfig.multipleStatements = true;
        var connection = mysql.createConnection(multiConfig);
        try {
            connection.connect();
            var query = connection.query(insertSeries + sqlInsertImages, function (err, results, fields) {
                if (err) throw err;

                console.log(dir, ' inserted (probably) ', files.length, ' rows');
                // add check file when dir/file already done
                fs.appendFileSync(root + 'saved_series.txt', dir);

                console.log(dir + " saved");

            });
            // console.log(query.sql);
        } catch (e) {
            console.error(e);
        } finally {
            connection.end();
        }
    });
}
app.get('/anime/update', function (req, res) {

    // read done file in sync
    var done = fs.readFileSync(root + 'saved_series.txt').toString().split("\n");

    fs.readdir(root, function (err, files) {
        for (var i = 0; i < files.length; i++) {
            var dir = files[i];
            if (done.indexOf(dir) === -1) { // not already processed
                if (fs.statSync(root + dir).isDirectory()) {
                    uploadDir(dir);
                }
            }
        }
    });
    res.send('done');
});

var port = process.env.PORT || 3000;
app.listen(port, function () {
    console.log('Example app listening on port ' + port)
});
<|endoftext|>


<commit_before>//#import "calabash_script_uia.js"

if (typeof JSON !== 'object') {
    JSON = {};
}
(function () {
    'use strict';
    function f(n) {
        return n < 10 ? '0' + n : n;
    }

    if (typeof Date.prototype.toJSON !== 'function') {
        Date.prototype.toJSON = function (key) {
            return isFinite(this.valueOf()) ? this.getUTCFullYear() + '-' +
                f(this.getUTCMonth() + 1) + '-' +
                f(this.getUTCDate()) + 'T' +
                f(this.getUTCHours()) + ':' +
                f(this.getUTCMinutes()) + ':' +
                f(this.getUTCSeconds()) + 'Z' : null;
        };
        String.prototype.toJSON = Number.prototype.toJSON = Boolean.prototype.toJSON = function (key) {
            return this.valueOf();
        };
    }
    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, gap, indent, meta = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"': '\\"', '\\': '\\\\'}, rep;

    function quote(string) {
        escapable.lastIndex = 0;
        return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
            var c = meta[a];
            return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
        }) + '"' : '"' + string + '"';
    }

    function str(key, holder) {
        var i, k, v, length, mind = gap, partial, value = holder[key];
        if (value && typeof value === 'object' && typeof value.toJSON === 'function') {
            value = value.toJSON(key);
        }
        if (typeof rep === 'function') {
            value = rep.call(holder, key, value);
        }
        switch (typeof value) {
            case'string':
                return quote(value);
            case'number':
                return isFinite(value) ? String(value) : 'null';
            case'boolean':
            case'null':
                return String(value);
            case'object':
                if (!value) {
                    return'null';
                }
                gap += indent;
                partial = [];
                if (Object.prototype.toString.apply(value) === '[object Array]') {
                    length = value.length;
                    for (i = 0; i < length; i += 1) {
                        partial[i] = str(i, value) || 'null';
                    }
                    v = partial.length === 0 ? '[]' : gap ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : '[' + partial.join(',') + ']';
                    gap = mind;
                    return v;
                }
                if (rep && typeof rep === 'object') {
                    length = rep.length;
                    for (i = 0; i < length; i += 1) {
                        if (typeof rep[i] === 'string') {
                            k = rep[i];
                            v = str(k, value);
                            if (v) {
                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
                            }
                        }
                    }
                } else {
                    for (k in value) {
                        if (Object.prototype.hasOwnProperty.call(value, k)) {
                            v = str(k, value);
                            if (v) {
                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
                            }
                        }
                    }
                }
                v = partial.length === 0 ? '{}' : gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : '{' + partial.join(',') + '}';
                gap = mind;
                return v;
        }
    }

    if (typeof JSON.stringify !== 'function') {
        JSON.stringify = function (value, replacer, space) {
            var i;
            gap = '';
            indent = '';
            if (typeof space === 'number') {
                for (i = 0; i < space; i += 1) {
                    indent += ' ';
                }
            } else if (typeof space === 'string') {
                indent = space;
            }
            rep = replacer;
            if (replacer && typeof replacer !== 'function' && (typeof replacer !== 'object' || typeof replacer.length !== 'number')) {
                throw new Error('JSON.stringify');
            }
            return str('', {'': value});
        };
    }
    if (typeof JSON.parse !== 'function') {
        JSON.parse = function (text, reviver) {
            var j;

            function walk(holder, key) {
                var k, v, value = holder[key];
                if (value && typeof value === 'object') {
                    for (k in value) {
                        if (Object.prototype.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }

            text = String(text);
            cx.lastIndex = 0;
            if (cx.test(text)) {
                text = text.replace(cx, function (a) {
                    return'\\u' +
                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                });
            }
            if (/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
                j = eval('(' + text + ')');
                return typeof reviver === 'function' ? walk({'': j}, '') : j;
            }
            throw new SyntaxError('JSON.parse');
        };
    }
}());

var commandPath = "$PATH";
if (!/\/$/.test(commandPath)) {
    commandPath += "/";
}
commandPath += "repl-cmd.pipe";

var blockingReadScriptPath = "$READ_SCRIPT_PATH";

var _expectedIndex = 0,//expected index of next command
    _actualIndex,//actual index of next command by reading commandPath
    _index,//index of ':' char in command
    _exp,//expression to be eval'ed
    _result,//result of eval
    _input,//command
    _process;//host command process

var Log = (function () {
    var forceFlush = [],
        N = "$MODE" == "FLUSH" ? 16384 : 0,
        i = N;
    while (i--) {
        forceFlush[i] = "*";
    }
    forceFlush = forceFlush.join('');

    function log_json(object)
    {
        UIALogger.logMessage("OUTPUT_JSON:\n"+JSON.stringify(object)+"\nEND_OUTPUT");
    }

    return {
        result: function (status, data) {
            log_json({"status": status, "value": data, "index":_actualIndex})
            if (forceFlush.length > 0) {
                UIALogger.logMessage(forceFlush);
            }
        },
        output: function (msg) {
            log_json({"output": msg,"last_index":_actualIndex});
            if (forceFlush.length > 0) {
                UIALogger.logMessage(forceFlush);
            }
        }
    };
})();

function findAlertViewText(alert) {
    if (!alert) {
        return false;
    }
    var txt = alert.name(),
        txts;
    if (txt == null) {
        txts = alert.staticTexts();
        if (txts != null && txts.length > 0) {
            txt = txts[0].name();
        }
    }
    return txt;
}

function isLocationPrompt(alert) {
    var exps = [
            ["OK", /vil bruge din aktuelle placering/],
            ["OK", /Would Like to Use Your Current Location/],
            ["OK", /Would Like to Send You Notifications/],
            ["Allow", /access your location/],
            ["Ja", /Darf (?:.)+ Ihren aktuellen Ort verwenden/],
            ["OK", /Would Like to Access Your Photos/],
            ["OK", /Would Like to Access Your Contacts/],
            ["OK", /запрашивает разрешение на использование Ващей текущей пгеопозиции/]
        ],
        ans, exp,
        txt;

    txt = findAlertViewText(alert);
    Log.output({"output":"alert: "+txt}, true);
    for (var i = 0; i < exps.length; i++) {
        ans = exps[i][0];
        exp = exps[i][1];
        if (exp.test(txt)) {
            return ans;
        }
    }
    return false;
}

UIATarget.onAlert = function (alert) {
    Log.output({"output":"on alert"}, true);
    var target = UIATarget.localTarget();
    target.pushTimeout(10);
    function attemptTouchOKOnLocation(retry_count) {
        retry_count = retry_count || 0;
        if (retry_count >= 5) {
            Log.output("Maxed out retry (5) - unable to dismiss location dialog.");
            return;
        }
        try {
            var answer = isLocationPrompt(alert);
            if (answer) {
                alert.buttons()[answer].tap();
            }
        }
        catch (e) {
            Log.output("Exception while trying to touch alert dialog. Retrying...");
            if (e && typeof e.toString == 'function') {
                Log.output(e.toString());
            }
            target.delay(1);
            attemptTouchOKOnLocation(retry_count + 1);
        }
    }

    attemptTouchOKOnLocation(0);
    target.popTimeout();
    return true;
};

var target = null,
    host = null;

while (true) {
    target = UIATarget.localTarget();

    host = target.host();
    try {
        _process = host.performTaskWithPathArgumentsTimeout("/bin/bash",
            [blockingReadScriptPath, commandPath],
            //[commandPath],
           1);

    } catch (e) {
        Log.output("Timeout on read command...");
        continue;
    }
    if (_process.exitCode != 0) {
        if (_process.exitCode != 15) {
            Log.output("unable to execute: " + blockingReadScriptPath + " " + commandPath + " exitCode " + _process.exitCode + ". Error: " + _process.stderr + _process.stdout);
        }
    }
    else {
        _input = _process.stdout;
        try {
            _index = _input.indexOf(":", 0);
            if (_index > -1) {
                _actualIndex = parseInt(_input.substring(0, _index), 10);
                if (!isNaN(_actualIndex) && _actualIndex >= _expectedIndex) {
                    _exp = _input.substring(_index + 1, _input.length);
                    Log.output("Execute: "+_exp);
                    _result = eval(_exp);
                }
                else {//likely old command is lingering...
                    continue;
                }
            }
            else {
                continue;
            }

        }
        catch (err) {
            Log.result("error", err.toString() + "  " + (err.stack ? err.stack.toString() : ""));
            _expectedIndex++;
            continue;
        }

        _expectedIndex = Math.max(_actualIndex+1, _expectedIndex+1);
        Log.result("success", _result);
    }
}<commit_msg>Add a log msg before starting js while<commit_after>//#import "calabash_script_uia.js"

if (typeof JSON !== 'object') {
    JSON = {};
}
(function () {
    'use strict';
    function f(n) {
        return n < 10 ? '0' + n : n;
    }

    if (typeof Date.prototype.toJSON !== 'function') {
        Date.prototype.toJSON = function (key) {
            return isFinite(this.valueOf()) ? this.getUTCFullYear() + '-' +
                f(this.getUTCMonth() + 1) + '-' +
                f(this.getUTCDate()) + 'T' +
                f(this.getUTCHours()) + ':' +
                f(this.getUTCMinutes()) + ':' +
                f(this.getUTCSeconds()) + 'Z' : null;
        };
        String.prototype.toJSON = Number.prototype.toJSON = Boolean.prototype.toJSON = function (key) {
            return this.valueOf();
        };
    }
    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, gap, indent, meta = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"': '\\"', '\\': '\\\\'}, rep;

    function quote(string) {
        escapable.lastIndex = 0;
        return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
            var c = meta[a];
            return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
        }) + '"' : '"' + string + '"';
    }

    function str(key, holder) {
        var i, k, v, length, mind = gap, partial, value = holder[key];
        if (value && typeof value === 'object' && typeof value.toJSON === 'function') {
            value = value.toJSON(key);
        }
        if (typeof rep === 'function') {
            value = rep.call(holder, key, value);
        }
        switch (typeof value) {
            case'string':
                return quote(value);
            case'number':
                return isFinite(value) ? String(value) : 'null';
            case'boolean':
            case'null':
                return String(value);
            case'object':
                if (!value) {
                    return'null';
                }
                gap += indent;
                partial = [];
                if (Object.prototype.toString.apply(value) === '[object Array]') {
                    length = value.length;
                    for (i = 0; i < length; i += 1) {
                        partial[i] = str(i, value) || 'null';
                    }
                    v = partial.length === 0 ? '[]' : gap ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : '[' + partial.join(',') + ']';
                    gap = mind;
                    return v;
                }
                if (rep && typeof rep === 'object') {
                    length = rep.length;
                    for (i = 0; i < length; i += 1) {
                        if (typeof rep[i] === 'string') {
                            k = rep[i];
                            v = str(k, value);
                            if (v) {
                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
                            }
                        }
                    }
                } else {
                    for (k in value) {
                        if (Object.prototype.hasOwnProperty.call(value, k)) {
                            v = str(k, value);
                            if (v) {
                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
                            }
                        }
                    }
                }
                v = partial.length === 0 ? '{}' : gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : '{' + partial.join(',') + '}';
                gap = mind;
                return v;
        }
    }

    if (typeof JSON.stringify !== 'function') {
        JSON.stringify = function (value, replacer, space) {
            var i;
            gap = '';
            indent = '';
            if (typeof space === 'number') {
                for (i = 0; i < space; i += 1) {
                    indent += ' ';
                }
            } else if (typeof space === 'string') {
                indent = space;
            }
            rep = replacer;
            if (replacer && typeof replacer !== 'function' && (typeof replacer !== 'object' || typeof replacer.length !== 'number')) {
                throw new Error('JSON.stringify');
            }
            return str('', {'': value});
        };
    }
    if (typeof JSON.parse !== 'function') {
        JSON.parse = function (text, reviver) {
            var j;

            function walk(holder, key) {
                var k, v, value = holder[key];
                if (value && typeof value === 'object') {
                    for (k in value) {
                        if (Object.prototype.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }

            text = String(text);
            cx.lastIndex = 0;
            if (cx.test(text)) {
                text = text.replace(cx, function (a) {
                    return'\\u' +
                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                });
            }
            if (/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
                j = eval('(' + text + ')');
                return typeof reviver === 'function' ? walk({'': j}, '') : j;
            }
            throw new SyntaxError('JSON.parse');
        };
    }
}());

var commandPath = "$PATH";
if (!/\/$/.test(commandPath)) {
    commandPath += "/";
}
commandPath += "repl-cmd.pipe";

var blockingReadScriptPath = "$READ_SCRIPT_PATH";

var _expectedIndex = 0,//expected index of next command
    _actualIndex,//actual index of next command by reading commandPath
    _index,//index of ':' char in command
    _exp,//expression to be eval'ed
    _result,//result of eval
    _input,//command
    _process;//host command process

var Log = (function () {
    var forceFlush = [],
        N = "$MODE" == "FLUSH" ? 16384 : 0,
        i = N;
    while (i--) {
        forceFlush[i] = "*";
    }
    forceFlush = forceFlush.join('');

    function log_json(object)
    {
        UIALogger.logMessage("OUTPUT_JSON:\n"+JSON.stringify(object)+"\nEND_OUTPUT");
    }

    return {
        result: function (status, data) {
            log_json({"status": status, "value": data, "index":_actualIndex})
            if (forceFlush.length > 0) {
                UIALogger.logMessage(forceFlush);
            }
        },
        output: function (msg) {
            log_json({"output": msg,"last_index":_actualIndex});
            if (forceFlush.length > 0) {
                UIALogger.logMessage(forceFlush);
            }
        }
    };
})();

function findAlertViewText(alert) {
    if (!alert) {
        return false;
    }
    var txt = alert.name(),
        txts;
    if (txt == null) {
        txts = alert.staticTexts();
        if (txts != null && txts.length > 0) {
            txt = txts[0].name();
        }
    }
    return txt;
}

function isLocationPrompt(alert) {
    var exps = [
            ["OK", /vil bruge din aktuelle placering/],
            ["OK", /Would Like to Use Your Current Location/],
            ["OK", /Would Like to Send You Notifications/],
            ["Allow", /access your location/],
            ["Ja", /Darf (?:.)+ Ihren aktuellen Ort verwenden/],
            ["OK", /Would Like to Access Your Photos/],
            ["OK", /Would Like to Access Your Contacts/],
            ["OK", /запрашивает разрешение на использование Ващей текущей пгеопозиции/]
        ],
        ans, exp,
        txt;

    txt = findAlertViewText(alert);
    Log.output({"output":"alert: "+txt}, true);
    for (var i = 0; i < exps.length; i++) {
        ans = exps[i][0];
        exp = exps[i][1];
        if (exp.test(txt)) {
            return ans;
        }
    }
    return false;
}

UIATarget.onAlert = function (alert) {
    Log.output({"output":"on alert"}, true);
    var target = UIATarget.localTarget();
    target.pushTimeout(10);
    function attemptTouchOKOnLocation(retry_count) {
        retry_count = retry_count || 0;
        if (retry_count >= 5) {
            Log.output("Maxed out retry (5) - unable to dismiss location dialog.");
            return;
        }
        try {
            var answer = isLocationPrompt(alert);
            if (answer) {
                alert.buttons()[answer].tap();
            }
        }
        catch (e) {
            Log.output("Exception while trying to touch alert dialog. Retrying...");
            if (e && typeof e.toString == 'function') {
                Log.output(e.toString());
            }
            target.delay(1);
            attemptTouchOKOnLocation(retry_count + 1);
        }
    }

    attemptTouchOKOnLocation(0);
    target.popTimeout();
    return true;
};

var target = null,
    host = null;

Log.output('Starting loop');
while (true) {
    target = UIATarget.localTarget();

    host = target.host();
    try {
        _process = host.performTaskWithPathArgumentsTimeout("/bin/bash",
            [blockingReadScriptPath, commandPath],
            //[commandPath],
           1);

    } catch (e) {
        Log.output("Timeout on read command...");
        continue;
    }
    if (_process.exitCode != 0) {
        if (_process.exitCode != 15) {
            Log.output("unable to execute: " + blockingReadScriptPath + " " + commandPath + " exitCode " + _process.exitCode + ". Error: " + _process.stderr + _process.stdout);
        }
    }
    else {
        _input = _process.stdout;
        try {
            _index = _input.indexOf(":", 0);
            if (_index > -1) {
                _actualIndex = parseInt(_input.substring(0, _index), 10);
                if (!isNaN(_actualIndex) && _actualIndex >= _expectedIndex) {
                    _exp = _input.substring(_index + 1, _input.length);
                    Log.output("Execute: "+_exp);
                    _result = eval(_exp);
                }
                else {//likely old command is lingering...
                    continue;
                }
            }
            else {
                continue;
            }

        }
        catch (err) {
            Log.result("error", err.toString() + "  " + (err.stack ? err.stack.toString() : ""));
            _expectedIndex++;
            continue;
        }

        _expectedIndex = Math.max(_actualIndex+1, _expectedIndex+1);
        Log.result("success", _result);
    }
}<|endoftext|>


<commit_before>if (typeof(console) == 'undefined') {
	console = {
		log: function() { return null; }
	};
}

if (typeof(Number.prototype.toRad) === 'undefined') {
	Number.prototype.toRad = function() {
		return this * Math.PI / 180;
	}
}

// Sometimes jQuery doesn't understand the Number.toRad()
function _toRad(Value) {
    /** Converts numeric degrees to radians */
    return Value * Math.PI / 180;
}

var History = window.History;

History.Adapter.bind(window,'statechange',function() {
	var State = History.getState();
	History.log(State.data, State.title, State.url);
	if (!App.config) return;
	if (App._init) {
		App.loadPage();
	}
});

if (!typeof(App) == 'undefined') {
	App = {};
}

App.request = function(url, complete) {
	$.getJSON(url,function(json) {
		complete(json);
	});
};

App.cache = function(type, id) {
	var finalid, args = arguments, complete, partComplete;

	complete = args[2] ? args[2] : function() {};

	partComplete = function() {
		if (this.uuid) {
			App.cached[type][id.uuid] = this;
			App.cached[type][id] = this;
		}
		if (this.permalink) {
			App.cached[type][id.permalink] = this;
			App.cached[type][id] = this;
		}
		complete.call(this);
	}

	if (typeof(id) == 'object') {
		//App.cached[type][id.id] = id;

		eval('App.cached[type][id.id] = new '+type+'(id,partComplete)');
		finalid = id.id;

	} else if (!App.cached[type][id]) {
		eval('App.cached[type][id] = new '+type+'(id,partComplete)');

	} else {
		complete.call(App.cached[type][id]);
	}

	// only works sync (Ti)
	return App.cached[type][finalid || id];

};

/**
 * Rounds a float number rounded up with 2 digits.
 *
 * @return float
 */
App.ceil = function(num) {
	num = num*100;
	num = Math.ceil(num);
	return num / 100;
};

App.phone = {

	/**
	 * Add dashes to the phone number, unifying how phone number looks
	 */
	format: function(num) {
		if( num != null ){
			num = num.replace(/^0|^1/,'');
			num = num.replace(/[^\d]*/gi,'');
			num = num.substr(0,10);

			if (num.length >= 7) {
				num = num.replace(/(\d{3})(\d{3})(.*)/, "$1-$2-$3");
			} else if (num.length >= 4) {
				num = num.replace(/(\d{3})(.*)/, "$1-$2");
			}
		}
		return num;
	},
	validate: function(num) {

		if (!num || num.length != 10) {
			return false;
		}

		var
			nums = num.split(''),
			prev;

		for (x in nums) {
			if (!prev) {
				prev = nums[x];
				continue;
			}

			if (nums[x] != prev) {
				return true;
			}
		}

		return false;
	}
};

App.pad = function(number, length) {
	var str = '' + number;
	while (str.length < length) {
		str = '0' + str;
	}
	return str;
};

App.formatTime = function(time) {
	if (!time) {
		return '';
	}

	var vals = time.split(':');
	var pm = false;

	vals[0] = new String(vals[0]);
	vals[1] = vals[1] ? new String(vals[1]) : 0;

	if (vals[0].match(/^[0-9]{3,4}$/i)) {
		vals[1] = vals[0].substr(-2,2);
		vals[0] = vals[0].substr(0,vals[0].length - 2);
	}

	if (vals[0] == '12') {
		pm =  true;
	}

	if (vals[0] == '24') {
		vals[0] = '00';
	}

	if (!vals[1]) {

		if (vals[0].match(/pm/i)) {
			pm = true;
		}
		if (vals[0] > 12) {
			vals[0] -= 12;
			pm = true;
		}

		vals[1] = '00';

	} else {

		if (vals[1].match(/pm/i)) {
			pm = true;
		}
		if (vals[0] > 12) {
			vals[0] -= 12;
			pm = true;
		}

		vals[1] = vals[1].replace(/[^0-9]+/,'');
	}

	vals[0] = new String(vals[0]).replace(/[^0-9]+/,'');

	vals[0] = App.pad(vals[0],2);
	vals[1] = App.pad(vals[1],2);

	if (vals[0] == '00') {
		vals[0] = 12;
	}

	return vals.join(':') + (pm ? ' PM' : ' AM');
};

/**
 * Turns AM/PM format in 24h
 *
 * It was storing 12:00 AM as 24:00, or even worse, 12:30 AM as 24:30.
 * 12:00 AM should be 00:00 everywhere
 *
 * @return string
 */
App.unFormatTime = function(time) {
	var part   = time.split(' ');
	part[0]    = part[0].split(':');
	part[0][0] = parseInt(part[0][0], 10);

	if (part[1] == 'PM') {
		if (part[0][0] != 12) {
			part[0][0] = App.pad(part[0][0] + 12,2);
		}
	} else {
		if (part[0][0] == 12) {
			part[0][0] = '00';
		}
	}
	return part[0][0] + ':' + part[0][1];
};

App.cleanInput = function(text) {
	text = App.cleanBad(text);
	var type = arguments[1];
	switch (type) {
		case 'float':
			text = text.replace(/[^0-9\.]+/i,'');
			break;
		case 'text':
		default:
			text = text.replace(/[^a-z0-9\-_ \(\)\+\.\@\%\&\!\;\:\"\'\,\\\/]+/i,'');
			break;
	}

	return text
};

App.cleanBad = function(s) {
	s = s.replace(/\u2018|\u2019|\u201A|\uFFFD/g, "'");
	s = s.replace(/\u201c|\u201d|\u201e/g, '"');
	s = s.replace(/\u02C6/g, '^');
	s = s.replace(/\u2039/g, '<');
	s = s.replace(/\u203A/g, '>');
	s = s.replace(/\u2013/g, '-');
	s = s.replace(/\u2014/g, '--');
	s = s.replace(/\u2026/g, '...');
	s = s.replace(/\u00A9/g, '(c)');
	s = s.replace(/\u00AE/g, '(r)');
	s = s.replace(/\u2122/g, 'TM');
	s = s.replace(/\u00BC/g, '1/4');
	s = s.replace(/\u00BD/g, '1/2');
	s = s.replace(/\u00BE/g, '3/4');
	s = s.replace(/[\u02DC|\u00A0]/g, " ");
	return s;
}<commit_msg>Issue #873 (restaurant hours problem)<commit_after>if (typeof(console) == 'undefined') {
	console = {
		log: function() { return null; }
	};
}

if (typeof(Number.prototype.toRad) === 'undefined') {
	Number.prototype.toRad = function() {
		return this * Math.PI / 180;
	}
}

// Sometimes jQuery doesn't understand the Number.toRad()
function _toRad(Value) {
    /** Converts numeric degrees to radians */
    return Value * Math.PI / 180;
}

var History = window.History;

History.Adapter.bind(window,'statechange',function() {
	var State = History.getState();
	History.log(State.data, State.title, State.url);
	if (!App.config) return;
	if (App._init) {
		App.loadPage();
	}
});

if (!typeof(App) == 'undefined') {
	App = {};
}

App.request = function(url, complete) {
	$.getJSON(url,function(json) {
		complete(json);
	});
};

App.cache = function(type, id) {
	var finalid, args = arguments, complete, partComplete;

	complete = args[2] ? args[2] : function() {};

	partComplete = function() {
		if (this.uuid) {
			App.cached[type][id.uuid] = this;
			App.cached[type][id] = this;
		}
		if (this.permalink) {
			App.cached[type][id.permalink] = this;
			App.cached[type][id] = this;
		}
		complete.call(this);
	}

	if (typeof(id) == 'object') {
		//App.cached[type][id.id] = id;

		eval('App.cached[type][id.id] = new '+type+'(id,partComplete)');
		finalid = id.id;

	} else if (!App.cached[type][id]) {
		eval('App.cached[type][id] = new '+type+'(id,partComplete)');

	} else {
		complete.call(App.cached[type][id]);
	}

	// only works sync (Ti)
	return App.cached[type][finalid || id];

};

/**
 * Rounds a float number rounded up with 2 digits.
 *
 * @return float
 */
App.ceil = function(num) {
	num = num*100;
	num = Math.ceil(num);
	return num / 100;
};

App.phone = {

	/**
	 * Add dashes to the phone number, unifying how phone number looks
	 */
	format: function(num) {
		if( num != null ){
			num = num.replace(/^0|^1/,'');
			num = num.replace(/[^\d]*/gi,'');
			num = num.substr(0,10);

			if (num.length >= 7) {
				num = num.replace(/(\d{3})(\d{3})(.*)/, "$1-$2-$3");
			} else if (num.length >= 4) {
				num = num.replace(/(\d{3})(.*)/, "$1-$2");
			}
		}
		return num;
	},
	validate: function(num) {

		if (!num || num.length != 10) {
			return false;
		}

		var
			nums = num.split(''),
			prev;

		for (x in nums) {
			if (!prev) {
				prev = nums[x];
				continue;
			}

			if (nums[x] != prev) {
				return true;
			}
		}

		return false;
	}
};

App.pad = function(number, length) {
	var str = '' + number;
	while (str.length < length) {
		str = '0' + str;
	}
	return str;
};

App.formatTime = function(time) {
	if (!time) {
		return '';
	}

	var vals = time.split(':');

	var pm = false;

	vals[0] = new String(vals[0]);
	vals[1] = vals[1] ? new String(vals[1]) : 0;

	if (vals[0].match(/^[0-9]{3,4}$/i)) {
		vals[1] = vals[0].substr(-2,2);
		vals[0] = vals[0].substr(0,vals[0].length - 2);
	}

	if (vals[0] == '12') {
		pm =  true;
	}

	if (vals[0] == '24') {
		vals[0] = '00';
	}

	if (vals[0] > 12) {
		vals[0] -= 12;
		pm = true;
	} else{
		if (!pm && time.match(/p/i)) {
			pm = true;
		}
		if (pm && time.match(/a/i)) {
			pm = false;
		}
	}

	if (!vals[1]) {
		vals[1] = '00';
	}

	vals[1] = vals[1].replace(/[^0-9]+/,'');
	vals[0] = new String(vals[0]).replace(/[^0-9]+/,'');

	vals[0] = App.pad(vals[0],2);
	vals[1] = App.pad(vals[1],2);

	if (vals[0] == '00') {
		vals[0] = 12;
	}

	return vals.join(':') + (pm ? ' PM' : ' AM');
};

/**
 * Turns AM/PM format in 24h
 *
 * It was storing 12:00 AM as 24:00, or even worse, 12:30 AM as 24:30.
 * 12:00 AM should be 00:00 everywhere
 *
 * @return string
 */
App.unFormatTime = function(time) {
	var part   = time.split(' ');
	part[0]    = part[0].split(':');
	part[0][0] = parseInt(part[0][0], 10);

	if (part[1] == 'PM') {
		if (part[0][0] != 12) {
			part[0][0] = App.pad(part[0][0] + 12,2);
		}
	} else {
		if (part[0][0] == 12) {
			part[0][0] = '00';
		}
	}
	return part[0][0] + ':' + part[0][1];
};

App.cleanInput = function(text) {
	text = App.cleanBad(text);
	var type = arguments[1];
	switch (type) {
		case 'float':
			text = text.replace(/[^0-9\.]+/i,'');
			break;
		case 'text':
		default:
			text = text.replace(/[^a-z0-9\-_ \(\)\+\.\@\%\&\!\;\:\"\'\,\\\/]+/i,'');
			break;
	}

	return text
};

App.cleanBad = function(s) {
	s = s.replace(/\u2018|\u2019|\u201A|\uFFFD/g, "'");
	s = s.replace(/\u201c|\u201d|\u201e/g, '"');
	s = s.replace(/\u02C6/g, '^');
	s = s.replace(/\u2039/g, '<');
	s = s.replace(/\u203A/g, '>');
	s = s.replace(/\u2013/g, '-');
	s = s.replace(/\u2014/g, '--');
	s = s.replace(/\u2026/g, '...');
	s = s.replace(/\u00A9/g, '(c)');
	s = s.replace(/\u00AE/g, '(r)');
	s = s.replace(/\u2122/g, 'TM');
	s = s.replace(/\u00BC/g, '1/4');
	s = s.replace(/\u00BD/g, '1/2');
	s = s.replace(/\u00BE/g, '3/4');
	s = s.replace(/[\u02DC|\u00A0]/g, " ");
	return s;
}<|endoftext|>


<commit_before>//
// 官方插件仅作为 DEMO
// 具体检测能力请根据业务来定制~
//
// 10月底我们将更新一版官方插件，公开一些零规则检测攻击的插件，以及一些应用的专用插件
//

'use strict'
var plugin = new RASP('offical')

var clean = {
    action: 'ignore',
    message: '无风险',
    confidence: 0
}

var xssRegex  = /<script|script>|<iframe|iframe>|javascript:(?!(?:history\.(?:go|back)|void\(0\)))/i
var nameRegex = /\.(jspx?|php[345]?|phtml)\.?$/i
var ntfsRegex = /::\$(DATA|INDEX)$/i
var uaRegex   = /nessus|sqlmap|nikto|havij|netsparker/i
var sqlRegex  = /\bupdatexml\s*\(|\bextractvalue\s*\(|\bunion.*select.*(from|into|benchmark).*\b/i
var sysRegex  = /^\/(etc|proc|sys|var\/log)(\/|$)/
var ognlPayloads = [
    'ognl.OgnlContext',
    'ognl.TypeConverter',
    'ognl.MemberAccess',
    '_memberAccess',
    'ognl.ClassResolver',
    'java.lang.Runtime',
    'java.lang.Class',
    'java.lang.ClassLoader',
    'java.lang.System',
    'java.lang.ProcessBuilder',
    'java.lang.Object', 'java.lang.Shutdown',
    'java.io.File',
    'javax.script.ScriptEngineManager',
    'com.opensymphony.xwork2.ActionContext'
]
var deserializationInvalidClazz = [
    'org.apache.commons.collections.functors.InvokerTransformer',
    'org.apache.commons.collections.functors.InstantiateTransformer',
    'org.apache.commons.collections4.functors.InvokerTransformer',
    'org.apache.commons.collections4.functors.InstantiateTransformer',
    'org.codehaus.groovy.runtime.ConvertedClosure',
    'org.codehaus.groovy.runtime.MethodClosure',
    'org.springframework.beans.factory.ObjectFactory'
]

function canonicalPath (path) {
    return path.replace(/\/\.\//g, '/').replace(/\/+/g, '/')
}

plugin.register('directory', function (params, context) {
    var path = canonicalPath(params.path)
    if (path.indexOf('/../../../') !== -1 || path.indexOf('\\..\\..\\..\\') !== -1) {
        return {
            action: 'block',
            message: '目录遍历攻击',
            confidence: 90
        }
    }

    if (sysRegex.test(path)) {
        return {
            action: 'block',
            message: '读取系统目录',
            confidence: 100
        }
    }
    return clean
})

plugin.register('readFile', function (params, context) {
    var path = canonicalPath(params.path)
    if (path.indexOf('/../../../') !== -1 || path.indexOf('\\..\\..\\..\\') !== -1) {
        return {
            action: 'block',
            message: '目录遍历攻击',
            confidence: 90
        }
    }

    if (sysRegex.test(params.realpath)) {
        return {
            action: 'block',
            message: '读取系统文件',
            confidence: 100
        }
    }
    return clean
})

plugin.register('writeFile', function (params, context) {
    if (nameRegex.test(params.filename) || ntfsRegex.test(params.filename)) {
        return {
            action: 'block',
            message: '尝试上传脚本文件: ' + params.filename,
            confidence: 90
        }
    }
    return clean
})

plugin.register('fileUpload', function (params, context) {
    if (nameRegex.test(params.filename) || ntfsRegex.test(params.filename)) {
        return {
            action: 'block',
            message: '尝试上传脚本文件: ' + params.filename,
            confidence: 90
        }
    }
    return clean
})

plugin.register('sql', function (params, context) {
    if (sqlRegex.test(params.query)) {
        return {
            action: 'block',
            message: 'SQL 注入攻击',
            confidence: 100
        }
    }
    return clean
})

plugin.register('command', function (params, context) {
    return {
        action: 'block',
        message: '尝试执行命令',
        confidence: 100
    }
})

plugin.register('xxe', function (params, context) {
    var items = params.entity.split('://')

    if (items.length >= 2) {
        var protocol = items[0]
        /* eslint-disable */
        var path = items[1];
        /* eslint-enable */
        if (protocol === 'gopher') {
            return {
                action: 'block',
                message: 'SSRF 攻击（gopher 协议)',
                confidence: 100
            }
        }

        if (protocol === 'file') {
            return {
                action: 'log',
                message: '尝试读取外部实体 (file 协议)',
                confidence: 90
            }
        }
    }
    return clean
})

plugin.register('ognl', function (params, context) {
    var ognlExpression = params.expression
    for (var index in ognlPayloads) {
        if (ognlExpression.indexOf(ognlPayloads[index]) > -1) {
            return {
                action: 'block',
                message: '尝试ognl远程命令执行',
                confidence: 100
            }
        }

    }
    return clean
})

plugin.register('deserialization', function (params, context) {
    var clazz = params.clazz
    for (var index in deserializationInvalidClazz) {
        if (clazz === deserializationInvalidClazz[index]) {
            return {
                action: 'block',
                message: '尝试反序列化攻击',
                confidence: 100
            }
        }
    }
    return clean
})

plugin.register('reflection', function(params, context) {
    var title = '异常的执行流'
    var known = {
        'com.thoughtworks.xstream.XStream.unmarshal': 'xstream 反序列化攻击',
        'org.apache.commons.collections4.functors.InvokerTransformer.transform': 'transformer 反序列化攻击'
    }

    params.stack.some(function (method) {
        if (known[method]) {
            title = known[method]
            return true;
        }
    });

    return {
        action:  'block',
        message: title + ':' + params.clazz + '.' + params.method,
        confidence: 100
    }
})

plugin.register('request', function(params, context) {
    // xss 检测 DEMO
    var parameters = context.parameter;
    var message    = '';

    Object.keys(parameters).some(function (name) {
        parameters[name].some(function (value) {
            if (xssRegex.test(value)) {
                message = 'XSS 攻击: ' + value;
                return true;
            }
        });

        if (message.length) {
            return true;
        }
    });

    if (! message.length) {
        return clean;
    }

    return {
        action: 'block',
        message: message
    }
})

plugin.log('初始化成功')
<commit_msg>更新插件检测逻辑<commit_after>//
// 官方插件仅作为 DEMO
// 具体检测能力请根据业务来定制~
//
// 10月底我们将更新一版官方插件，公开一些零规则检测攻击的插件，以及一些应用的专用插件
//

'use strict'
var plugin = new RASP('offical')

var clean = {
    action: 'ignore',
    message: '无风险',
    confidence: 0
}

var forcefulBrowsing = {
    dotFiles: /\.(gz|7z|xz|tar|rar|zip|sql|db)$/,
    systemFiles: /^\/(etc|proc|sys|var\/log)(\/|$)/,
    unwanted: [
        // user files
        '.DS_Store',
        'id_rsa', 'id_rsa.pub', 'known_hosts', 'authorized_keys',
        '.bash_history', '.csh_history', '.zsh_history', '.mysql_history',

        // project files
        '.htaccess', '.user.ini',

        'web.config', 'web.xml', 'build.property.xml', 'bower.json',
        'Gemfile', 'Gemfile.lock',
        '.gitignore',
        'error_log', 'error.log', 'nohup.out',
    ],
    absolutePaths: [
        '/etc/shadow',
        '/etc/hosts',
        '/etc/apache2/apache2.conf',
        '/root/.bash_history',
        '/root/.bash_profile',
    ]
}

var scannerUA = [
    "attack", "scan", "vulnerability", "injection", "xss", "exploit", "grabber",
    "cgichk", "bsqlbf", "sqlmap", "nessus", "arachni", "metis", "sql power injector",
    "bilbo", "absinthe", "black widow", "n-stealth", "brutus", "webtrends security analyzer",
    "netsparker", "jaascois", "pmafind", ".nasl", "nsauditor", "paros", "dirbuster",
    "pangolin", "nmap nse", "sqlninja", "nikto", "webinspect", "blackwidow",
    "grendel-scan", "havij", "w3af", "hydra"
]

var xssRegex        = /<script|script>|<iframe|iframe>|javascript:(?!(?:history\.(?:go|back)|void\(0\)))/i
var scriptFileRegex = /\.(jspx?|php[345]?|phtml)\.?$/i
var ntfsRegex       = /::\$(DATA|INDEX)$/i

var ognlPayloads = [
    'ognl.OgnlContext',
    'ognl.TypeConverter',
    'ognl.MemberAccess',
    '_memberAccess',
    'ognl.ClassResolver',
    'java.lang.Runtime',
    'java.lang.Class',
    'java.lang.ClassLoader',
    'java.lang.System',
    'java.lang.ProcessBuilder',
    'java.lang.Object',
    'java.lang.Shutdown',
    'java.io.File',
    'javax.script.ScriptEngineManager',
    'com.opensymphony.xwork2.ActionContext'
]

var deserializationInvalidClazz = [
    'org.apache.commons.collections.functors.InvokerTransformer',
    'org.apache.commons.collections.functors.InstantiateTransformer',
    'org.apache.commons.collections4.functors.InvokerTransformer',
    'org.apache.commons.collections4.functors.InstantiateTransformer',
    'org.codehaus.groovy.runtime.ConvertedClosure',
    'org.codehaus.groovy.runtime.MethodClosure',
    'org.springframework.beans.factory.ObjectFactory',
    'xalan.internal.xsltc.trax.TemplatesImpl'
]

function canonicalPath (path) {
    return path.replace(/\/\.\//g, '/').replace(/\/+/g, '/')
}

plugin.register('directory', function (params, context) {
    var path = canonicalPath(params.path)

    // 简单判断目录遍历，可结合业务定制: e.g 不能超出应用根目录
    if (path.indexOf('/../../../') !== -1 || path.indexOf('\\..\\..\\..\\') !== -1) {
        return {
            action: 'block',
            message: '目录遍历攻击',
            confidence: 90
        }
    }

    if (forcefulBrowsing.systemFiles.test(path)) {
        return {
            action: 'block',
            message: '读取系统目录',
            confidence: 100
        }
    }
    return clean
})

plugin.register('readFile', function (params, context) {
    // 检查是否为成功的目录扫描
    var filename_1 = context.url.replace(/.*\//, '')
    var filename_2 = params.realpath.replace(/.*\//, '')

    plugin.log('readFile:', filename_1, filename_2)

    if (filename_1 == filename_2) {
        var matched = false

        // 尝试下载压缩包、SQL文件等等
        if (forcefulBrowsing.dotFiles.test(filename_1)) {
            matched = true
        } else {
            // 尝试访问敏感文件
            for (var i = 0; i < forcefulBrowsing.unwanted; i ++) {
                if (forcefulBrowsing.unwanted[i] == filename_1) {
                    matched = true
                }
            }
        }

        if (matched) {
            return {
                action: 'block',
                message: '尝试下载敏感文件(' + context.method.toUpperCase() + ' 方式): ' + params.realpath,

                // 如果是HEAD方式下载敏感文件，100% 扫描器攻击
                confidence: context.method == 'head' ? 100 : 90
            }
        }
    }

    // 如果使用绝对路径访问敏感文件
    // 判定为 webshell
    if (params.realpath == params.path) {
        for (var j = 0; j < forcefulBrowsing.absolutePaths; j ++) {
            if (forcefulBrowsing.absolutePaths[i] == params.realpath) {
                return {
                    action:  'block',
                    message: '疑似webshell - 尝试读取系统文件: ' + params.realpath
                }
            }
        }
    }

    // 检查目录遍历（弱）
    var path = canonicalPath(params.path)
    if (path.indexOf('/../../../') !== -1 || path.indexOf('\\..\\..\\..\\') !== -1) {
        return {
            action: 'block',
            message: '目录遍历攻击',
            confidence: 90
        }
    }

    if (forcefulBrowsing.systemFiles.test(params.realpath)) {
        return {
            action: 'block',
            message: '读取系统文件',
            confidence: 100
        }
    }
    return clean
})

plugin.register('writeFile', function (params, context) {
    if (scriptFileRegex.test(params.filename) || ntfsRegex.test(params.filename)) {
        return {
            action: 'block',
            message: '尝试上传脚本文件: ' + params.filename,
            confidence: 90
        }
    }
    return clean
})

plugin.register('fileUpload', function (params, context) {
    if (scriptFileRegex.test(params.filename) || ntfsRegex.test(params.filename)) {
        return {
            action: 'block',
            message: '尝试上传脚本文件: ' + params.filename,
            confidence: 90
        }
    }
    return clean
})

// [[ 近期调整~ ]]
plugin.register('sql', function (params, context) {
    // SQLi 检测 demo
    var sqlRegex = /\bupdatexml\s*\(|\bextractvalue\s*\(|\bunion.*select.*(from|into|benchmark).*\b/i

    if (sqlRegex.test(params.query)) {
        return {
            action: 'block',
            message: 'SQL 注入攻击',
            confidence: 100
        }
    }
    return clean
})

plugin.register('command', function (params, context) {
    return {
        action: 'block',
        message: '尝试执行命令',
        confidence: 100
    }
})

plugin.register('xxe', function (params, context) {
    var items = params.entity.split('://')

    if (items.length >= 2) {
        var protocol = items[0]

        if (protocol === 'gopher' || protocol === 'dict') {
            return {
                action: 'block',
                message: 'SSRF 攻击 (' + protocol + ' 协议)',
                confidence: 100
            }
        }

        if (protocol === 'file') {
            return {
                action: 'log',
                message: '尝试读取外部实体 (file 协议)',
                confidence: 90
            }
        }
    }
    return clean
})

plugin.register('ognl', function (params, context) {
    var ognlExpression = params.expression
    for (var index in ognlPayloads) {
        if (ognlExpression.indexOf(ognlPayloads[index]) > -1) {
            return {
                action: 'block',
                message: '尝试ognl远程命令执行',
                confidence: 100
            }
        }

    }
    return clean
})

// [[ 近期调整~ ]]
plugin.register('deserialization', function (params, context) {
    var clazz = params.clazz
    for (var index in deserializationInvalidClazz) {
        if (clazz === deserializationInvalidClazz[index]) {
            return {
                action: 'block',
                message: '尝试反序列化攻击',
                confidence: 100
            }
        }
    }
    return clean
})

plugin.register('reflection', function(params, context) {
    var title = '异常的执行流'
    var known = {
        'com.thoughtworks.xstream.XStream.unmarshal': 'xstream 反序列化攻击',
        'org.apache.commons.collections4.functors.InvokerTransformer.transform': 'transformer 反序列化攻击'
    }

    // 是否为已知类型的反序列化攻击？
    params.stack.some(function (method) {
        if (known[method]) {
            title = known[method]
            return true;
        }
    });

    return {
        action:  'block',
        message: title + ':' + params.clazz + '.' + params.method,
        confidence: 100
    }
})

// [[ 近期调整~ ]]
plugin.register('request', function(params, context) {

    // 已知扫描 UA
    var foundScanner = false

    if (context.header['acunetix-product'] || context.header['x-wipp']) {
        foundScanner = true
    } else {
        var ua = context.header['user-agent']
        if (ua) {
            for (var i = 0; i < scannerUA.length; i ++) {
                if (ua.indexOf(scannerUA[i].toLowerCase()) != -1) {
                    foundScanner = true
                    break
                }
            }
        }
    }

    if (foundScanner) {
        return {
            action:  'block',
            message: '已知的扫描器UA: ' + scannerUA[i],
            confidence: 90
        }
    }

    // xss 检测 DEMO
    var parameters = context.parameter;
    var message    = '';

    Object.keys(parameters).some(function (name) {
        parameters[name].some(function (value) {
            if (xssRegex.test(value)) {
                message = 'XSS 攻击: ' + value;
                return true;
            }
        });

        if (message.length) {
            return true;
        }
    });

    if (! message.length) {
        return clean;
    }

    return {
        action: 'block',
        message: message
    }
})

plugin.log('初始化成功')
<|endoftext|>


<commit_before>'use strict';
/**
 * Messages resource
 * @module resources/messages
 */

var utils = require('../utils');

module.exports = function(request) {
  return {

    /**
     * Send a message
     *
     * @param  {String}   conversationId  Conversation ID
     * @param  {Object}   body            Message body
     * @param  {Function} callback        Callback function
     */
    send: send.bind(null, null),

    /**
     * Send a message with de-duplicating UUID
     *
     * @param  {String}   dedupe          UUID
     * @param  {String}   conversationId  Conversation ID
     * @param  {Object}   body            Message body
     * @param  {Function} callback        Callback function
     */
    sendDedupe: send,

    /**
     * Send a plain text message from `userId`
     *
     * @param  {String}   conversationId Conversation ID
     * @param  {String}   userId         User ID
     * @param  {String}   text           Message text
     * @param  {Function} callback       Callback function
     */
    sendTextFromUser: function(conversationId, userId, text, callback) {
      send(null, conversationId, utils.messageText('user_id', userId, text), callback);
    },

    /**
     * Send a plain text message from `name`
     *
     * @param  {String}   conversationId Conversation ID
     * @param  {String}   name           Sender name
     * @param  {String}   text           Message text
     * @param  {Function} callback       Callback function
     */
    sendTextFromName: function(conversationId, name, text, callback) {
      send(null, conversationId, utils.messageText('name', name, text), callback);
    }
  };

  function send(dedupe, conversationId, body, callback) {
    if (dedupe !== null && !utils.toUUID(dedupe)) return callback(new Error(utils.i18n.dedupe));
    conversationId = utils.toUUID(conversationId);
    if (!conversationId) return callback(new Error(utils.i18n.messages.cid));
    if (!body || typeof body !== 'object') return callback(new Error(utils.i18n.messages.body));
    utils.debug('Message send: ' + conversationId);

    request.post({
      path: '/conversations/' + conversationId + '/messages',
      body: body,
      dedupe: dedupe
    }, callback || utils.nop);
  }
};
<commit_msg>Breaking method name change deprecation<commit_after>'use strict';
/**
 * Messages resource
 * @module resources/messages
 */

var utils = require('../utils');

module.exports = function(request) {
  return {

    /**
     * Send a message
     *
     * @param  {String}   conversationId  Conversation ID
     * @param  {Object}   body            Message body
     * @param  {Function} callback        Callback function
     */
    send: send.bind(null, null),

    /**
     * Send a message with de-duplicating UUID
     *
     * @param  {String}   dedupe          UUID
     * @param  {String}   conversationId  Conversation ID
     * @param  {Object}   body            Message body
     * @param  {Function} callback        Callback function
     */
    sendDedupe: send,

    /**
     * Send a plain text message from `userId`
     *
     * @param  {String}   conversationId Conversation ID
     * @param  {String}   userId         User ID
     * @param  {String}   text           Message text
     * @param  {Function} callback       Callback function
     */
    sendTextFromUser: fromUser,
    sendTexFromUser: fromUser, // Deprecated

    /**
     * Send a plain text message from `name`
     *
     * @param  {String}   conversationId Conversation ID
     * @param  {String}   name           Sender name
     * @param  {String}   text           Message text
     * @param  {Function} callback       Callback function
     */
    sendTextFromName: fromName,
    sendTexFromName: fromName, // Deprecated

    /**
     * Retrieve messages in a conversation
     *
     * @param  {String}   conversationId  Conversation ID
     * @param  {Function} callback        Callback function
     */
    getAll: function(conversationId, callback) {
      conversationId = utils.toUUID(conversationId);
      if (!conversationId) return callback(new Error(utils.i18n.messages.cid));
      utils.debug('Message getAll: ' + conversationId);

      request.get({
        path: '/conversations/' + conversationId + '/messages'
      }, callback || utils.nop);
    },

    /**
     * Retrieve messages in a conversation from a user
     *
     * @param  {String}   userId          User ID
     * @param  {String}   conversationId  Conversation ID
     * @param  {Function} callback        Callback function
     */
    getAllFromUser: function(userId, conversationId, callback) {
      if (!userId) return callback(new Error(utils.i18n.messages.userId));
      conversationId = utils.toUUID(conversationId);
      if (!conversationId) return callback(new Error(utils.i18n.messages.cid));
      utils.debug('Message getAllFromUser: ' + userId + ', ' + conversationId);

      request.get({
        path: '/users/' + querystring.escape(userId) + '/conversations/' + conversationId + '/messages'
      }, callback || utils.nop);
    },

    /**
     * Retrieve a message in a conversation from a user
     *
     * @param  {String}   userId          User ID
     * @param  {String}   messageId       Message ID
     * @param  {Function} callback        Callback function
     */
    getFromUser: function(userId, messageId, callback) {
      if (!userId) return callback(new Error(utils.i18n.messages.userId));
      messageId = utils.toUUID(messageId);
      if (!messageId) return callback(new Error(utils.i18n.messages.mid));
      utils.debug('Message getFromUser: ' + userId + ', ' + messageId);

      request.get({
        path: '/users/' + querystring.escape(userId) + '/messages/' + messageId
      }, callback || utils.nop);
    }
  };

  function fromUser(conversationId, userId, text, callback) {
    send(null, conversationId, utils.messageText('user_id', userId, text), callback);
  }
  function fromName(conversationId, name, text, callback) {
    send(null, conversationId, utils.messageText('name', name, text), callback);
  }

  function send(dedupe, conversationId, body, callback) {
    if (dedupe !== null && !utils.toUUID(dedupe)) return callback(new Error(utils.i18n.dedupe));
    conversationId = utils.toUUID(conversationId);
    if (!conversationId) return callback(new Error(utils.i18n.messages.cid));
    if (!body || typeof body !== 'object') return callback(new Error(utils.i18n.messages.body));
    utils.debug('Message send: ' + conversationId);

    request.post({
      path: '/conversations/' + conversationId + '/messages',
      body: body,
      dedupe: dedupe
    }, callback || utils.nop);
  }
};
<|endoftext|>


<commit_before>var NanoFeed = (NanoFeed || function(urls, options, callback) {
  'use strict';

  if (typeof options === 'function') {
    callback = options;
    options = {};
  }

  if (typeof urls === 'string') {
    urls = [urls];
  }
  else if (!Array.isArray(urls) || !urls.length) {
    return NanoFeed;
  }

  options = extend({
    title: true,
    link: true,
    date: false,
    description: false,
    qty: 5
  }, options);

  // Optimized cross-product make simple array of results in 'query.results.results.item'
  // All tables Env allows select from 'query.multi'
  var config = {
    baseUrl: '//query.yahooapis.com/v1/public/yql?format=json&callback=&' +
             'crossProduct=optimized&env=http://datatables.org/alltables.env',
    template: 'SELECT {COLS} FROM query.multi WHERE queries=\'' +
    'SELECT title,link,pubDate,description ' +
    'FROM rss ' +
    'WHERE url in ("{URLS}")' +
    '|UNIQUE(field="title",hideRepeatCount="true")' +
    '|UNIQUE(field="link",hideRepeatCount="true")' +
    '|SORT(field="pubDate",descending="true")' +
    '|TRUNCATE({QTY})\''
  };

  var query = config.template
    .replace('{COLS}', getQueryColumns(options))
    .replace('{URLS}', urls.join('","'))
    .replace('{QTY}', options.qty);

  var url = config.baseUrl + '&q=' + encodeURIComponent(query);

  getJSON(url, function (json) {
    if (json && json.query && json.query.count >= 0) {
      var data = [];
      if (json.query.count) {
        try {
          var result = json.query.results.results.item;
          var data = result.length ? result : [result];

          if (options.date) {
            data.forEach(function (item) {
              item.pubDate = new Date(item.pubDate);
            });
          }
        }
        catch (e) {
        }
      }
      return callback(data);
    }
  });

  return NanoFeed;

  //---- private ----------------------------------------------

  function extend(a, b) {
    for (var key in b) {
      if (b.hasOwnProperty(key)) {
        a[key] = b[key];
      }
    }
    return a;
  }

  function getQueryColumns(options) {
    var cols = '';

    cols += options.title ? 'item.title,': '';
    cols += options.link ? 'item.link,': '';
    cols += options.date ? 'item.pubDate,': '';
    cols += options.description ? 'item.description,': '';

    return cols.slice(0, -1) || 'item.title';
  }

  function getJSON(url, callback) {
    var request = new XMLHttpRequest();
    request.onload = function () {
      var data;
      try {
        data = JSON.parse(request.responseText);
      }
      catch (e){
      }
      callback(data);
    };
    request.open('GET', url, true);
    request.send();
  }
});<commit_msg>removed re-declaration of var data<commit_after>var NanoFeed = (NanoFeed || function(urls, options, callback) {
  'use strict';

  if (typeof options === 'function') {
    callback = options;
    options = {};
  }

  if (typeof urls === 'string') {
    urls = [urls];
  }
  else if (!Array.isArray(urls) || !urls.length) {
    return NanoFeed;
  }

  options = extend({
    title: true,
    link: true,
    date: false,
    description: false,
    qty: 5
  }, options);

  // Optimized cross-product make simple array of results in 'query.results.results.item'
  // All tables Env allows select from 'query.multi'
  var config = {
    baseUrl: '//query.yahooapis.com/v1/public/yql?format=json&callback=&' +
             'crossProduct=optimized&env=http://datatables.org/alltables.env',
    template: 'SELECT {COLS} FROM query.multi WHERE queries=\'' +
    'SELECT title,link,pubDate,description ' +
    'FROM rss ' +
    'WHERE url in ("{URLS}")' +
    '|UNIQUE(field="title",hideRepeatCount="true")' +
    '|UNIQUE(field="link",hideRepeatCount="true")' +
    '|SORT(field="pubDate",descending="true")' +
    '|TRUNCATE({QTY})\''
  };

  var query = config.template
    .replace('{COLS}', getQueryColumns(options))
    .replace('{URLS}', urls.join('","'))
    .replace('{QTY}', options.qty);

  var url = config.baseUrl + '&q=' + encodeURIComponent(query);

  getJSON(url, function (json) {
    if (json && json.query && json.query.count >= 0) {
      var data = [];
      if (json.query.count) {
        try {
          var result = json.query.results.results.item;
          data = result.length ? result : [result];

          if (options.date) {
            data.forEach(function (item) {
              item.pubDate = new Date(item.pubDate);
            });
          }
        }
        catch (e) {
        }
      }
      return callback(data);
    }
  });

  return NanoFeed;

  //---- private ----------------------------------------------

  function extend(a, b) {
    for (var key in b) {
      if (b.hasOwnProperty(key)) {
        a[key] = b[key];
      }
    }
    return a;
  }

  function getQueryColumns(options) {
    var cols = '';

    cols += options.title ? 'item.title,': '';
    cols += options.link ? 'item.link,': '';
    cols += options.date ? 'item.pubDate,': '';
    cols += options.description ? 'item.description,': '';

    return cols.slice(0, -1) || 'item.title';
  }

  function getJSON(url, callback) {
    var request = new XMLHttpRequest();
    request.onload = function () {
      var data;
      try {
        data = JSON.parse(request.responseText);
      }
      catch (e){
      }
      callback(data);
    };
    request.open('GET', url, true);
    request.send();
  }
});
<|endoftext|>


<commit_before>// @inheritedComponent Portal

import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { polyfill } from 'react-lifecycles-compat';
import warning from 'warning';
import keycode from 'keycode';
import activeElement from 'dom-helpers/activeElement';
import contains from 'dom-helpers/query/contains';
import inDOM from 'dom-helpers/util/inDOM';
import ownerDocument from 'dom-helpers/ownerDocument';
import RootRef from '../internal/RootRef';
import Portal from '../Portal';
import { createChainedFunction } from '../utils/helpers';
import withStyles from '../styles/withStyles';
import ModalManager from './ModalManager';
import Backdrop from './Backdrop';

function getContainer(container, defaultContainer) {
  container = typeof container === 'function' ? container() : container;
  return ReactDOM.findDOMNode(container) || defaultContainer;
}

function getHasTransition(props) {
  return props.children ? props.children.props.hasOwnProperty('in') : false;
}

export const styles = theme => ({
  root: {
    display: 'flex',
    width: '100%',
    height: '100%',
    position: 'fixed',
    zIndex: theme.zIndex.modal,
    top: 0,
    left: 0,
  },
  hidden: {
    visibility: 'hidden',
  },
});

class Modal extends React.Component {
  static getDerivedStateFromProps(nextProps) {
    if (nextProps.open) {
      return {
        exited: false,
      };
    } else if (!getHasTransition(nextProps)) {
      // Otherwise let handleExited take care of marking exited.
      return {
        exited: true,
      };
    }

    return null;
  }

  constructor(props, context) {
    super(props, context);

    this.state = {
      exited: !this.props.open,
    };
  }

  componentDidMount() {
    this.mounted = true;
    if (this.props.open) {
      this.handleOpen();
    }
  }

  componentDidUpdate(prevProps) {
    if (!prevProps.open && this.props.open) {
      this.checkForFocus();
    }

    if (prevProps.open && !this.props.open && !getHasTransition(this.props)) {
      // Otherwise handleExited will call this.
      this.handleClose();
    } else if (!prevProps.open && this.props.open) {
      this.handleOpen();
    }
  }

  componentWillUnmount() {
    this.mounted = false;

    if (this.props.open || (getHasTransition(this.props) && !this.state.exited)) {
      this.handleClose();
    }
  }

  dialogElement = null;
  mounted = false;
  mountNode = null;

  handleRendered = () => {
    this.autoFocus();

    if (this.props.onRendered) {
      this.props.onRendered();
    }
  };

  handleOpen = () => {
    const doc = ownerDocument(this.mountNode);
    const container = getContainer(this.props.container, doc.body);

    this.props.manager.add(this, container);
    doc.addEventListener('keydown', this.handleDocumentKeyDown);
    doc.addEventListener('focus', this.enforceFocus, true);
  };

  handleClose = () => {
    this.props.manager.remove(this);
    const doc = ownerDocument(this.mountNode);
    doc.removeEventListener('keydown', this.handleDocumentKeyDown);
    doc.removeEventListener('focus', this.enforceFocus);
    this.restoreLastFocus();
  };

  handleExited = () => {
    this.setState({ exited: true });
    this.handleClose();
  };

  handleBackdropClick = event => {
    if (event.target !== event.currentTarget) {
      return;
    }

    if (this.props.onBackdropClick) {
      this.props.onBackdropClick(event);
    }

    if (!this.props.disableBackdropClick && this.props.onClose) {
      this.props.onClose(event, 'backdropClick');
    }
  };

  handleDocumentKeyDown = event => {
    if (!this.isTopModal() || keycode(event) !== 'esc') {
      return;
    }

    if (this.props.onEscapeKeyDown) {
      this.props.onEscapeKeyDown(event);
    }

    if (!this.props.disableEscapeKeyDown && this.props.onClose) {
      this.props.onClose(event, 'escapeKeyDown');
    }
  };

  checkForFocus = () => {
    if (inDOM) {
      this.lastFocus = activeElement();
    }
  };

  autoFocus() {
    if (this.props.disableAutoFocus) {
      return;
    }

    const currentActiveElement = activeElement(ownerDocument(this.mountNode));

    if (this.dialogElement && !contains(this.dialogElement, currentActiveElement)) {
      this.lastFocus = currentActiveElement;

      if (!this.dialogElement.hasAttribute('tabIndex')) {
        warning(
          false,
          [
            'Material-UI: the modal content node does not accept focus.',
            'For the benefit of assistive technologies, ' +
              'the tabIndex of the node is being set to "-1".',
          ].join('\n'),
        );
        this.dialogElement.setAttribute('tabIndex', -1);
      }

      this.dialogElement.focus();
    }
  }

  restoreLastFocus() {
    if (this.props.disableRestoreFocus) {
      return;
    }

    if (this.lastFocus) {
      this.lastFocus.focus();
      this.lastFocus = null;
    }
  }

  enforceFocus = () => {
    if (this.props.disableEnforceFocus || !this.mounted || !this.isTopModal()) {
      return;
    }

    const currentActiveElement = activeElement(ownerDocument(this.mountNode));

    if (this.dialogElement && !contains(this.dialogElement, currentActiveElement)) {
      this.dialogElement.focus();
    }
  };

  isTopModal() {
    return this.props.manager.isTopModal(this);
  }

  render() {
    const {
      BackdropComponent,
      BackdropProps,
      children,
      classes,
      className,
      container,
      disableAutoFocus,
      disableBackdropClick,
      disableEnforceFocus,
      disableEscapeKeyDown,
      disableRestoreFocus,
      hideBackdrop,
      keepMounted,
      onBackdropClick,
      onClose,
      onEscapeKeyDown,
      onRendered,
      open,
      manager,
      ...other
    } = this.props;
    const { exited } = this.state;
    const hasTransition = getHasTransition(this.props);
    const childProps = {};

    if (!keepMounted && !open && (!hasTransition || exited)) {
      return null;
    }

    // It's a Transition like component
    if (hasTransition) {
      childProps.onExited = createChainedFunction(this.handleExited, children.props.onExited);
    }

    if (children.props.role === undefined) {
      childProps.role = children.props.role || 'document';
    }

    if (children.props.tabIndex === undefined) {
      childProps.tabIndex = children.props.tabIndex || '-1';
    }

    return (
      <Portal
        ref={node => {
          this.mountNode = node ? node.getMountNode() : node;
        }}
        container={container}
        onRendered={this.handleRendered}
      >
        <div
          data-mui-test="Modal"
          className={classNames(classes.root, className, {
            [classes.hidden]: exited,
          })}
          {...other}
        >
          {hideBackdrop ? null : (
            <BackdropComponent open={open} onClick={this.handleBackdropClick} {...BackdropProps} />
          )}
          <RootRef
            rootRef={node => {
              this.dialogElement = node;
            }}
          >
            {React.cloneElement(children, childProps)}
          </RootRef>
        </div>
      </Portal>
    );
  }
}

Modal.propTypes = {
  /**
   * A backdrop component. Useful for custom backdrop rendering.
   */
  BackdropComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
  /**
   * Properties applied to the `Backdrop` element.
   */
  BackdropProps: PropTypes.object,
  /**
   * A single child content element.
   */
  children: PropTypes.element,
  /**
   * Useful to extend the style applied to components.
   */
  classes: PropTypes.object.isRequired,
  /**
   * @ignore
   */
  className: PropTypes.string,
  /**
   * A node, component instance, or function that returns either.
   * The `container` will have the portal children appended to it.
   */
  container: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
  /**
   * If `true`, the modal will not automatically shift focus to itself when it opens, and
   * replace it to the last focused element when it closes.
   * This also works correctly with any modal children that have the `disableAutoFocus` prop.
   *
   * Generally this should never be set to `true` as it makes the modal less
   * accessible to assistive technologies, like screen readers.
   */
  disableAutoFocus: PropTypes.bool,
  /**
   * If `true`, clicking the backdrop will not fire any callback.
   */
  disableBackdropClick: PropTypes.bool,
  /**
   * If `true`, the modal will not prevent focus from leaving the modal while open.
   *
   * Generally this should never be set to `true` as it makes the modal less
   * accessible to assistive technologies, like screen readers.
   */
  disableEnforceFocus: PropTypes.bool,
  /**
   * If `true`, hitting escape will not fire any callback.
   */
  disableEscapeKeyDown: PropTypes.bool,
  /**
   * If `true`, the modal will not restore focus to previously focused element once
   * modal is hidden.
   */
  disableRestoreFocus: PropTypes.bool,
  /**
   * If `true`, the backdrop is not rendered.
   */
  hideBackdrop: PropTypes.bool,
  /**
   * Always keep the children in the DOM.
   * This property can be useful in SEO situation or
   * when you want to maximize the responsiveness of the Modal.
   */
  keepMounted: PropTypes.bool,
  /**
   * A modal manager used to track and manage the state of open
   * Modals. Useful when customizing how modals interact within a container.
   */
  manager: PropTypes.object,
  /**
   * Callback fired when the backdrop is clicked.
   */
  onBackdropClick: PropTypes.func,
  /**
   * Callback fired when the component requests to be closed.
   * The `reason` parameter can optionally be used to control the response to `onClose`.
   *
   * @param {object} event The event source of the callback
   * @param {string} reason Can be:`"escapeKeyDown"`, `"backdropClick"`
   */
  onClose: PropTypes.func,
  /**
   * Callback fired when the escape key is pressed,
   * `disableEscapeKeyDown` is false and the modal is in focus.
   */
  onEscapeKeyDown: PropTypes.func,
  /**
   * Callback fired once the children has been mounted into the `container`.
   * It signals that the `open={true}` property took effect.
   */
  onRendered: PropTypes.func,
  /**
   * If `true`, the modal is open.
   */
  open: PropTypes.bool.isRequired,
};

Modal.defaultProps = {
  disableAutoFocus: false,
  disableBackdropClick: false,
  disableEnforceFocus: false,
  disableEscapeKeyDown: false,
  disableRestoreFocus: false,
  hideBackdrop: false,
  keepMounted: false,
  // Modals don't open on the server so this won't conflict with concurrent requests.
  manager: new ModalManager(),
  BackdropComponent: Backdrop,
};

export default withStyles(styles, { flip: false, name: 'MuiModal' })(polyfill(Modal));
<commit_msg>[Modal] Prevent IE11 from crashing on modal close (#11115)<commit_after>// @inheritedComponent Portal

import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { polyfill } from 'react-lifecycles-compat';
import warning from 'warning';
import keycode from 'keycode';
import activeElement from 'dom-helpers/activeElement';
import contains from 'dom-helpers/query/contains';
import inDOM from 'dom-helpers/util/inDOM';
import ownerDocument from 'dom-helpers/ownerDocument';
import RootRef from '../internal/RootRef';
import Portal from '../Portal';
import { createChainedFunction } from '../utils/helpers';
import withStyles from '../styles/withStyles';
import ModalManager from './ModalManager';
import Backdrop from './Backdrop';

function getContainer(container, defaultContainer) {
  container = typeof container === 'function' ? container() : container;
  return ReactDOM.findDOMNode(container) || defaultContainer;
}

function getHasTransition(props) {
  return props.children ? props.children.props.hasOwnProperty('in') : false;
}

export const styles = theme => ({
  root: {
    display: 'flex',
    width: '100%',
    height: '100%',
    position: 'fixed',
    zIndex: theme.zIndex.modal,
    top: 0,
    left: 0,
  },
  hidden: {
    visibility: 'hidden',
  },
});

class Modal extends React.Component {
  static getDerivedStateFromProps(nextProps) {
    if (nextProps.open) {
      return {
        exited: false,
      };
    } else if (!getHasTransition(nextProps)) {
      // Otherwise let handleExited take care of marking exited.
      return {
        exited: true,
      };
    }

    return null;
  }

  constructor(props, context) {
    super(props, context);

    this.state = {
      exited: !this.props.open,
    };
  }

  componentDidMount() {
    this.mounted = true;
    if (this.props.open) {
      this.handleOpen();
    }
  }

  componentDidUpdate(prevProps) {
    if (!prevProps.open && this.props.open) {
      this.checkForFocus();
    }

    if (prevProps.open && !this.props.open && !getHasTransition(this.props)) {
      // Otherwise handleExited will call this.
      this.handleClose();
    } else if (!prevProps.open && this.props.open) {
      this.handleOpen();
    }
  }

  componentWillUnmount() {
    this.mounted = false;

    if (this.props.open || (getHasTransition(this.props) && !this.state.exited)) {
      this.handleClose();
    }
  }

  dialogElement = null;
  mounted = false;
  mountNode = null;

  handleRendered = () => {
    this.autoFocus();

    if (this.props.onRendered) {
      this.props.onRendered();
    }
  };

  handleOpen = () => {
    const doc = ownerDocument(this.mountNode);
    const container = getContainer(this.props.container, doc.body);

    this.props.manager.add(this, container);
    doc.addEventListener('keydown', this.handleDocumentKeyDown);
    doc.addEventListener('focus', this.enforceFocus, true);
  };

  handleClose = () => {
    this.props.manager.remove(this);
    const doc = ownerDocument(this.mountNode);
    doc.removeEventListener('keydown', this.handleDocumentKeyDown);
    doc.removeEventListener('focus', this.enforceFocus);
    this.restoreLastFocus();
  };

  handleExited = () => {
    this.setState({ exited: true });
    this.handleClose();
  };

  handleBackdropClick = event => {
    if (event.target !== event.currentTarget) {
      return;
    }

    if (this.props.onBackdropClick) {
      this.props.onBackdropClick(event);
    }

    if (!this.props.disableBackdropClick && this.props.onClose) {
      this.props.onClose(event, 'backdropClick');
    }
  };

  handleDocumentKeyDown = event => {
    if (!this.isTopModal() || keycode(event) !== 'esc') {
      return;
    }

    if (this.props.onEscapeKeyDown) {
      this.props.onEscapeKeyDown(event);
    }

    if (!this.props.disableEscapeKeyDown && this.props.onClose) {
      this.props.onClose(event, 'escapeKeyDown');
    }
  };

  checkForFocus = () => {
    if (inDOM) {
      this.lastFocus = activeElement();
    }
  };

  autoFocus() {
    if (this.props.disableAutoFocus) {
      return;
    }

    const currentActiveElement = activeElement(ownerDocument(this.mountNode));

    if (this.dialogElement && !contains(this.dialogElement, currentActiveElement)) {
      this.lastFocus = currentActiveElement;

      if (!this.dialogElement.hasAttribute('tabIndex')) {
        warning(
          false,
          [
            'Material-UI: the modal content node does not accept focus.',
            'For the benefit of assistive technologies, ' +
              'the tabIndex of the node is being set to "-1".',
          ].join('\n'),
        );
        this.dialogElement.setAttribute('tabIndex', -1);
      }

      this.dialogElement.focus();
    }
  }

  restoreLastFocus() {
    if (this.props.disableRestoreFocus) {
      return;
    }

    if (this.lastFocus) {
      // Not all elements in IE11 have a focus method.
      // Because IE11 market share is low, we accept the restore focus being broken
      // and we silent the issue.
      if (this.lastFocus.focus) {
        this.lastFocus.focus();
      }

      this.lastFocus = null;
    }
  }

  enforceFocus = () => {
    if (this.props.disableEnforceFocus || !this.mounted || !this.isTopModal()) {
      return;
    }

    const currentActiveElement = activeElement(ownerDocument(this.mountNode));

    if (this.dialogElement && !contains(this.dialogElement, currentActiveElement)) {
      this.dialogElement.focus();
    }
  };

  isTopModal() {
    return this.props.manager.isTopModal(this);
  }

  render() {
    const {
      BackdropComponent,
      BackdropProps,
      children,
      classes,
      className,
      container,
      disableAutoFocus,
      disableBackdropClick,
      disableEnforceFocus,
      disableEscapeKeyDown,
      disableRestoreFocus,
      hideBackdrop,
      keepMounted,
      onBackdropClick,
      onClose,
      onEscapeKeyDown,
      onRendered,
      open,
      manager,
      ...other
    } = this.props;
    const { exited } = this.state;
    const hasTransition = getHasTransition(this.props);
    const childProps = {};

    if (!keepMounted && !open && (!hasTransition || exited)) {
      return null;
    }

    // It's a Transition like component
    if (hasTransition) {
      childProps.onExited = createChainedFunction(this.handleExited, children.props.onExited);
    }

    if (children.props.role === undefined) {
      childProps.role = children.props.role || 'document';
    }

    if (children.props.tabIndex === undefined) {
      childProps.tabIndex = children.props.tabIndex || '-1';
    }

    return (
      <Portal
        ref={node => {
          this.mountNode = node ? node.getMountNode() : node;
        }}
        container={container}
        onRendered={this.handleRendered}
      >
        <div
          data-mui-test="Modal"
          className={classNames(classes.root, className, {
            [classes.hidden]: exited,
          })}
          {...other}
        >
          {hideBackdrop ? null : (
            <BackdropComponent open={open} onClick={this.handleBackdropClick} {...BackdropProps} />
          )}
          <RootRef
            rootRef={node => {
              this.dialogElement = node;
            }}
          >
            {React.cloneElement(children, childProps)}
          </RootRef>
        </div>
      </Portal>
    );
  }
}

Modal.propTypes = {
  /**
   * A backdrop component. Useful for custom backdrop rendering.
   */
  BackdropComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
  /**
   * Properties applied to the `Backdrop` element.
   */
  BackdropProps: PropTypes.object,
  /**
   * A single child content element.
   */
  children: PropTypes.element,
  /**
   * Useful to extend the style applied to components.
   */
  classes: PropTypes.object.isRequired,
  /**
   * @ignore
   */
  className: PropTypes.string,
  /**
   * A node, component instance, or function that returns either.
   * The `container` will have the portal children appended to it.
   */
  container: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
  /**
   * If `true`, the modal will not automatically shift focus to itself when it opens, and
   * replace it to the last focused element when it closes.
   * This also works correctly with any modal children that have the `disableAutoFocus` prop.
   *
   * Generally this should never be set to `true` as it makes the modal less
   * accessible to assistive technologies, like screen readers.
   */
  disableAutoFocus: PropTypes.bool,
  /**
   * If `true`, clicking the backdrop will not fire any callback.
   */
  disableBackdropClick: PropTypes.bool,
  /**
   * If `true`, the modal will not prevent focus from leaving the modal while open.
   *
   * Generally this should never be set to `true` as it makes the modal less
   * accessible to assistive technologies, like screen readers.
   */
  disableEnforceFocus: PropTypes.bool,
  /**
   * If `true`, hitting escape will not fire any callback.
   */
  disableEscapeKeyDown: PropTypes.bool,
  /**
   * If `true`, the modal will not restore focus to previously focused element once
   * modal is hidden.
   */
  disableRestoreFocus: PropTypes.bool,
  /**
   * If `true`, the backdrop is not rendered.
   */
  hideBackdrop: PropTypes.bool,
  /**
   * Always keep the children in the DOM.
   * This property can be useful in SEO situation or
   * when you want to maximize the responsiveness of the Modal.
   */
  keepMounted: PropTypes.bool,
  /**
   * A modal manager used to track and manage the state of open
   * Modals. Useful when customizing how modals interact within a container.
   */
  manager: PropTypes.object,
  /**
   * Callback fired when the backdrop is clicked.
   */
  onBackdropClick: PropTypes.func,
  /**
   * Callback fired when the component requests to be closed.
   * The `reason` parameter can optionally be used to control the response to `onClose`.
   *
   * @param {object} event The event source of the callback
   * @param {string} reason Can be:`"escapeKeyDown"`, `"backdropClick"`
   */
  onClose: PropTypes.func,
  /**
   * Callback fired when the escape key is pressed,
   * `disableEscapeKeyDown` is false and the modal is in focus.
   */
  onEscapeKeyDown: PropTypes.func,
  /**
   * Callback fired once the children has been mounted into the `container`.
   * It signals that the `open={true}` property took effect.
   */
  onRendered: PropTypes.func,
  /**
   * If `true`, the modal is open.
   */
  open: PropTypes.bool.isRequired,
};

Modal.defaultProps = {
  disableAutoFocus: false,
  disableBackdropClick: false,
  disableEnforceFocus: false,
  disableEscapeKeyDown: false,
  disableRestoreFocus: false,
  hideBackdrop: false,
  keepMounted: false,
  // Modals don't open on the server so this won't conflict with concurrent requests.
  manager: new ModalManager(),
  BackdropComponent: Backdrop,
};

export default withStyles(styles, { flip: false, name: 'MuiModal' })(polyfill(Modal));
<|endoftext|>