<commit_before>(function (window, $, ch) {
    'use strict';

    if (ch === undefined) {
        throw new window.Error('Expected ch namespace defined.');
    }

    /**
     * Tooltip improves the native tooltips. Tooltip uses the 'alt' and 'title' attributes to grab its content.
     * @name Tooltip
     * @class Tooltip
     * @augments ch.Floats
     * @memberOf ch
     * @param {Object} [conf] Object with configuration properties.
     * @param {Boolean} [conf.fx] Enable or disable UI effects. By default, the effects are enable.
     * @param {String} [conf.points] Sets the points where component will be positioned, specified by configuration or centered by default: "cm cm".
     * @param {String} [conf.offset] Sets the offset in pixels that component will be displaced from original position determined by points. It's specified by configuration or zero by default: "0 0".
     * @returns itself
     * @factorized
     * @see ch.Modal
     * @see ch.Layer
     * @see ch.Zoom
     * @see ch.Flaots
     * @exampleDescription Create a tooltip.
     * @example
     * var widget = $(".some-element").tooltip();
     * @exampleDescription Create a new tooltip with configuration.
     * @example
     * var widget = $("a.example").tooltip({
     *     "fx": false,
     *     "offset": "10 -10",
     *     "points": "lt rt"
     * });
     * @exampleDescription
     * Now widget is a reference to the tooltip instance controller.
     * You can set a new content by using widget like this:
     * @example
     * widget.width(300);
     */
    function Modal($el, options) {

        this.init($el, options);

        /**
         * Reference to a internal component instance, saves all the information and configuration properties.
         * @private
         * @type {Object}
         */
        var that = this;

        /**
         * Triggers when the component is ready to use (Since 0.8.0).
         * @name ch.Popover#ready
         * @event
         * @public
         * @since 0.8.0
         * @exampleDescription Following the first example, using widget as popover's instance controller:
         * @example
         * widget.on("ready",function () {
         * this.show();
         * });
         */
        window.setTimeout(function () { that.emit('ready'); }, 50);
    }

    /**
     * Private members
     */
    var parent = ch.util.inherits(Modal, ch.Popover),

        $body = $('body'),

        $dimmer = $('
');

    /**
     * Public members
     */
    Modal.prototype.name = 'modal';

    Modal.prototype.constructor = Modal;

    Modal.prototype._defaults = $.extend(ch.util.clone(parent._defaults), {
        '_className': 'ch-modal ch-box-lite',
        'ariaRole': 'dialog',
        'width': '50%',
        'hiddenby': 'all',
        'reference': ch.viewport,
        'waiting': '

',
        'positioned': 'fixed'
    });

    Modal.prototype.init = function ($el, options) {
        parent.init.call(this, $el, options);
    };

    /**
     * Inner show method. Attach the component layout to the DOM tree.
     * @protected
     * @name ch.Popover#innerShow
     * @function
     * @returns itself
     */
    Modal.prototype.show = function (content) {

        if (!this._enabled) {
            return this;
        }

        var that = this,
            hiddenby = this._options.hiddenby;

        // Add to the dimmer the ability to close the widget only if the closable config. allows
        if (hiddenby === 'all' || hiddenby === 'pointers-only') {
            // Allow only one click to analize the config every time
            $dimmer.one(ch.onpointertap, function (event) {
                // Close dimmer and execute the original hide()
                that.hide();
            });
        }

        // Append dimmer element
        $dimmer.css('z-index', ch.util.zIndex).appendTo($body);

        if (ch.support.fx) {
            $dimmer.fadeIn();
        } else {
            $dimmer.removeClass('ch-hide');
        }

        // Execute the original show()
        parent.show.call(this, content);

        // Return the instance
        return this;
    };

    /**
     * Inner hide method. Hides the component and detach it from DOM tree.
     * @protected
     * @name ch.Popover#innerHide
     * @function
     * @returns itself
     */
    Modal.prototype.hide = function () {
        // Delete the dimmer element
        if (ch.support.fx) {
            $dimmer.fadeOut('normal', function () {
                $dimmer.remove(null, true);
            });
        } else {
            $dimmer.addClass('ch-hide').remove(null, true);
        }
        // Execute the original hide()
        parent.hide.call(this);
        // Return the instance
        return this;
    };

    /**
     * Factory
     */
    ch.factory(Modal, parent._normalizeOptions);

}(this, this.ch.$, this.ch));
<commit_msg>GH-989 Modal: add ARIA to the dimmer (tabindex -1)<commit_after>(function (window, $, ch) {
    'use strict';

    if (ch === undefined) {
        throw new window.Error('Expected ch namespace defined.');
    }

    /**
     * Tooltip improves the native tooltips. Tooltip uses the 'alt' and 'title' attributes to grab its content.
     * @name Tooltip
     * @class Tooltip
     * @augments ch.Floats
     * @memberOf ch
     * @param {Object} [conf] Object with configuration properties.
     * @param {Boolean} [conf.fx] Enable or disable UI effects. By default, the effects are enable.
     * @param {String} [conf.points] Sets the points where component will be positioned, specified by configuration or centered by default: "cm cm".
     * @param {String} [conf.offset] Sets the offset in pixels that component will be displaced from original position determined by points. It's specified by configuration or zero by default: "0 0".
     * @returns itself
     * @factorized
     * @see ch.Modal
     * @see ch.Layer
     * @see ch.Zoom
     * @see ch.Flaots
     * @exampleDescription Create a tooltip.
     * @example
     * var widget = $(".some-element").tooltip();
     * @exampleDescription Create a new tooltip with configuration.
     * @example
     * var widget = $("a.example").tooltip({
     *     "fx": false,
     *     "offset": "10 -10",
     *     "points": "lt rt"
     * });
     * @exampleDescription
     * Now widget is a reference to the tooltip instance controller.
     * You can set a new content by using widget like this:
     * @example
     * widget.width(300);
     */
    function Modal($el, options) {

        this.init($el, options);

        /**
         * Reference to a internal component instance, saves all the information and configuration properties.
         * @private
         * @type {Object}
         */
        var that = this;

        /**
         * Triggers when the component is ready to use (Since 0.8.0).
         * @name ch.Popover#ready
         * @event
         * @public
         * @since 0.8.0
         * @exampleDescription Following the first example, using widget as popover's instance controller:
         * @example
         * widget.on("ready",function () {
         * this.show();
         * });
         */
        window.setTimeout(function () { that.emit('ready'); }, 50);
    }

    /**
     * Private members
     */
    var parent = ch.util.inherits(Modal, ch.Popover),

        $body = $('body'),

        $dimmer = $('
');

    /**
     * Public members
     */
    Modal.prototype.name = 'modal';

    Modal.prototype.constructor = Modal;

    Modal.prototype._defaults = $.extend(ch.util.clone(parent._defaults), {
        '_className': 'ch-modal ch-box-lite',
        'ariaRole': 'dialog',
        'width': '50%',
        'hiddenby': 'all',
        'reference': ch.viewport,
        'waiting': '

',
        'positioned': 'fixed'
    });

    Modal.prototype.init = function ($el, options) {
        parent.init.call(this, $el, options);
    };

    /**
     * Inner show method. Attach the component layout to the DOM tree.
     * @protected
     * @name ch.Popover#innerShow
     * @function
     * @returns itself
     */
    Modal.prototype.show = function (content) {

        if (!this._enabled) {
            return this;
        }

        var that = this,
            hiddenby = this._options.hiddenby;

        // Add to the dimmer the ability to close the widget only if the closable config. allows
        if (hiddenby === 'all' || hiddenby === 'pointers-only') {
            // Allow only one click to analize the config every time
            $dimmer.one(ch.onpointertap, function (event) {
                // Close dimmer and execute the original hide()
                that.hide();
            });
        }

        // Append dimmer element
        $dimmer.css('z-index', ch.util.zIndex).appendTo($body);

        if (ch.support.fx) {
            $dimmer.fadeIn();
        } else {
            $dimmer.removeClass('ch-hide');
        }

        // Execute the original show()
        parent.show.call(this, content);

        // Return the instance
        return this;
    };

    /**
     * Inner hide method. Hides the component and detach it from DOM tree.
     * @protected
     * @name ch.Popover#innerHide
     * @function
     * @returns itself
     */
    Modal.prototype.hide = function () {
        // Delete the dimmer element
        if (ch.support.fx) {
            $dimmer.fadeOut('normal', function () {
                $dimmer.remove(null, true);
            });
        } else {
            $dimmer.addClass('ch-hide').remove(null, true);
        }
        // Execute the original hide()
        parent.hide.call(this);
        // Return the instance
        return this;
    };

    /**
     * Factory
     */
    ch.factory(Modal, parent._normalizeOptions);

}(this, this.ch.$, this.ch));
<|endoftext|>


<commit_before>const request = require('request');
const gm = require('gm').subClass({ imageMagick: true });
const fs = require('fs');
const config = require('../profile.json');
const api = require('../wechat/wechat_api.js');
const Customer = require(config.CustomerModel);
const Activity = require(config.ActivityModel);

exports.wechat_event = (req, res, next) => {

    console.log("消息判断和事件响应");

    var message = req.weixin;
    console.log(message);

    switch (message.MsgType) {
        case "text": {
            switch (message.Content) {
                case "diaosi": {
                    res.reply('hehe');
                }; break;
                case "getUserList": {
                    (async () => {
                        let openids = await getFollower();
                        let alldatas = await getBatchGetUsers(openids);
                        await customer_create(alldatas);
                    })().then(() => {
                        res.reply('保存成功');
                    }).catch((err) => {
                        console.log(err);
                        res.reply('保存失败');
                    })
                }; break;
                case "qr": {

                    var qr_path = config.path_wechat + message.FromUserName + message.CreateTime + '.png';
                    var a_path = config.path_wechat + 'a.jpg';
                    var b_path = config.path_wechat + 'b.jpg';
                    var c_path = config.path_wechat + 'c.png';
                    var qr_path_out_resize = config.path_wechat + message.FromUserName + message.CreateTime + '_out1.png';
                    var qr_path_out = config.path_wechat + message.FromUserName + message.CreateTime + '_out2.png';
                    var media_id = "";
                    (async () => {
                        let tmpQRCodeURL = await getTmpQRCodeURL(message.FromUserName);
                        await downTmpQRCode(qr_path, tmpQRCodeURL);
                        await gmResize(qr_path, qr_path_out_resize);
                        await gmComposite(a_path, qr_path_out_resize, qr_path_out);
                        media_id = await douploadMedia(qr_path_out);
                    })().then(() => {
                        res.reply({
                            type: "image",
                            content: {
                                mediaId: media_id
                            }
                        });
                    }).catch((err) => {
                        console.log(err);
                        res.reply('获取二维码失败');
                    })
                }; break;
                case "hehe": {
                    res.reply({
                        type: "music",
                        content: {
                            title: "来段音乐吧",
                            description: "一无所有",
                            musicUrl: "http://mp3.com/xx.mp3",
                            hqMusicUrl: "http://mp3.com/xx.mp3",
                            thumbMediaId: "thisThumbMediaId"
                        }
                    });
                } break;
                default: {
                    res.reply([
                        {
                            title: 'meet_test_shell',
                            description: 'meet_test_shell',
                            picurl: 'http://nodeapi.cloudfoundry.com/qrcode.jpg',
                            url: 'http://www.yangtz.com'
                        }
                    ]);
                }
            }
        }; break;
        case "event": {
            switch (message.Event) {
                case "subscribe": {
                    console.log(message);

                    (async () => {
                        //此处要取得两个数据
                        //openid 、头像信息、nikename
                        if (message.EventKey === "qrscene_0") {
                            let wherestr = { 'activity_ticket': message.Ticket };
                            let activityInfo = await getActivityInfo(wherestr);
                            console.log(activityInfo);
                            let recommenderInfo = await getOneUserInfo(activityInfo[0].open_id);
                            let commenderInfo = await getOneUserInfo(message.FromUserName);
                            let newCustomerUser = [];

                            commenderInfo.recommender_openid = recommenderInfo.openid;
                            commenderInfo.recommender_nickname = recommenderInfo.nickname;
                            commenderInfo.recommender_headimgurl = recommenderInfo.headimgurl;
                            newCustomerUser[0] = commenderInfo;
                            await customer_create(newCustomerUser);
                        } else {
                            let commenderInfo = await getOneUserInfo(message.FromUserName);
                            let newCustomerUser = [];
                            newCustomerUser[0] = commenderInfo;
                            await customer_create(newCustomerUser);
                        }

                        // for (key in recommenderInfo) {
                        //     console.log("recommenderInfo:" + key + ":" + recommenderInfo[key]); // { errcode: 0, errmsg: 'ok' }
                        // }
                    })().then(() => {
                        res.reply('成功关注');
                    }).catch((err) => {
                        console.log(err);
                        res.reply('欢迎欢迎');
                    })

                }; break;
                case "unsubscribe": {
                    res.reply('unsubscribe');
                }; break;
                case "SCAN": {
                    // res.reply('感谢您关注米特学院，么么哒，目前该功能还不完善，更多功能需要和彭老师一起定制哟' + message.EventKey);
                    try {
                        res.redirect(301, 'http://www.yangtz.com/users/');
                    } catch (err) {
                        console.log(err);
                        res.reply('失败');
                    }

                }; break;

                case "VIEW": {
                    message.EventKey=message.EventKey+"?openid="+message.FromUserName;
                    console.log(message.EventKey);
                    res.reply('接收跳转事件');

                }; break;

            }
        }; break;
    }
}

var getFollower = () => {
    return new Promise((resolve, reject) => {
        api.getFollowers((err, data, response) => {
            if (err) {
                console.log("获取关注用户数据失败:" + err);
                reject(err);
            }
            else {
                console.log("获取关注用户数据成功:");
                resolve(data.data.openid);
            }
        });
    });
};

var getOneUserInfo = (open_id) => {
    return new Promise((resolve, reject) => {
        api.getUser(open_id, (err, data, response) => {
            if (err) {
                console.log("获取关注用户数据失败:" + err);
                reject(err);
            }
            else {
                console.log("获取关注用户数据成功:");
                resolve(data);
            }
        });
    });
};

var getActivityInfo = (wherestr) => {
    return new Promise((resolve, reject) => {
        Activity.Model.find(wherestr, (err, response) => {
            if (err) {
                console.log("获取关注用户数据失败:" + err);
                reject(err);
            }
            else {
                console.log("获取关注用户数据成功:");
                resolve(response);
            }
        });
    });
};

var getBatchGetUsers = (openids) => {
    return new Promise((resolve, reject) => {
        api.batchGetUsers(openids, (err, data, responses) => {
            if (err) {
                console.log("批量获取数据失败:" + err);
                reject(err);
            }
            else {
                console.log("批量获取数据成功:");
                resolve(data["user_info_list"]);
            }
        });
    });
};

var customer_create = (alldatas) => {
    return new Promise((resolve, reject) => {
        Customer.Model.create(alldatas, (err, response) => {
            if (err) {
                console.log("保存失败:" + err);
                reject(err);
            }
            else {
                console.log("保存成功:" + response);
                resolve(true);
            }
        });
    });
}

var getTmpQRCodeURL = (open_id) => {
    return new Promise((resolve, reject) => {
        //此处需要增加活动管理
        //0：代表关注公众号
        //1：代表某课程
        //以此类推
        var qr_event_code = 0;
        var exist_time = 600;
        api.createTmpQRCode(qr_event_code, exist_time, (err, data, response) => {
            if (err) {
                console.log("获取二维码信息失败:" + err);
                reject(err);
            }
            else {
                //先保存
                let activity = new Activity.Model({
                    activity_ticket: data.ticket,
                    qr_event_code: qr_event_code,
                    open_id: open_id,
                    qr_time: new Date(),
                    livetime: exist_time
                });

                activity.save((err, response) => {

                    if (err) {
                        console.log("保存失败" + err);
                        res.send('保存失败');
                    }
                    else {
                        console.log("Res:" + response);
                        let qucodemedia = api.showQRCodeURL(data.ticket);
                        console.log("showQRCodeURL:" + qucodemedia);
                        resolve(qucodemedia);
                    }
                });
            }
        });
    })
}

var downTmpQRCode = (qr_path, qucodemedia) => {
    return new Promise((resolve, reject) => {
        var fileWriteStream = fs.createWriteStream(qr_path);
        request(qucodemedia).pipe(fileWriteStream);
        fileWriteStream.on('close', (err) => {
            if (err) {
                console.log("图片下载失败:" + err);
                reject(err);
            }
            else {
                console.log('copy over');
                resolve("done");
            }
        });
    });
}

var gmResize = (qr_path, qr_path_out_resize) => {
    return new Promise((resolve, reject) => {
        gm(qr_path)
            .resize(126, 126)
            .noProfile()
            .write(qr_path_out_resize, (err) => {
                if (err) {
                    console.log("图片裁剪失败:" + err);
                    reject(err);
                }
                else {
                    console.log("图片裁剪成功");
                    resolve("done");
                }
            });
    })
}

var gmComposite = (a_path, qr_path_out_resize, qr_path_out) => {
    return new Promise((resolve, reject) => {
        gm(a_path)
            .composite(qr_path_out_resize)
            .geometry('+130+67')
            .write(qr_path_out, (err) => {
                if (err) {
                    console.log("图片合成失败:" + err);
                    reject(err);
                }
                else {
                    console.log("图片合成成功");
                    resolve("done");
                }
            });
    })
}

var douploadMedia = (qr_path_out) => {
    return new Promise((resolve, reject) => {
        api.uploadMedia(qr_path_out, "image", (err, result) => {
            if (err) {
                console.log("图片上传失败:" + err);
                reject(err);
            }
            else {
                console.log("图片上传成功:" + result.media_id);
                resolve(result.media_id);
            }
        });
    })
}<commit_msg>传递openid<commit_after>const request = require('request');
const gm = require('gm').subClass({ imageMagick: true });
const fs = require('fs');
const config = require('../profile.json');
const api = require('../wechat/wechat_api.js');
const Customer = require(config.CustomerModel);
const Activity = require(config.ActivityModel);

exports.wechat_event = (req, res, next) => {

    console.log("消息判断和事件响应");

    var message = req.weixin;
    console.log(message);

    switch (message.MsgType) {
        case "text": {
            switch (message.Content) {
                case "diaosi": {
                    res.reply('hehe');
                }; break;
                case "getUserList": {
                    (async () => {
                        let openids = await getFollower();
                        let alldatas = await getBatchGetUsers(openids);
                        await customer_create(alldatas);
                    })().then(() => {
                        res.reply('保存成功');
                    }).catch((err) => {
                        console.log(err);
                        res.reply('保存失败');
                    })
                }; break;
                case "qr": {

                    var qr_path = config.path_wechat + message.FromUserName + message.CreateTime + '.png';
                    var a_path = config.path_wechat + 'a.jpg';
                    var b_path = config.path_wechat + 'b.jpg';
                    var c_path = config.path_wechat + 'c.png';
                    var qr_path_out_resize = config.path_wechat + message.FromUserName + message.CreateTime + '_out1.png';
                    var qr_path_out = config.path_wechat + message.FromUserName + message.CreateTime + '_out2.png';
                    var media_id = "";
                    (async () => {
                        let tmpQRCodeURL = await getTmpQRCodeURL(message.FromUserName);
                        await downTmpQRCode(qr_path, tmpQRCodeURL);
                        await gmResize(qr_path, qr_path_out_resize);
                        await gmComposite(a_path, qr_path_out_resize, qr_path_out);
                        media_id = await douploadMedia(qr_path_out);
                    })().then(() => {
                        res.reply({
                            type: "image",
                            content: {
                                mediaId: media_id
                            }
                        });
                    }).catch((err) => {
                        console.log(err);
                        res.reply('获取二维码失败');
                    })
                }; break;
                case "hehe": {
                    res.reply({
                        type: "music",
                        content: {
                            title: "来段音乐吧",
                            description: "一无所有",
                            musicUrl: "http://mp3.com/xx.mp3",
                            hqMusicUrl: "http://mp3.com/xx.mp3",
                            thumbMediaId: "thisThumbMediaId"
                        }
                    });
                } break;
                default: {
                    res.reply([
                        {
                            title: 'meet_test_shell',
                            description: 'meet_test_shell',
                            picurl: 'http://nodeapi.cloudfoundry.com/qrcode.jpg',
                            url: 'http://www.yangtz.com'
                        }
                    ]);
                }
            }
        }; break;
        case "event": {
            switch (message.Event) {
                case "subscribe": {
                    console.log(message);

                    (async () => {
                        //此处要取得两个数据
                        //openid 、头像信息、nikename
                        if (message.EventKey === "qrscene_0") {
                            let wherestr = { 'activity_ticket': message.Ticket };
                            let activityInfo = await getActivityInfo(wherestr);
                            console.log(activityInfo);
                            let recommenderInfo = await getOneUserInfo(activityInfo[0].open_id);
                            let commenderInfo = await getOneUserInfo(message.FromUserName);
                            let newCustomerUser = [];

                            commenderInfo.recommender_openid = recommenderInfo.openid;
                            commenderInfo.recommender_nickname = recommenderInfo.nickname;
                            commenderInfo.recommender_headimgurl = recommenderInfo.headimgurl;
                            newCustomerUser[0] = commenderInfo;
                            await customer_create(newCustomerUser);
                        } else {
                            let commenderInfo = await getOneUserInfo(message.FromUserName);
                            let newCustomerUser = [];
                            newCustomerUser[0] = commenderInfo;
                            await customer_create(newCustomerUser);
                        }

                        // for (key in recommenderInfo) {
                        //     console.log("recommenderInfo:" + key + ":" + recommenderInfo[key]); // { errcode: 0, errmsg: 'ok' }
                        // }
                    })().then(() => {
                        res.reply('成功关注');
                    }).catch((err) => {
                        console.log(err);
                        res.reply('欢迎欢迎');
                    })

                }; break;
                case "unsubscribe": {
                    res.reply('unsubscribe');
                }; break;
                case "SCAN": {
                    // res.reply('感谢您关注米特学院，么么哒，目前该功能还不完善，更多功能需要和彭老师一起定制哟' + message.EventKey);
                    try {
                        res.redirect(301, 'http://www.yangtz.com/users/');
                    } catch (err) {
                        console.log(err);
                        res.reply('失败');
                    }

                }; break;

                case "VIEW": {
                    req.session.openid=message.FromUserName;
                    // message.EventKey=message.EventKey+"?openid="+message.FromUserName;
                    // console.log(message.EventKey);
                    // res.reply('接收跳转事件');

                }; break;

            }
        }; break;
    }
}

var getFollower = () => {
    return new Promise((resolve, reject) => {
        api.getFollowers((err, data, response) => {
            if (err) {
                console.log("获取关注用户数据失败:" + err);
                reject(err);
            }
            else {
                console.log("获取关注用户数据成功:");
                resolve(data.data.openid);
            }
        });
    });
};

var getOneUserInfo = (open_id) => {
    return new Promise((resolve, reject) => {
        api.getUser(open_id, (err, data, response) => {
            if (err) {
                console.log("获取关注用户数据失败:" + err);
                reject(err);
            }
            else {
                console.log("获取关注用户数据成功:");
                resolve(data);
            }
        });
    });
};

var getActivityInfo = (wherestr) => {
    return new Promise((resolve, reject) => {
        Activity.Model.find(wherestr, (err, response) => {
            if (err) {
                console.log("获取关注用户数据失败:" + err);
                reject(err);
            }
            else {
                console.log("获取关注用户数据成功:");
                resolve(response);
            }
        });
    });
};

var getBatchGetUsers = (openids) => {
    return new Promise((resolve, reject) => {
        api.batchGetUsers(openids, (err, data, responses) => {
            if (err) {
                console.log("批量获取数据失败:" + err);
                reject(err);
            }
            else {
                console.log("批量获取数据成功:");
                resolve(data["user_info_list"]);
            }
        });
    });
};

var customer_create = (alldatas) => {
    return new Promise((resolve, reject) => {
        Customer.Model.create(alldatas, (err, response) => {
            if (err) {
                console.log("保存失败:" + err);
                reject(err);
            }
            else {
                console.log("保存成功:" + response);
                resolve(true);
            }
        });
    });
}

var getTmpQRCodeURL = (open_id) => {
    return new Promise((resolve, reject) => {
        //此处需要增加活动管理
        //0：代表关注公众号
        //1：代表某课程
        //以此类推
        var qr_event_code = 0;
        var exist_time = 600;
        api.createTmpQRCode(qr_event_code, exist_time, (err, data, response) => {
            if (err) {
                console.log("获取二维码信息失败:" + err);
                reject(err);
            }
            else {
                //先保存
                let activity = new Activity.Model({
                    activity_ticket: data.ticket,
                    qr_event_code: qr_event_code,
                    open_id: open_id,
                    qr_time: new Date(),
                    livetime: exist_time
                });

                activity.save((err, response) => {

                    if (err) {
                        console.log("保存失败" + err);
                        res.send('保存失败');
                    }
                    else {
                        console.log("Res:" + response);
                        let qucodemedia = api.showQRCodeURL(data.ticket);
                        console.log("showQRCodeURL:" + qucodemedia);
                        resolve(qucodemedia);
                    }
                });
            }
        });
    })
}

var downTmpQRCode = (qr_path, qucodemedia) => {
    return new Promise((resolve, reject) => {
        var fileWriteStream = fs.createWriteStream(qr_path);
        request(qucodemedia).pipe(fileWriteStream);
        fileWriteStream.on('close', (err) => {
            if (err) {
                console.log("图片下载失败:" + err);
                reject(err);
            }
            else {
                console.log('copy over');
                resolve("done");
            }
        });
    });
}

var gmResize = (qr_path, qr_path_out_resize) => {
    return new Promise((resolve, reject) => {
        gm(qr_path)
            .resize(126, 126)
            .noProfile()
            .write(qr_path_out_resize, (err) => {
                if (err) {
                    console.log("图片裁剪失败:" + err);
                    reject(err);
                }
                else {
                    console.log("图片裁剪成功");
                    resolve("done");
                }
            });
    })
}

var gmComposite = (a_path, qr_path_out_resize, qr_path_out) => {
    return new Promise((resolve, reject) => {
        gm(a_path)
            .composite(qr_path_out_resize)
            .geometry('+130+67')
            .write(qr_path_out, (err) => {
                if (err) {
                    console.log("图片合成失败:" + err);
                    reject(err);
                }
                else {
                    console.log("图片合成成功");
                    resolve("done");
                }
            });
    })
}

var douploadMedia = (qr_path_out) => {
    return new Promise((resolve, reject) => {
        api.uploadMedia(qr_path_out, "image", (err, result) => {
            if (err) {
                console.log("图片上传失败:" + err);
                reject(err);
            }
            else {
                console.log("图片上传成功:" + result.media_id);
                resolve(result.media_id);
            }
        });
    })
}<|endoftext|>


<commit_before>// Includes
var addIf = require('./addIf.js');
var options = require('../options.js');
var promise = require('../util/promise.js');

// Define
function wrap (type, index, func) {
  var cache = options.cache;
  if (cache[type].expire > 0) {
    return promise(function (resolve, reject) {
      addIf(cache, type, index, {
        done: resolve,
        add: function (done) {
          return promise(func).then(done).catch(reject);
        }
      });
    });
  } else {
    return promise(func);
  }
}

module.exports = function (type, index, func) {
  return wrap(type, index, func);
};
<commit_msg>Fix for permanent<commit_after>// Includes
var addIf = require('./addIf.js');
var options = require('../options.js');
var promise = require('../util/promise.js');

// Define
function wrap (type, index, func) {
  var cache = options.cache;
  var group = cache[type];
  if (group.expire > 0 || group.permanent) {
    return promise(function (resolve, reject) {
      addIf(cache, type, index, {
        done: resolve,
        add: function (done) {
          return promise(func).then(done).catch(reject);
        }
      });
    });
  } else {
    return promise(func);
  }
}

module.exports = function (type, index, func) {
  return wrap(type, index, func);
};
<|endoftext|>


<commit_before>/* find the shape of array */
module.exports = function calc_shape(arr) {
    const shape = [];
    shape.push(arr.length);
    let a = arr[0];
    while (Array.isArray(a)) {
        shape.push(a.length);
        a = a[0];
    }
    return shape;
}<commit_msg>Update calc_shape.js<commit_after>/* find the shape of array  */
module.exports = function calc_shape(arr) {
    const shape = [];
    shape.push(arr.length);
    let a = arr[0];
    while (Array.isArray(a)) {
        shape.push(a.length);
        a = a[0];
    }
    return shape;
}
<|endoftext|>


<commit_before>var sort_distances = function (distances, locations) {
	distances.sort(function(a, b) {return a.duration - b.duration;});
	console.log(distances);
}

var async = function(locations, callback, i, current_location, distances) {
	gd(locations, current_location, distances, i);
}

var func_final = function(distances, locations) {
	console.log(distances);
	console.log(locations);
}

var get_choice = function(locations, current_location) {
	var distances = [];
	for (var i = 0; i < locations.length; i++) {
		async(locations, get_distance, i, current_location, distances);
	}
}

var gd = function(locations, current_location, distances, i) {
	var distance = require('google-distance');

	var lat = locations[i].location_1.latitude;
	var longitude = locations[i].location_1.longitude;
	var my_origin = lat.concat(',').concat(longitude);

	var dest_lat = current_location[0].latitude;
	var dest_long = current_location[0].longitude;
	var my_destination = dest_lat.concat(',').concat(dest_long);

	distance.get(
	{
		origin: my_origin,
		destination: my_destination,
		units: 'imperial'
	},
	function(err, data) {
		if (err) return console.log(err);
		locations[i].duration =  data.durationValue;
		// my_obj = {"duration" : data.durationValue, "index" : i}
		distances.push(data.durationValue);

		if (distances.length == locations.length) {
			func_final(distances, locations);
		}
	});
}

// var get_distance = function ( i, locations, current_location, distances) {

// 	var distance = require('google-distance');

// 	var lat = locations[i].location_1.latitude;
// 	var longitude = locations[i].location_1.longitude;
// 	var my_origin = lat.concat(',').concat(longitude);

// 	var dest_lat = current_location[0].latitude;
// 	var dest_long = current_location[0].longitude;
// 	var my_destination = dest_lat.concat(',').concat(dest_long);

// 	distance.get(
// 	{
// 		origin: my_origin,
// 		destination: my_destination,
// 		units: 'imperial'
// 	},
// 	function(err, data) {
// 		if (err) return console.log(err);
// 		my_obj = {"duration" : data.durationValue, "index" : i}
// 		distances.push(my_obj);

// 		if (i+1 < locations.length) {
// 			get_distance(i+1, locations, current_location, distances);
// 		}
// 		else {
// 			console.log(distances);
// 			sort_distances(distances, locations);
// 		}
// 	});
// }

var get_top_choice = function(locations, current_location) {
	// assume current_location is json with "latitude" and "longitude" entries
	// locations is json where lat/long are under "location_1" key
	get_choice(locations, current_location);
}

var get_json = function(data) {
	return data;
}

exports.get_info_sheet = function(material, all_objects){
	var unirest = require('unirest');
	var url = 'http://data.countyofriverside.us/resource/4yyp-e7qt.json?$q='.concat(material);
	unirest.get(url)
				.end(function (data) {
					var material_info = [
						{"info_sheet" : data['body'],
						"location_information" : all_objects}];
					var current_location = [{
						"latitude": "33.948769",
						 "longitude": "-117.380940"}];
					 // console.log(material_info[0].location_information[0]);
					get_top_choice(material_info[0].location_information, current_location);
					// return material;
				});
}

exports.get_greeenwaste_recyclers = function(material, all_objects, info_sheet_function) {
	var unirest = require('unirest');
	var url = 'http://data.countyofriverside.us/resource/76h8-zrhh.json?$q='.concat(material);
	unirest.get(url)
			.end(function (data) {
				for (var i = 0; i < data['body'].length; i++) {
					var addr_json = JSON.parse(data['body'][i]['location_1']['human_address'])
					var obj_info = {"phone" : data['body'][i]['phone'],
									"facility" : data['body'][i]['facility'],
									"location_1" : {
										"latitude" : data['body'][i]['location_1']['latitude'],
										"longitude" : data['body'][i]['location_1']['longitude'],
										"human_address" : addr_json,
									},
								};
					all_objects.push(obj_info);
				}
				info_sheet_function(material, all_objects);
			});
}

exports.get_locations = function(input, info_sheet_function, greenwaste_function) {
  // assumes input is a material that is listed on the database material_accepted
  var unirest = require('unirest');
  var info_sheet;
  var url = 'http://data.countyofriverside.us/resource/fp33-e7hd.json?$q='.concat(input);
  unirest.get(url)
  	.end(function (data) {
  		var all_objects = [];
  		var data_json = JSON.parse(data['raw_body']);

  		for (var i = 0; i < data_json.length; i++ ) {
  			var result = [];
  			var addr_json = JSON.parse(data_json[i]['location_1']['human_address']);
  			var obj_info = {"phone" : data_json[i]['phone'],
  							"facility" : data_json[i]['facility'],
  							"location_1" : {
  								"latitude" : data_json[i]['location_1']['latitude'],
  								"longitude" : data_json[i]['location_1']['longitude'],
  								"human_address" : addr_json,
  							},
  						};

  			all_objects.push(obj_info);
		}
		greenwaste_function(input, all_objects, info_sheet_function);
	});

}

<commit_msg>not recursive<commit_after>var sort_distances = function (locations) {
	locations.sort(function(a, b) {return a.duration - b.duration;});
	// console.log(locations);
}

var async = function(locations, i, current_location, distances) {
	gd(locations, current_location, distances, i);
}

var func_final = function(distances, locations) {
	// console.log(distances);
	// console.log(locations);
	sort_distances(locations);
}

var get_choice = function(locations, current_location) {
	var distances = [];
	for (var i = 0; i < locations.length; i++) {
		async(locations, i, current_location, distances);
	}
}

var gd = function(locations, current_location, distances, i) {
	var distance = require('google-distance');

	var lat = locations[i].location_1.latitude;
	var longitude = locations[i].location_1.longitude;
	var my_origin = lat.concat(',').concat(longitude);

	var dest_lat = current_location[0].latitude;
	var dest_long = current_location[0].longitude;
	var my_destination = dest_lat.concat(',').concat(dest_long);

	distance.get(
	{
		origin: my_origin,
		destination: my_destination,
		units: 'imperial'
	},
	function(err, data) {
		if (err) return console.log(err);
		locations[i].duration =  data.durationValue;
		distances.push(data.durationValue);

		if (distances.length == locations.length) {
			func_final(distances, locations);
		}
	});
}

// var get_distance = function ( i, locations, current_location, distances) {

// 	var distance = require('google-distance');

// 	var lat = locations[i].location_1.latitude;
// 	var longitude = locations[i].location_1.longitude;
// 	var my_origin = lat.concat(',').concat(longitude);

// 	var dest_lat = current_location[0].latitude;
// 	var dest_long = current_location[0].longitude;
// 	var my_destination = dest_lat.concat(',').concat(dest_long);

// 	distance.get(
// 	{
// 		origin: my_origin,
// 		destination: my_destination,
// 		units: 'imperial'
// 	},
// 	function(err, data) {
// 		if (err) return console.log(err);
// 		my_obj = {"duration" : data.durationValue, "index" : i}
// 		distances.push(my_obj);

// 		if (i+1 < locations.length) {
// 			get_distance(i+1, locations, current_location, distances);
// 		}
// 		else {
// 			console.log(distances);
// 			sort_distances(distances, locations);
// 		}
// 	});
// }

var get_top_choice = function(locations, current_location) {
	// assume current_location is json with "latitude" and "longitude" entries
	// locations is json where lat/long are under "location_1" key
	get_choice(locations, current_location);
}

var get_json = function(data) {
	return data;
}

exports.get_info_sheet = function(material, all_objects){
	var unirest = require('unirest');
	var url = 'http://data.countyofriverside.us/resource/4yyp-e7qt.json?$q='.concat(material);
	unirest.get(url)
				.end(function (data) {
					var material_info = [
						{"info_sheet" : data['body'],
						"location_information" : all_objects}];
					var current_location = [{
						"latitude": "33.948769",
						 "longitude": "-117.380940"}];
					 // console.log(material_info[0].location_information[0]);
					 console.log(material_info[0].location_information);
					get_top_choice(material_info[0].location_information, current_location);
					// return material;
				});
}

exports.get_greeenwaste_recyclers = function(material, all_objects, info_sheet_function) {
	var unirest = require('unirest');
	var url = 'http://data.countyofriverside.us/resource/76h8-zrhh.json?$q='.concat(material);
	unirest.get(url)
			.end(function (data) {
				for (var i = 0; i < data['body'].length; i++) {
					var addr_json = JSON.parse(data['body'][i]['location_1']['human_address'])
					var obj_info = {"phone" : data['body'][i]['phone'],
									"facility" : data['body'][i]['facility'],
									"location_1" : {
										"latitude" : data['body'][i]['location_1']['latitude'],
										"longitude" : data['body'][i]['location_1']['longitude'],
										"human_address" : addr_json,
									},
								};
					all_objects.push(obj_info);
				}
				info_sheet_function(material, all_objects);
			});
}

exports.get_locations = function(input, info_sheet_function, greenwaste_function) {
  // assumes input is a material that is listed on the database material_accepted
  var unirest = require('unirest');
  var info_sheet;
  var url = 'http://data.countyofriverside.us/resource/fp33-e7hd.json?$q='.concat(input);
  unirest.get(url)
  	.end(function (data) {
  		var all_objects = [];
  		var data_json = JSON.parse(data['raw_body']);

  		for (var i = 0; i < data_json.length; i++ ) {
  			var result = [];
  			var addr_json = JSON.parse(data_json[i]['location_1']['human_address']);
  			var obj_info = {"phone" : data_json[i]['phone'],
  							"facility" : data_json[i]['facility'],
  							"location_1" : {
  								"latitude" : data_json[i]['location_1']['latitude'],
  								"longitude" : data_json[i]['location_1']['longitude'],
  								"human_address" : addr_json,
  							},
  						};

  			all_objects.push(obj_info);
		}
		greenwaste_function(input, all_objects, info_sheet_function);
	});

}

<|endoftext|>


<commit_before>const debug = require('debug')('auth:signaturevalidator');
const hawk = require('hawk');
const assert = require('assert');
const _ = require('lodash');
require('superagent-hawk')(require('superagent'));
// Someone should rename utils to scopes...
const utils = require('taskcluster-lib-scopes');
const hoek = require('hoek');
const https = require('https');
const crypto = require('crypto');

/**
 * Limit the client scopes and possibly use temporary keys.
 *
 * Takes a client object on the form: `{clientId, accessToken, scopes}`,
 * applies scope restrictions, certificate validation and returns a clone if
 * modified (otherwise it returns the original).
 */
const parseExt = function(ext) {
  // Attempt to parse ext
  try {
    ext = JSON.parse(new Buffer(ext, 'base64').toString('utf-8'));
  } catch (err) {
    throw new Error('Failed to parse ext');
  }

  return ext;
};

/**
 * Limit the client scopes and possibly use temporary keys.
 *
 * Takes a client object on the form: `{clientId, accessToken, scopes}`,
 * applies scope restrictions, certificate validation and returns a clone if
 * modified (otherwise it returns the original).
 */
const limitClientWithExt = function(credentialName, issuingClientId, accessToken, scopes,
  expires, ext, expandScopes) {
  let issuingScopes = scopes;
  let res = {scopes, expires, accessToken};

  // Handle certificates
  if (ext.certificate) {
    let cert = ext.certificate;
    // Validate the certificate
    if (!(cert instanceof Object)) {
      throw new Error('ext.certificate must be a JSON object');
    }
    if (cert.version !== 1) {
      throw new Error('ext.certificate.version must be 1');
    }
    if (typeof cert.seed !== 'string') {
      throw new Error('ext.certificate.seed must be a string');
    }
    if (cert.seed.length !== 44) {
      throw new Error('ext.certificate.seed must be 44 characters');
    }
    if (typeof cert.start !== 'number') {
      throw new Error('ext.certificate.start must be a number');
    }
    if (typeof cert.expiry !== 'number') {
      throw new Error('ext.certificate.expiry must be a number');
    }
    if (!(cert.scopes instanceof Array)) {
      throw new Error('ext.certificate.scopes must be an array');
    }
    if (!cert.scopes.every(utils.validScope)) {
      throw new Error('ext.certificate.scopes must be an array of valid scopes');
    }

    // Check start and expiry
    let now = new Date().getTime();
    if (cert.start > now + 5 * 60 * 1000) {
      throw new Error('ext.certificate.start > now');
    }
    if (cert.expiry < now - 5 * 60 * 1000) {
      throw new Error('ext.certificate.expiry < now');
    }
    // Check max time between start and expiry
    if (cert.expiry - cert.start > 31 * 24 * 60 * 60 * 1000) {
      throw new Error('ext.certificate cannot last longer than 31 days!');
    }

    // Check clientId validity
    if (issuingClientId !== credentialName) {
      let createScope = 'auth:create-client:' + credentialName;
      if (!utils.scopeMatch(issuingScopes, [[createScope]])) {
        throw new Error('ext.certificate issuer `' + issuingClientId +
                        '` doesn\'t have `' + createScope + '` for supplied clientId.');
      }
    } else if (cert.hasOwnProperty('clientId')) {
      throw new Error('ext.certificate.clientId must only be used with ext.certificate.issuer');
    }

    // Validate certificate scopes are subset of client
    if (!utils.scopeMatch(scopes, [cert.scopes])) {
      throw new Error('ext.certificate issuer `' + issuingClientId +
                      '` doesn\'t satisfiy all certificate scopes ' +
                      cert.scopes.join(', ') + '.  The temporary ' +
                      'credentials were not generated correctly.');
    }

    // Generate certificate signature
    let sigContent = [];
    sigContent.push('version:'    + '1');
    if (cert.issuer) {
      sigContent.push('clientId:' + credentialName);
      sigContent.push('issuer:'   + cert.issuer);
    }
    sigContent.push('seed:'       + cert.seed);
    sigContent.push('start:'      + cert.start);
    sigContent.push('expiry:'     + cert.expiry);
    sigContent.push('scopes:');
    sigContent = sigContent.concat(cert.scopes);
    let signature = crypto.createHmac('sha256', accessToken)
      .update(sigContent.join('\n'))
      .digest('base64');

    // Validate signature
    if (typeof cert.signature !== 'string' ||
        !crypto.timingSafeEqual(Buffer.from(cert.signature), Buffer.from(signature))) {
      if (cert.issuer) {
        throw new Error('ext.certificate.signature is not valid, or wrong clientId provided');
      } else {
        throw new Error('ext.certificate.signature is not valid');
      }
    }

    // Regenerate temporary key
    let temporaryKey = crypto.createHmac('sha256', accessToken)
      .update(cert.seed)
      .digest('base64')
      .replace(/\+/g, '-')  // Replace + with - (see RFC 4648, sec. 5)
      .replace(/\//g, '_')  // Replace / with _ (see RFC 4648, sec. 5)
      .replace(/=/g,  '');  // Drop '==' padding

    // Update expiration, scopes and accessToken
    res.accessToken = temporaryKey;

    let cert_expires = new Date(cert.expiry);
    if (res.expires > cert_expires) {
      res.expires = cert_expires;
    }

    res.scopes = scopes = expandScopes(cert.scopes);
  }

  // Handle scope restriction with authorizedScopes
  if (ext.authorizedScopes) {
    // Validate input format
    if (!(ext.authorizedScopes instanceof Array)) {
      throw new Error('ext.authorizedScopes must be an array');
    }
    if (!ext.authorizedScopes.every(utils.validScope)) {
      throw new Error('ext.authorizedScopes must be an array of valid scopes');
    }

    // Validate authorizedScopes scopes are satisfied by client (or temp) scopes
    if (!utils.scopeMatch(res.scopes, [ext.authorizedScopes])) {
      throw new Error('Supplied credentials do not satisfy authorizedScopes; '
        + `credentials have scopes [${res.scopes}]; `
        + `authorizedScopes are [${[ext.authorizedScopes]}]`);
    }

    // Further limit scopes
    res.scopes = scopes = expandScopes(ext.authorizedScopes);
  }

  return res;
};

/**
 * Make a function for the signature validation.
 *
 * options:
 * {
 *    clientLoader:   async (clientId) => {clientId, expires, accessToken, scopes},
 *    nonceManager:   nonceManager({size: ...}),
 *    expandScopes:   (scopes) => scopes,
 *    monitor:        // an instance of taskcluster-lib-monitor
 * }
 *
 * The function returned takes an object:
 *     {method, resource, host, port, authorization, sourceIp}
 * And returns promise for an object on one of the forms:
 *     {status: 'auth-failed', message},
 *     {status: 'auth-success', clientId, scheme, scopes}, or
 *     {status: 'auth-success', clientId, scheme, scopes, hash}
 * where `hash` is the payload hash.
 *
 * The `expandScopes` applies any rules that expands scopes, such as roles.
 * It is assumed that clients from `clientLoader` are returned with scopes
 * fully expanded.
 *
 * The method returned by this function works as `signatureValidator` for
 * `remoteAuthentication`.
 */
const createSignatureValidator = function(options) {
  assert(typeof options === 'object', 'options must be an object');
  assert(options.clientLoader instanceof Function,
    'options.clientLoader must be a function');
  if (!options.expandScopes) {
    // Default to the identity function
    options.expandScopes = function(scopes) { return scopes; };
  }
  assert(options.expandScopes instanceof Function,
    'options.expandScopes must be a function');
  assert(options.monitor, 'options.monitor must be provided');
  let loadCredentials = function(clientId, ext, callback) {
    // We may have two clientIds here: the credentialName (the one the caller
    // sent in the Hawk Authorization header) and the issuingClientId (the one
    // that signed the temporary credentials).
    let credentialName = clientId,
      issuingClientId = clientId;

    (async () => {
      // extract ext.certificate.issuer, if present
      if (ext) {
        ext = parseExt(ext);
        if (ext.certificate && ext.certificate.issuer) {
          issuingClientId = ext.certificate.issuer;
          if (typeof issuingClientId !== 'string') {
            throw new Error('ext.certificate.issuer must be a string');
          }
          if (issuingClientId == credentialName) {
            throw new Error('ext.certificate.issuer must differ from the supplied clientId');
          }
        }
      }

      let accessToken, scopes, expires;
      ({clientId, expires, accessToken, scopes} = await options.clientLoader(issuingClientId));

      // apply restrictions based on the ext field
      if (ext) {
        ({scopes, expires, accessToken} = limitClientWithExt(
          credentialName, issuingClientId, accessToken,
          scopes, expires, ext, options.expandScopes));
      }

      callback(null, {
        key:       accessToken,
        algorithm: 'sha256',
        clientId:  credentialName,
        expires:   expires,
        scopes:    scopes,
      });
    })().catch(callback);
  };
  return function(req) {
    return new Promise(function(accept) {
      let authenticated = function(err, credentials, artifacts) {
        let result = null;
        if (err) {
          let message = 'Unknown authorization error';
          if (err.output && err.output.payload && err.output.payload.error) {
            message = err.output.payload.error;
            if (err.output.payload.message) {
              message += ': ' + err.output.payload.message;
            }
          } else if (err.message) {
            message = err.message;
          }
          result = {
            status:   'auth-failed',
            message:  '' + message,
          };
        } else {
          result = {
            status:   'auth-success',
            scheme:   'hawk',
            expires:  credentials.expires,
            scopes:   credentials.scopes,
            clientId: credentials.clientId,
          };
          if (artifacts.hash) {
            result.hash = artifacts.hash;
          }
        }
        options.monitor.log({
          time: new Date(),
          event: 'signature-validation',
          version: 3,
          expires: credentials? credentials.expires : '',
          scopes: credentials? credentials.scopes : [],
          clientId: credentials? credentials.clientId : '',
          status: result.status || '',
          scheme: result.scheme || '',
          message: result.message || '',
          hash: result.hash || '',
          host: req.host,
          port: req.port,
          resource: req.resource,
          method: req.method.toUpperCase(),
          sourceIp: req.sourceIp,
        });
        return accept(result);
      };
      if (req.authorization) {
        hawk.server.authenticate({
          method:           req.method.toUpperCase(),
          url:              req.resource,
          host:             req.host,
          port:             req.port,
          authorization:    req.authorization,
        }, function(clientId, callback) {
          let ext = undefined;

          // Parse authorization header for ext
          let attrs = hawk.utils.parseAuthorizationHeader(
            req.authorization
          );
          // Extra ext
          if (!(attrs instanceof Error)) {
            ext = attrs.ext;
          }

          // Get credentials with ext
          loadCredentials(clientId, ext, callback);
        }, {
          // Not sure if JSON stringify is not deterministic by specification.
          // I suspect not, so we'll postpone this till we're sure we want to do
          // payload validation and how we want to do it.
          //payload:      JSON.stringify(req.body),

          // We found that clients often have time skew (particularly on OSX)
          // since all our services require https we hardcode the allowed skew
          // to a very high number (15 min) similar to AWS.
          timestampSkewSec: 15 * 60,

          // Provide nonce manager
          nonceFunc:    options.nonceManager,
        }, authenticated);
      } else {
        // If there is no authorization header we'll attempt a login with bewit
        hawk.uri.authenticate({
          method:           req.method.toUpperCase(),
          url:              req.resource,
          host:             req.host,
          port:             req.port,
        }, function(clientId, callback) {
          let ext = undefined;

          // Get bewit string (stolen from hawk)
          let parts = req.resource.match(
            /^(\/.*)([\?&])bewit\=([^&$]*)(?:&(.+))?$/
          );

          let bewitString;
          try {
            bewitString = hoek.base64urlDecode(parts[3]);
          } catch (err) {
            bewitString = err;
          }

          if (!(bewitString instanceof Error)) {
            // Split string as hawk does it
            let parts = bewitString.split('\\');
            if (parts.length === 4 && parts[3]) {
              ext = parts[3];
            }
          }

          // Get credentials with ext
          loadCredentials(clientId, ext, callback);
        }, {}, authenticated);
      }
    });
  };
};

exports.createSignatureValidator = createSignatureValidator;
<commit_msg>add || 0.0.0.0 to sourceIp logging line<commit_after>const debug = require('debug')('auth:signaturevalidator');
const hawk = require('hawk');
const assert = require('assert');
const _ = require('lodash');
require('superagent-hawk')(require('superagent'));
// Someone should rename utils to scopes...
const utils = require('taskcluster-lib-scopes');
const hoek = require('hoek');
const https = require('https');
const crypto = require('crypto');

/**
 * Limit the client scopes and possibly use temporary keys.
 *
 * Takes a client object on the form: `{clientId, accessToken, scopes}`,
 * applies scope restrictions, certificate validation and returns a clone if
 * modified (otherwise it returns the original).
 */
const parseExt = function(ext) {
  // Attempt to parse ext
  try {
    ext = JSON.parse(new Buffer(ext, 'base64').toString('utf-8'));
  } catch (err) {
    throw new Error('Failed to parse ext');
  }

  return ext;
};

/**
 * Limit the client scopes and possibly use temporary keys.
 *
 * Takes a client object on the form: `{clientId, accessToken, scopes}`,
 * applies scope restrictions, certificate validation and returns a clone if
 * modified (otherwise it returns the original).
 */
const limitClientWithExt = function(credentialName, issuingClientId, accessToken, scopes,
  expires, ext, expandScopes) {
  let issuingScopes = scopes;
  let res = {scopes, expires, accessToken};

  // Handle certificates
  if (ext.certificate) {
    let cert = ext.certificate;
    // Validate the certificate
    if (!(cert instanceof Object)) {
      throw new Error('ext.certificate must be a JSON object');
    }
    if (cert.version !== 1) {
      throw new Error('ext.certificate.version must be 1');
    }
    if (typeof cert.seed !== 'string') {
      throw new Error('ext.certificate.seed must be a string');
    }
    if (cert.seed.length !== 44) {
      throw new Error('ext.certificate.seed must be 44 characters');
    }
    if (typeof cert.start !== 'number') {
      throw new Error('ext.certificate.start must be a number');
    }
    if (typeof cert.expiry !== 'number') {
      throw new Error('ext.certificate.expiry must be a number');
    }
    if (!(cert.scopes instanceof Array)) {
      throw new Error('ext.certificate.scopes must be an array');
    }
    if (!cert.scopes.every(utils.validScope)) {
      throw new Error('ext.certificate.scopes must be an array of valid scopes');
    }

    // Check start and expiry
    let now = new Date().getTime();
    if (cert.start > now + 5 * 60 * 1000) {
      throw new Error('ext.certificate.start > now');
    }
    if (cert.expiry < now - 5 * 60 * 1000) {
      throw new Error('ext.certificate.expiry < now');
    }
    // Check max time between start and expiry
    if (cert.expiry - cert.start > 31 * 24 * 60 * 60 * 1000) {
      throw new Error('ext.certificate cannot last longer than 31 days!');
    }

    // Check clientId validity
    if (issuingClientId !== credentialName) {
      let createScope = 'auth:create-client:' + credentialName;
      if (!utils.scopeMatch(issuingScopes, [[createScope]])) {
        throw new Error('ext.certificate issuer `' + issuingClientId +
                        '` doesn\'t have `' + createScope + '` for supplied clientId.');
      }
    } else if (cert.hasOwnProperty('clientId')) {
      throw new Error('ext.certificate.clientId must only be used with ext.certificate.issuer');
    }

    // Validate certificate scopes are subset of client
    if (!utils.scopeMatch(scopes, [cert.scopes])) {
      throw new Error('ext.certificate issuer `' + issuingClientId +
                      '` doesn\'t satisfiy all certificate scopes ' +
                      cert.scopes.join(', ') + '.  The temporary ' +
                      'credentials were not generated correctly.');
    }

    // Generate certificate signature
    let sigContent = [];
    sigContent.push('version:'    + '1');
    if (cert.issuer) {
      sigContent.push('clientId:' + credentialName);
      sigContent.push('issuer:'   + cert.issuer);
    }
    sigContent.push('seed:'       + cert.seed);
    sigContent.push('start:'      + cert.start);
    sigContent.push('expiry:'     + cert.expiry);
    sigContent.push('scopes:');
    sigContent = sigContent.concat(cert.scopes);
    let signature = crypto.createHmac('sha256', accessToken)
      .update(sigContent.join('\n'))
      .digest('base64');

    // Validate signature
    if (typeof cert.signature !== 'string' ||
        !crypto.timingSafeEqual(Buffer.from(cert.signature), Buffer.from(signature))) {
      if (cert.issuer) {
        throw new Error('ext.certificate.signature is not valid, or wrong clientId provided');
      } else {
        throw new Error('ext.certificate.signature is not valid');
      }
    }

    // Regenerate temporary key
    let temporaryKey = crypto.createHmac('sha256', accessToken)
      .update(cert.seed)
      .digest('base64')
      .replace(/\+/g, '-')  // Replace + with - (see RFC 4648, sec. 5)
      .replace(/\//g, '_')  // Replace / with _ (see RFC 4648, sec. 5)
      .replace(/=/g,  '');  // Drop '==' padding

    // Update expiration, scopes and accessToken
    res.accessToken = temporaryKey;

    let cert_expires = new Date(cert.expiry);
    if (res.expires > cert_expires) {
      res.expires = cert_expires;
    }

    res.scopes = scopes = expandScopes(cert.scopes);
  }

  // Handle scope restriction with authorizedScopes
  if (ext.authorizedScopes) {
    // Validate input format
    if (!(ext.authorizedScopes instanceof Array)) {
      throw new Error('ext.authorizedScopes must be an array');
    }
    if (!ext.authorizedScopes.every(utils.validScope)) {
      throw new Error('ext.authorizedScopes must be an array of valid scopes');
    }

    // Validate authorizedScopes scopes are satisfied by client (or temp) scopes
    if (!utils.scopeMatch(res.scopes, [ext.authorizedScopes])) {
      throw new Error('Supplied credentials do not satisfy authorizedScopes; '
        + `credentials have scopes [${res.scopes}]; `
        + `authorizedScopes are [${[ext.authorizedScopes]}]`);
    }

    // Further limit scopes
    res.scopes = scopes = expandScopes(ext.authorizedScopes);
  }

  return res;
};

/**
 * Make a function for the signature validation.
 *
 * options:
 * {
 *    clientLoader:   async (clientId) => {clientId, expires, accessToken, scopes},
 *    nonceManager:   nonceManager({size: ...}),
 *    expandScopes:   (scopes) => scopes,
 *    monitor:        // an instance of taskcluster-lib-monitor
 * }
 *
 * The function returned takes an object:
 *     {method, resource, host, port, authorization, sourceIp}
 * And returns promise for an object on one of the forms:
 *     {status: 'auth-failed', message},
 *     {status: 'auth-success', clientId, scheme, scopes}, or
 *     {status: 'auth-success', clientId, scheme, scopes, hash}
 * where `hash` is the payload hash.
 *
 * The `expandScopes` applies any rules that expands scopes, such as roles.
 * It is assumed that clients from `clientLoader` are returned with scopes
 * fully expanded.
 *
 * The method returned by this function works as `signatureValidator` for
 * `remoteAuthentication`.
 */
const createSignatureValidator = function(options) {
  assert(typeof options === 'object', 'options must be an object');
  assert(options.clientLoader instanceof Function,
    'options.clientLoader must be a function');
  if (!options.expandScopes) {
    // Default to the identity function
    options.expandScopes = function(scopes) { return scopes; };
  }
  assert(options.expandScopes instanceof Function,
    'options.expandScopes must be a function');
  assert(options.monitor, 'options.monitor must be provided');
  let loadCredentials = function(clientId, ext, callback) {
    // We may have two clientIds here: the credentialName (the one the caller
    // sent in the Hawk Authorization header) and the issuingClientId (the one
    // that signed the temporary credentials).
    let credentialName = clientId,
      issuingClientId = clientId;

    (async () => {
      // extract ext.certificate.issuer, if present
      if (ext) {
        ext = parseExt(ext);
        if (ext.certificate && ext.certificate.issuer) {
          issuingClientId = ext.certificate.issuer;
          if (typeof issuingClientId !== 'string') {
            throw new Error('ext.certificate.issuer must be a string');
          }
          if (issuingClientId == credentialName) {
            throw new Error('ext.certificate.issuer must differ from the supplied clientId');
          }
        }
      }

      let accessToken, scopes, expires;
      ({clientId, expires, accessToken, scopes} = await options.clientLoader(issuingClientId));

      // apply restrictions based on the ext field
      if (ext) {
        ({scopes, expires, accessToken} = limitClientWithExt(
          credentialName, issuingClientId, accessToken,
          scopes, expires, ext, options.expandScopes));
      }

      callback(null, {
        key:       accessToken,
        algorithm: 'sha256',
        clientId:  credentialName,
        expires:   expires,
        scopes:    scopes,
      });
    })().catch(callback);
  };
  return function(req) {
    return new Promise(function(accept) {
      let authenticated = function(err, credentials, artifacts) {
        let result = null;
        if (err) {
          let message = 'Unknown authorization error';
          if (err.output && err.output.payload && err.output.payload.error) {
            message = err.output.payload.error;
            if (err.output.payload.message) {
              message += ': ' + err.output.payload.message;
            }
          } else if (err.message) {
            message = err.message;
          }
          result = {
            status:   'auth-failed',
            message:  '' + message,
          };
        } else {
          result = {
            status:   'auth-success',
            scheme:   'hawk',
            expires:  credentials.expires,
            scopes:   credentials.scopes,
            clientId: credentials.clientId,
          };
          if (artifacts.hash) {
            result.hash = artifacts.hash;
          }
        }
        options.monitor.log({
          time: new Date(),
          event: 'signature-validation',
          version: 3,
          expires: credentials? credentials.expires : '',
          scopes: credentials? credentials.scopes : [],
          clientId: credentials? credentials.clientId : '',
          status: result.status || '',
          scheme: result.scheme || '',
          message: result.message || '',
          hash: result.hash || '',
          host: req.host,
          port: req.port,
          resource: req.resource,
          method: req.method.toUpperCase(),
          sourceIp: req.sourceIp || '0.0.0.0',
        });
        return accept(result);
      };
      if (req.authorization) {
        hawk.server.authenticate({
          method:           req.method.toUpperCase(),
          url:              req.resource,
          host:             req.host,
          port:             req.port,
          authorization:    req.authorization,
        }, function(clientId, callback) {
          let ext = undefined;

          // Parse authorization header for ext
          let attrs = hawk.utils.parseAuthorizationHeader(
            req.authorization
          );
          // Extra ext
          if (!(attrs instanceof Error)) {
            ext = attrs.ext;
          }

          // Get credentials with ext
          loadCredentials(clientId, ext, callback);
        }, {
          // Not sure if JSON stringify is not deterministic by specification.
          // I suspect not, so we'll postpone this till we're sure we want to do
          // payload validation and how we want to do it.
          //payload:      JSON.stringify(req.body),

          // We found that clients often have time skew (particularly on OSX)
          // since all our services require https we hardcode the allowed skew
          // to a very high number (15 min) similar to AWS.
          timestampSkewSec: 15 * 60,

          // Provide nonce manager
          nonceFunc:    options.nonceManager,
        }, authenticated);
      } else {
        // If there is no authorization header we'll attempt a login with bewit
        hawk.uri.authenticate({
          method:           req.method.toUpperCase(),
          url:              req.resource,
          host:             req.host,
          port:             req.port,
        }, function(clientId, callback) {
          let ext = undefined;

          // Get bewit string (stolen from hawk)
          let parts = req.resource.match(
            /^(\/.*)([\?&])bewit\=([^&$]*)(?:&(.+))?$/
          );

          let bewitString;
          try {
            bewitString = hoek.base64urlDecode(parts[3]);
          } catch (err) {
            bewitString = err;
          }

          if (!(bewitString instanceof Error)) {
            // Split string as hawk does it
            let parts = bewitString.split('\\');
            if (parts.length === 4 && parts[3]) {
              ext = parts[3];
            }
          }

          // Get credentials with ext
          loadCredentials(clientId, ext, callback);
        }, {}, authenticated);
      }
    });
  };
};

exports.createSignatureValidator = createSignatureValidator;
<|endoftext|>


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

exports.MINIMUM_DOMAIN_NAME_LENGTH = 3;
exports.MAXIMUM_DOMAIN_NAME_LENGTH = 28;
exports.INVALID_DOMAIN_NAME_CHARACTER_PATTERN = /[^\-a-z0-9]/g;
exports.INVALID_TABLE_NAME_CHARACTER_PATTERN = /[^_a-z0-9]/g;

function assertValidDomainName(domain) {
  if (typeof domain != 'string')
    throw new Error('domain name must be a string');

  if (domain.length < exports.MINIMUM_DOMAIN_NAME_LENGTH)
    throw new Error('too short domain name (minimum length = ' +
                    exports.MINIMUM_DOMAIN_NAME_LENGTH + ')');

  if (domain.length > exports.MAXIMUM_DOMAIN_NAME_LENGTH)
    throw new Error('too long domain name (max length = ' +
                   exports.MAXIMUM_DOMAIN_NAME_LENGTH + ')');

  var invalidCharacter = domain.match(exports.INVALID_DOMAIN_NAME_CHARACTER_PATTERN) ||
                         domain.match(exports.INVALID_TABLE_NAME_CHARACTER_PATTERN);
  if (invalidCharacter) {
    var characters = Array.prototype.map.call(invalidCharacter, function(aCharacter) {
                       return '"' + aCharacter + '"';
                     });
    throw new Error(characters.join(', ') + ' cannot appear in a domain name');
  }
}

function Domain(source) {
  if (source instanceof Domain)
    return source;

  this.initialize(source);
}
Domain.prototype = {
  initialize: function(source) {
    this.name = this.getName(source);
    this.indexFields = {};
    // for validation
    this.tableName;
    this.termsTableName;
  },
  getName: function(source) {
    if (typeof source == 'string')
      return source;

    if (source.query && source.query.DomainName)
      return source.query.DomainName;

    if (source.headers && source.headers.host) {
      var host = source.headers.host;
      var domainNameFromHost = Domain.getNameFromHost(host);
      if (domainNameFromHost)
        return domainNameFromHost;
    }

    throw new Error('no domain name');
  },
  get tableName() {
    if (!this._tableName) {
      assertValidDomainName(this.name);
      this._tableName = this.name;
    }
    return this._tableName;
  },
  get termsTableName() {
    if (!this._termsTableName)
      this._termsTableName = this.tableName + '_BigramTerms';
    return this._termsTableName;
  },
  getAlterTableName: function(field) {
    return this.tableName + '_' + field;
  },
  getIndexField: function(field) {
    return this.indexFields[field] ||
           (this.indexFields[field] = new IndexField(field, this));
  }
};

exports.Domain = Domain;

Domain.getNameFromHost = function(host) {
  var domainMatcher = /^(?:doc|search)-([^\.]+)-([^\.\-]+)\./;
  var match = host.match(domainMatcher);
  if (match)
    return match[1];

  return '';
};

exports.MINIMUM_FIELD_NAME_LENGTH = 3;
exports.MAXIMUM_FIELD_NAME_LENGTH = 64;
exports.INVALID_FIELD_NAME_CHARACTER_PATTERN = /[^_a-z0-9]/g;
exports.INVALID_COLUMN_NAME_CHARACTER_PATTERN = /[^_a-z0-9]/g;
exports.RESERVED_FIELD_NAMES = [
  'body',
  'docid',
  'text_relevance'
];
exports.RESERVED_COLUMN_NAMES = [
  '_key'
];

function assertValidFieldName(field) {
  if (typeof field != 'string')
    throw new Error('field name must be a string');

  if (field.length < exports.MINIMUM_FIELD_NAME_LENGTH)
    throw new Error('too short field name (minimum length = ' +
                    exports.MINIMUM_FIELD_NAME_LENGTH + ')');

  if (field.length > exports.MAXIMUM_FIELD_NAME_LENGTH)
    throw new Error('too long field name (max length = ' +
                   exports.MAXIMUM_FIELD_NAME_LENGTH + ')');

  var invalidCharacter = field.match(exports.INVALID_FIELD_NAME_CHARACTER_PATTERN) ||
                         field.match(exports.INVALID_COLUMN_NAME_CHARACTER_PATTERN);
  if (invalidCharacter) {
    var characters = Array.prototype.map.call(invalidCharacter, function(aCharacter) {
                       return '"' + aCharacter + '"';
                     });
    throw new Error(characters.join(', ') + ' cannot appear in a field name');
  }

  var index = exports.RESERVED_FIELD_NAMES.indexOf(field);
  if (index > -1) index = exports.RESERVED_COLUMN_NAMES.indexOf(field);
  if (index > -1)
    throw new Error(field + ' is a reserved field name');
}

function IndexField(name, domain) {
  this.domain = domain;
  this.name = name;
  this.initialize();
};
IndexField.prototype = {
  initialize: function() {
    // for validation
    this.columnName;
    this.indexColumnName;
  },
  get columnName() {
    if (!this._columnName) {
      assertValidFieldName(this.name);
      this._columnName = this.name;
    }
    return this._columnName;
  },
  get indexColumnName() {
    if (!this._indexColumnName)
      this._indexColumnName = this.domain.tableName + '_' + this.columnName;
    return this._indexColumnName;
  },
  get alterTableName() {
    if (!this._alterTableName)
      this._alterTableName = this.domain.tableName + '_' + this.columnName;
    return this._alterTableName;
  },
  fieldTypeToColumnType: function(fieldType) {
    switch (fieldType) {
      case 'text':
        return Database.ShortText;
      case 'uint':
        return Database.UInt32;
      case 'literal':
        return this.alterTableName;
      default:
        throw new Error('Unsupported index field type '+fieldType);
  }
};

exports.IndexField = IndexField;
<commit_msg>Fix syntax error<commit_after>var http = require('http');

exports.MINIMUM_DOMAIN_NAME_LENGTH = 3;
exports.MAXIMUM_DOMAIN_NAME_LENGTH = 28;
exports.INVALID_DOMAIN_NAME_CHARACTER_PATTERN = /[^\-a-z0-9]/g;
exports.INVALID_TABLE_NAME_CHARACTER_PATTERN = /[^_a-z0-9]/g;

function assertValidDomainName(domain) {
  if (typeof domain != 'string')
    throw new Error('domain name must be a string');

  if (domain.length < exports.MINIMUM_DOMAIN_NAME_LENGTH)
    throw new Error('too short domain name (minimum length = ' +
                    exports.MINIMUM_DOMAIN_NAME_LENGTH + ')');

  if (domain.length > exports.MAXIMUM_DOMAIN_NAME_LENGTH)
    throw new Error('too long domain name (max length = ' +
                   exports.MAXIMUM_DOMAIN_NAME_LENGTH + ')');

  var invalidCharacter = domain.match(exports.INVALID_DOMAIN_NAME_CHARACTER_PATTERN) ||
                         domain.match(exports.INVALID_TABLE_NAME_CHARACTER_PATTERN);
  if (invalidCharacter) {
    var characters = Array.prototype.map.call(invalidCharacter, function(aCharacter) {
                       return '"' + aCharacter + '"';
                     });
    throw new Error(characters.join(', ') + ' cannot appear in a domain name');
  }
}

function Domain(source) {
  if (source instanceof Domain)
    return source;

  this.initialize(source);
}
Domain.prototype = {
  initialize: function(source) {
    this.name = this.getName(source);
    this.indexFields = {};
    // for validation
    this.tableName;
    this.termsTableName;
  },
  getName: function(source) {
    if (typeof source == 'string')
      return source;

    if (source.query && source.query.DomainName)
      return source.query.DomainName;

    if (source.headers && source.headers.host) {
      var host = source.headers.host;
      var domainNameFromHost = Domain.getNameFromHost(host);
      if (domainNameFromHost)
        return domainNameFromHost;
    }

    throw new Error('no domain name');
  },
  get tableName() {
    if (!this._tableName) {
      assertValidDomainName(this.name);
      this._tableName = this.name;
    }
    return this._tableName;
  },
  get termsTableName() {
    if (!this._termsTableName)
      this._termsTableName = this.tableName + '_BigramTerms';
    return this._termsTableName;
  },
  getAlterTableName: function(field) {
    return this.tableName + '_' + field;
  },
  getIndexField: function(field) {
    return this.indexFields[field] ||
           (this.indexFields[field] = new IndexField(field, this));
  }
};

exports.Domain = Domain;

Domain.getNameFromHost = function(host) {
  var domainMatcher = /^(?:doc|search)-([^\.]+)-([^\.\-]+)\./;
  var match = host.match(domainMatcher);
  if (match)
    return match[1];

  return '';
};

exports.MINIMUM_FIELD_NAME_LENGTH = 3;
exports.MAXIMUM_FIELD_NAME_LENGTH = 64;
exports.INVALID_FIELD_NAME_CHARACTER_PATTERN = /[^_a-z0-9]/g;
exports.INVALID_COLUMN_NAME_CHARACTER_PATTERN = /[^_a-z0-9]/g;
exports.RESERVED_FIELD_NAMES = [
  'body',
  'docid',
  'text_relevance'
];
exports.RESERVED_COLUMN_NAMES = [
  '_key'
];

function assertValidFieldName(field) {
  if (typeof field != 'string')
    throw new Error('field name must be a string');

  if (field.length < exports.MINIMUM_FIELD_NAME_LENGTH)
    throw new Error('too short field name (minimum length = ' +
                    exports.MINIMUM_FIELD_NAME_LENGTH + ')');

  if (field.length > exports.MAXIMUM_FIELD_NAME_LENGTH)
    throw new Error('too long field name (max length = ' +
                   exports.MAXIMUM_FIELD_NAME_LENGTH + ')');

  var invalidCharacter = field.match(exports.INVALID_FIELD_NAME_CHARACTER_PATTERN) ||
                         field.match(exports.INVALID_COLUMN_NAME_CHARACTER_PATTERN);
  if (invalidCharacter) {
    var characters = Array.prototype.map.call(invalidCharacter, function(aCharacter) {
                       return '"' + aCharacter + '"';
                     });
    throw new Error(characters.join(', ') + ' cannot appear in a field name');
  }

  var index = exports.RESERVED_FIELD_NAMES.indexOf(field);
  if (index > -1) index = exports.RESERVED_COLUMN_NAMES.indexOf(field);
  if (index > -1)
    throw new Error(field + ' is a reserved field name');
}

function IndexField(name, domain) {
  this.domain = domain;
  this.name = name;
  this.initialize();
};
IndexField.prototype = {
  initialize: function() {
    // for validation
    this.columnName;
    this.indexColumnName;
  },
  get columnName() {
    if (!this._columnName) {
      assertValidFieldName(this.name);
      this._columnName = this.name;
    }
    return this._columnName;
  },
  get indexColumnName() {
    if (!this._indexColumnName)
      this._indexColumnName = this.domain.tableName + '_' + this.columnName;
    return this._indexColumnName;
  },
  get alterTableName() {
    if (!this._alterTableName)
      this._alterTableName = this.domain.tableName + '_' + this.columnName;
    return this._alterTableName;
  },
  fieldTypeToColumnType: function(fieldType) {
    switch (fieldType) {
      case 'text':
        return Database.ShortText;
      case 'uint':
        return Database.UInt32;
      case 'literal':
        return this.alterTableName;
      default:
        throw new Error('Unsupported index field type '+fieldType);
    }
  }
};

exports.IndexField = IndexField;
<|endoftext|>


<commit_before>var fs = require('fs'),
    dox = require('dox'),
    hbs = require('handlebars'),
    helpers = require('../lib/helpers')(hbs),
    utils = require('../lib/utils');

var templates = {
    bootstrap: require('../templates/bootstrap.hbs'),
    markdown: require('../templates/markdown.hbs')
};

exports.parseFile = function (file, output, config) {

    if (fs.existsSync(file)) {

        exports.parseScript(fs.readFileSync(file, 'utf8'), output, config);

    } else {

        process.stdout.write('File not found.' + '\n');

    }

}

exports.parseScript = function (script, output, config) {

    var content,
        data = dox.parseComments(script);

    if (templates[config.layout]) {

        content = templates[config.layout]({
            title: config.title,
            description: config.description,
            methods: utils.parseData(data)
        });

        if (output) {

            fs.writeFileSync(output, content, 'utf8');

        } else {

            process.stdout.write(content);

            return content;

        }

    } else {

        process.stdout.write('Invalid layout specified.' + '\n');

    }

}
<commit_msg>parseFile now returns the correct value.<commit_after>var fs = require('fs'),
    dox = require('dox'),
    hbs = require('handlebars'),
    helpers = require('../lib/helpers')(hbs),
    utils = require('../lib/utils');

var templates = {
    bootstrap: require('../templates/bootstrap.hbs'),
    markdown: require('../templates/markdown.hbs')
};

exports.parseFile = function (file, output, config) {

    if (fs.existsSync(file)) {

        return exports.parseScript(fs.readFileSync(file, 'utf8'), output, config);

    } else {

        process.stdout.write('File not found.' + '\n');

    }

}

exports.parseScript = function (script, output, config) {

    var content,
        data = dox.parseComments(script);

    if (templates[config.layout]) {

        content = templates[config.layout]({
            title: config.title,
            description: config.description,
            methods: utils.parseData(data)
        });

        if (output) {

            fs.writeFileSync(output, content, 'utf8');

        } else {

            process.stdout.write(content);

            return content;

        }

    } else {

        process.stdout.write('Invalid layout specified.' + '\n');

    }

}
<|endoftext|>


<commit_before>Fractal.defineMixin('variables', {
	hooks: {
		afterRender: function() {
			this.replaceVariables();
		}
	},
	prototype: {
		// Replace {variable} definitions in the rendered HTML tree.
		replaceVariables: function( nodes ) {
			nodes || (nodes = this.template.fragmentNodes);
			Variable.parseElements(nodes, this.replaceVariablesWith());
		},
		// TODO: This is not the way to go. We should be able to bind a view to a model?
		//       Maybe a view should be a model itself?
		replaceVariablesWith: function() {
			return this.state;
		}
	}
});

// Variable class
// --------------
// Recognize variable definitions.
var _regVariables   = /\{('?)([^}.]+)\1(\.[^}]+)?\}/gi;
// Replace disallowed character from a string variable to turn it into
// an allowed javascript variable name.
var _regStr2VarName = /[^a-z0-9_]/gi;

// Additional modifiers can be added via `Fractal.Variable.modifier` method.
var Variable = Fractal.Variable = function( value, state ) {
	this.value = value != undefined ? value : '';
	this.state = state;
};
// {var}              Print variable value.
// {var.mod()}        Print variable value after tunning through modifier(s).
// {'string'}         Print string value (useless, but ok).
// {'string'.mod()}   Print string value after running through modifier(s).
Variable.parse = function( text, state ) {
	return text.replace(_regVariables, function ( match, quote, varName, modifiers ) {
		var isString = !!quote;

		if( isString && !modifiers ) {
			return varName;
		}

		var value = isString ? varName : state[varName];
		if( modifiers ) {
			var variable = new Variable(value, state);
			// Prepend _ to prevent problems with reserved keywords as variable names.
			varName = '_'+(isString ? varName.replace(_regStr2VarName, '_') : varName);
			Function(varName, varName+modifiers)(variable);
			value = variable.value;
		}

		return value != undefined ? value.toString() : '';
	});
};
Variable.parseElements = function parseElements( nodes, state, ignoreClones ) {
	if( !nodes && !nodes.length ) return;

	var node;
	for( var i = 0; node = nodes[i]; i++ ) {
		if( node instanceof Text ) {
			// Replace variables in text nodes.
			if( node.nodeValue.trim() ) {
				node.nodeValue = Variable.parse(node.nodeValue, state);
			}
		} else if( node instanceof Element ) {
			// Replace variables in attributes.
			if( node.hasAttributes() ) {
				var attrs = node.attributes, attr;
				for( var j = 0; attr = attrs[j]; j++ ) {
					attr.value = Variable.parse(attr.value, state);
				}
			}
			// Parse children.
			if( node.firstChild && !ignoreClones || !node.hasAttribute('data-clone') ) {
				parseElements(node.childNodes, state, true);
			}
		}
	}
};
Variable.modifier = function modifier( name, method ) {
	if( name.constructor == Object ) {
		for( var modName in name ) modifier(modName, name[modName]);
		return;
	}

	Variable.prototype[name] = method;
};

// Default modifiers
// -----------------
Fractal.Variable.modifier({
	date: function( format ) {
		var date = this.value;
		if( date ) {
			if( !(date instanceof Date) ) {
				date = new Date(+date);
			}
			if( !isNaN(+date) ) {
				this.value = MapKit.Date.strftime(date, format || 'j M Y');
			}
		}
		return this;
	},
	datetime: function( format ) {
		return this.date(format || 'j M Y H:i');
	},
	default: function( defaultValue ) {
		if( this.value == '' || this.value === null || this.value === undefined ) {
			this.value = defaultValue;
		}
		return this;
	},
	datetime: function( format ) {
		this.value = MapKit.Date.strftime(this.value, format||'j M Y H:i');
		return this;
	},
	prepend: function( value ) {
		this.value = value + this.value;
		return this;
	},
	upper: function() {
		this.value = this.value.toUpperCase();
		return this;
	},
	upperFirst: function() {
		if( this.value.length ) {
			this.value = this.value[0].toUpperCase() + this.value.substr(1);
		}
		return this;
	}
});
<commit_msg>Removed duplicate and too specific modifiers<commit_after>Fractal.defineMixin('variables', {
	hooks: {
		afterRender: function() {
			this.replaceVariables();
		}
	},
	prototype: {
		// Replace {variable} definitions in the rendered HTML tree.
		replaceVariables: function( nodes ) {
			nodes || (nodes = this.template.fragmentNodes);
			Variable.parseElements(nodes, this.replaceVariablesWith());
		},
		// TODO: This is not the way to go. We should be able to bind a view to a model?
		//       Maybe a view should be a model itself?
		replaceVariablesWith: function() {
			return this.state;
		}
	}
});

// Variable class
// --------------
// Recognize variable definitions.
var _regVariables   = /\{('?)([^}.]+)\1(\.[^}]+)?\}/gi;
// Replace disallowed character from a string variable to turn it into
// an allowed javascript variable name.
var _regStr2VarName = /[^a-z0-9_]/gi;

// Additional modifiers can be added via `Fractal.Variable.modifier` method.
var Variable = Fractal.Variable = function( value, state ) {
	this.value = value != undefined ? value : '';
	this.state = state;
};
// {var}              Print variable value.
// {var.mod()}        Print variable value after tunning through modifier(s).
// {'string'}         Print string value (useless, but ok).
// {'string'.mod()}   Print string value after running through modifier(s).
Variable.parse = function( text, state ) {
	return text.replace(_regVariables, function ( match, quote, varName, modifiers ) {
		var isString = !!quote;

		if( isString && !modifiers ) {
			return varName;
		}

		var value = isString ? varName : state[varName];
		if( modifiers ) {
			var variable = new Variable(value, state);
			// Prepend _ to prevent problems with reserved keywords as variable names.
			varName = '_'+(isString ? varName.replace(_regStr2VarName, '_') : varName);
			Function(varName, varName+modifiers)(variable);
			value = variable.value;
		}

		return value != undefined ? value.toString() : '';
	});
};
Variable.parseElements = function parseElements( nodes, state, ignoreClones ) {
	if( !nodes && !nodes.length ) return;

	var node;
	for( var i = 0; node = nodes[i]; i++ ) {
		if( node instanceof Text ) {
			// Replace variables in text nodes.
			if( node.nodeValue.trim() ) {
				node.nodeValue = Variable.parse(node.nodeValue, state);
			}
		} else if( node instanceof Element ) {
			// Replace variables in attributes.
			if( node.hasAttributes() ) {
				var attrs = node.attributes, attr;
				for( var j = 0; attr = attrs[j]; j++ ) {
					attr.value = Variable.parse(attr.value, state);
				}
			}
			// Parse children.
			if( node.firstChild && !ignoreClones || !node.hasAttribute('data-clone') ) {
				parseElements(node.childNodes, state, true);
			}
		}
	}
};
Variable.modifier = function modifier( name, method ) {
	if( name.constructor == Object ) {
		for( var modName in name ) modifier(modName, name[modName]);
		return;
	}

	Variable.prototype[name] = method;
};

// Default modifiers
// -----------------
Fractal.Variable.modifier({
	default: function( defaultValue ) {
		if( this.value == '' || this.value === null || this.value === undefined ) {
			this.value = defaultValue;
		}
		return this;
	},
	prepend: function( value ) {
		this.value = value + this.value;
		return this;
	},
	upper: function() {
		this.value = this.value.toUpperCase();
		return this;
	},
	upperFirst: function() {
		if( this.value.length ) {
			this.value = this.value[0].toUpperCase() + this.value.substr(1);
		}
		return this;
	}
});
<|endoftext|>