<commit_before>import koa_router from 'koa-router';
import koa_body from 'koa-body';
import models from 'db/models';
import findUser from 'db/utils/find_user';
import config from 'config';
import recordWebEvent from 'server/record_web_event';
import {esc, escAttrs} from 'db/models';
import {emailRegex, getRemoteIp, rateLimitReq, checkCSRF} from 'server/utils';
import coBody from 'co-body';
import {getLogger} from '../../app/utils/Logger'
import {Apis} from 'shared/api_client';
import {createTransaction, signTransaction} from 'shared/chain/transactions';
import {ops} from 'shared/serializer';
const {signed_transaction} = ops;
const print = getLogger('API - general').print

function dbStoreSingleMeta(name, k, v) {
    models.AccountMeta.findOne({
        attributes: [
            'accname', 'k', 'v'
        ],
        where: {
            accname: esc(name),
            k: esc(k)
        }
    }).then(function(it) {
        if (it) {
            if (it.dataValues.v !== v)
                models.AccountMeta.update({
                    v: esc(v)
                }, {
                    where: {
                        accname: esc(name),
                        k: esc(k)
                    }
                });
            }
        else {
            models.AccountMeta.create({accname: esc(name), k: esc(k), v: esc(v)});
        }
    });
}

export default function useGeneralApi(app) {
    const router = koa_router({
        prefix: '/api/v1'
    });
    app.use(router.routes());
    const koaBody = koa_body();

    router.post('/accounts', koaBody, function*() {
        if (rateLimitReq(this, this.req)) return;
        const params = this.request.body;
        print('params', params)
        const account = typeof(params) === 'string' ? JSON.parse(params) : params;
        if (!checkCSRF(this, account.csrf)) return;
        console.log('-- /accounts -->', this.session.uid, this.session.user, account);

        if ($STM_Config.disable_signups) {
            this.body = JSON.stringify({
                error: 'New signups are temporary disabled.'
            });
            this.status = 401;
            return;
        }

        try {
            const meta = {}
            const remote_ip = getRemoteIp(this.req);
            const user_id = this.session.user;
            if (!user_id) { // require user to sign in with identity provider
                this.body = JSON.stringify({
                    error: 'Unauthorized'
                });
                this.status = 401;
                return;
            }

            const user = yield models.User.findOne({
                attributes: ['verified', 'waiting_list'],
                where: {
                    id: user_id
                }
            });
            if (!user) {
                this.body = JSON.stringify({
                    error: 'Unauthorized'
                });
                this.status = 401;
                return;
            }

            const existing_account = yield models.Account.findOne({
                attributes: ['id', 'created_at'],
                where: {user_id, ignored: false},
                order: 'id DESC'
            });
            if (existing_account) { //TODO
                throw new Error("Only one Steem account per user is allowed in order to prevent abuse (Steemit, Inc. funds each new account with 3 STEEM)");
            }

            const same_ip_account = yield models.Account.findOne({
                attributes: ['created_at'],
                where: {
                    remote_ip: esc(remote_ip)
                },
                order: 'id DESC'
            });
            if (same_ip_account) {
                const minutes = (Date.now() - same_ip_account.created_at) / 60000;
                if (minutes < 10) {
                    console.log(`api /accounts: IP rate limit for user ${this.session.uid} #${user_id}, IP ${remote_ip}`);
                    throw new Error('Only one Steem account allowed per IP address every 10 minutes');
                }
            }
            if (user.waiting_list) {
                console.log(`api /accounts: waiting_list user ${this.session.uid} #${user_id}`);
                throw new Error('You are on the waiting list. We will get back to you at the earliest possible opportunity.');
            }
            const eid = yield models.Identity.findOne({
                attributes: ['id'],
                where: {
                    user_id,
                    provider: 'email',
                    verified: true
                },
                order: 'id DESC'
            });
            if (!eid) {
                console.log(`api /accounts: not confirmed email for user ${this.session.uid} #${user_id}`);
                throw new Error('Email address is not confirmed');
            }
            yield createAccount({
                signingKey: config.registrar.signing_key,
                fee: config.registrar.fee,
                creator: config.registrar.account,
                new_account_name: account.name,
                json_metadata: JSON.stringify(new Object()),
                owner: account.owner_key,
                active: account.active_key,
                posting: account.posting_key,
                memo: account.memo_key,
                broadcast: true
            });
            console.log('-- create_account_with_keys created -->', this.session.uid, account.name, user.id, account.owner_key);

            this.body = JSON.stringify({
                status: 'ok'
            });
            models.Account.create(escAttrs({
                    user_id,
                    name: account.name,
                    owner_key: account.owner_key,
                    active_key: account.active_key,
                    posting_key: account.posting_key,
                    memo_key: account.memo_key,
                    remote_ip,
                    referrer: this.session.r
                })).then(instance => {
                })
                .catch(error => {
                    console.error('!!! Can\'t create account model in /accounts api', this.session.uid, error);
                });
        } catch (error) {
            console.error('Error in /accounts api call', this.session.uid, error.toString());
            this.body = JSON.stringify({
                error: error.message
            });
            this.status = 500;
        }
        recordWebEvent(this, 'api/accounts', account ? account.name : 'n/a');
    });

    router.post('/update_email', koaBody, function*() {
        if (rateLimitReq(this, this.req)) return;
        const params = this.request.body;
        const {
            csrf,
            email
        } = typeof(params) === 'string' ? JSON.parse(params): params;
        if (!checkCSRF(this, csrf)) return;
        console.log('-- /update_email -->', this.session.uid, email);
        try {
            if (!emailRegex.test(email.toLowerCase())) throw new Error('not valid email: ' + email);
            // TODO: limit by 1/min/ip
            let user = yield findUser({
                user_id: this.session.user,
                email: esc(email),
                uid: this.session.uid
            });
            if (user) {
                user = yield models.User.update({
                    email: esc(email),
                    waiting_list: true
                }, {
                    where: {
                        id: user.id
                    }
                });
            } else {
                user = yield models.User.create({
                    email: esc(email),
                    waiting_list: true
                });
            }
            this.session.user = user.id;
            this.body = JSON.stringify({
                status: 'ok'
            });
        } catch (error) {
            console.error('Error in /update_email api call', this.session.uid, error);
            this.body = JSON.stringify({
                error: error.message
            });
            this.status = 500;
        }
        recordWebEvent(this, 'api/update_email', email);
    });

    router.post('/login_account', koaBody, function*() {
        if (rateLimitReq(this, this.req)) return;
        const params = this.request.body;
        const {
            csrf,
            account
        } = typeof(params) === 'string' ? JSON.parse(params): params;
        if (!checkCSRF(this, csrf)) return;
        console.log('-- /login_account -->', this.session.uid, account);
        try {
            this.session.a = account;
            const db_account = yield models.Account.findOne({
                attributes: ['user_id'],
                where: {
                    name: esc(account)
                }
            });
            if (db_account) this.session.user = db_account.user_id;
            this.body = JSON.stringify({
                status: 'ok'
            });
        } catch (error) {
            console.error('Error in /login_account api call', this.session.uid, error);
            this.body = JSON.stringify({
                error: error.message
            });
            this.status = 500;
        }
        recordWebEvent(this, 'api/login_account', account);
    });

    router.post('/logout_account', koaBody, function*() {
        // if (rateLimitReq(this, this.req)) return; - logout maybe immediately followed with login_attempt event
        const params = this.request.body;
        const {
            csrf
        } = typeof(params) === 'string' ? JSON.parse(params): params;
        if (!checkCSRF(this, csrf)) return;
        console.log('-- /logout_account -->', this.session.uid);
        try {
            this.session.a = null;
            this.body = JSON.stringify({
                status: 'ok'
            });
        } catch (error) {
            console.error('Error in /logout_account api call', this.session.uid, error);
            this.body = JSON.stringify({
                error: error.message
            });
            this.status = 500;
        }
    });

    router.post('/record_event', koaBody, function*() {
        if (rateLimitReq(this, this.req)) return;
        try {
            const params = this.request.body;
            const {
                csrf,
                type,
                value
            } = typeof(params) === 'string' ? JSON.parse(params): params;
            if (!checkCSRF(this, csrf)) return;
            console.log('-- /record_event -->', this.session.uid, type, value);
            const str_value = typeof value === 'string' ? value : JSON.stringify(value);
            this.body = JSON.stringify({
                status: 'ok'
            });
            recordWebEvent(this, type, str_value);
        } catch (error) {
            console.error('Error in /record_event api call', error);
            this.body = JSON.stringify({
                error: error.message
            });
            this.status = 500;
        }
    });

    router.post('/csp_violation', function*() {
        if (rateLimitReq(this, this.req)) return;
        const params = yield coBody.json(this);
        console.log('-- /csp_violation -->', this.req.headers['user-agent'], params);
        this.body = '';
    });

router.post('/account_update_hook', koaBody, function * () {
    //if (rateLimitReq(this, this.req)) return;
    const params = this.request.body;
    const {csrf, account_name} = typeof(params) === 'string'
        ? JSON.parse(params)
        : params;
    if (!checkCSRF(this, csrf))
        return; // disable for mass operations
    console.log(account_name);
    this.body = JSON.stringify({status: 'in process'});
    Apis.db_api('get_accounts', [account_name, 'cosmos']).then(function(response) {
        if (!response)
            return;
        response.forEach(function(account) {
            const json_metadata = account.json_metadata;
            const name = account.name;
            var meta = null
            console.log('updating meta for acc ' + name);
            try {
                meta = JSON.parse(json_metadata)
            } catch (e) {
                console.log(`account ${name} has invalid json_metadata`);
                return;
            }
            for (var p in meta) {
                if (meta.hasOwnProperty(p)) {
                    dbStoreSingleMeta(name, pair[0], pair[1]);
                }
            }
        })
    }).catch(function(error) {
        console.log("error when updating account meta table", error)
    });
});
}

/**
 @arg signingKey {string|PrivateKey} - WIF or PrivateKey object
 */
function* createAccount({
    signingKey,
    fee,
    creator,
    new_account_name,
    json_metadata = '',
    owner,
    active,
    posting,
    memo,
    broadcast = false,
}) {
    const operations = [
        ['account_create', {
            fee,
            creator,
            new_account_name,
            json_metadata,
            owner: {
                weight_threshold: 1,
                account_auths: [],
                key_auths: [
                    [owner, 1]
                ]
            },
            active: {
                weight_threshold: 1,
                account_auths: [],
                key_auths: [
                    [active, 1]
                ]
            },
            posting: {
                weight_threshold: 1,
                account_auths: [],
                key_auths: [
                    [posting, 1]
                ]
            },
            memo_key: memo,
        }]
    ]
    const tx = yield createTransaction(operations)
    const sx = signTransaction(tx, signingKey)
    if (!broadcast) return signed_transaction.toObject(sx)
    return yield new Promise((resolve, reject) =>
        Apis.broadcastTransaction(sx, () => {
            resolve()
        }).catch(e => {
            reject(e)
        })
    )
}
<commit_msg> bugfix<commit_after>import koa_router from 'koa-router';
import koa_body from 'koa-body';
import models from 'db/models';
import findUser from 'db/utils/find_user';
import config from 'config';
import recordWebEvent from 'server/record_web_event';
import {esc, escAttrs} from 'db/models';
import {emailRegex, getRemoteIp, rateLimitReq, checkCSRF} from 'server/utils';
import coBody from 'co-body';
import {getLogger} from '../../app/utils/Logger'
import {Apis} from 'shared/api_client';
import {createTransaction, signTransaction} from 'shared/chain/transactions';
import {ops} from 'shared/serializer';
const {signed_transaction} = ops;
const print = getLogger('API - general').print

function dbStoreSingleMeta(name, k, v) {
    models.AccountMeta.findOne({
        attributes: [
            'accname', 'k', 'v'
        ],
        where: {
            accname: esc(name),
            k: esc(k)
        }
    }).then(function(it) {
        if (it) {
            if (it.dataValues.v !== v)
                models.AccountMeta.update({
                    v: esc(v)
                }, {
                    where: {
                        accname: esc(name),
                        k: esc(k)
                    }
                });
            }
        else {
            models.AccountMeta.create({accname: esc(name), k: esc(k), v: esc(v)});
        }
    });
}

export default function useGeneralApi(app) {
    const router = koa_router({
        prefix: '/api/v1'
    });
    app.use(router.routes());
    const koaBody = koa_body();

    router.post('/accounts', koaBody, function*() {
        if (rateLimitReq(this, this.req)) return;
        const params = this.request.body;
        print('params', params)
        const account = typeof(params) === 'string' ? JSON.parse(params) : params;
        if (!checkCSRF(this, account.csrf)) return;
        console.log('-- /accounts -->', this.session.uid, this.session.user, account);

        if ($STM_Config.disable_signups) {
            this.body = JSON.stringify({
                error: 'New signups are temporary disabled.'
            });
            this.status = 401;
            return;
        }

        try {
            const meta = {}
            const remote_ip = getRemoteIp(this.req);
            const user_id = this.session.user;
            if (!user_id) { // require user to sign in with identity provider
                this.body = JSON.stringify({
                    error: 'Unauthorized'
                });
                this.status = 401;
                return;
            }

            const user = yield models.User.findOne({
                attributes: ['verified', 'waiting_list'],
                where: {
                    id: user_id
                }
            });
            if (!user) {
                this.body = JSON.stringify({
                    error: 'Unauthorized'
                });
                this.status = 401;
                return;
            }

            const existing_account = yield models.Account.findOne({
                attributes: ['id', 'created_at'],
                where: {user_id, ignored: false},
                order: 'id DESC'
            });
            if (existing_account) { //TODO
                throw new Error("Only one Steem account per user is allowed in order to prevent abuse (Steemit, Inc. funds each new account with 3 STEEM)");
            }

            const same_ip_account = yield models.Account.findOne({
                attributes: ['created_at'],
                where: {
                    remote_ip: esc(remote_ip)
                },
                order: 'id DESC'
            });
            if (same_ip_account) {
                const minutes = (Date.now() - same_ip_account.created_at) / 60000;
                if (minutes < 10) {
                    console.log(`api /accounts: IP rate limit for user ${this.session.uid} #${user_id}, IP ${remote_ip}`);
                    throw new Error('Only one Steem account allowed per IP address every 10 minutes');
                }
            }
            if (user.waiting_list) {
                console.log(`api /accounts: waiting_list user ${this.session.uid} #${user_id}`);
                throw new Error('You are on the waiting list. We will get back to you at the earliest possible opportunity.');
            }
            const eid = yield models.Identity.findOne({
                attributes: ['id'],
                where: {
                    user_id,
                    provider: 'email',
                    verified: true
                },
                order: 'id DESC'
            });
            if (!eid) {
                console.log(`api /accounts: not confirmed email for user ${this.session.uid} #${user_id}`);
                throw new Error('Email address is not confirmed');
            }
            yield createAccount({
                signingKey: config.registrar.signing_key,
                fee: config.registrar.fee,
                creator: config.registrar.account,
                new_account_name: account.name,
                json_metadata: JSON.stringify(new Object()),
                owner: account.owner_key,
                active: account.active_key,
                posting: account.posting_key,
                memo: account.memo_key,
                broadcast: true
            });
            console.log('-- create_account_with_keys created -->', this.session.uid, account.name, user.id, account.owner_key);

            this.body = JSON.stringify({
                status: 'ok'
            });
            models.Account.create(escAttrs({
                    user_id,
                    name: account.name,
                    owner_key: account.owner_key,
                    active_key: account.active_key,
                    posting_key: account.posting_key,
                    memo_key: account.memo_key,
                    remote_ip,
                    referrer: this.session.r
                })).then(instance => {
                })
                .catch(error => {
                    console.error('!!! Can\'t create account model in /accounts api', this.session.uid, error);
                });
        } catch (error) {
            console.error('Error in /accounts api call', this.session.uid, error.toString());
            this.body = JSON.stringify({
                error: error.message
            });
            this.status = 500;
        }
        recordWebEvent(this, 'api/accounts', account ? account.name : 'n/a');
    });

    router.post('/update_email', koaBody, function*() {
        if (rateLimitReq(this, this.req)) return;
        const params = this.request.body;
        const {
            csrf,
            email
        } = typeof(params) === 'string' ? JSON.parse(params): params;
        if (!checkCSRF(this, csrf)) return;
        console.log('-- /update_email -->', this.session.uid, email);
        try {
            if (!emailRegex.test(email.toLowerCase())) throw new Error('not valid email: ' + email);
            // TODO: limit by 1/min/ip
            let user = yield findUser({
                user_id: this.session.user,
                email: esc(email),
                uid: this.session.uid
            });
            if (user) {
                user = yield models.User.update({
                    email: esc(email),
                    waiting_list: true
                }, {
                    where: {
                        id: user.id
                    }
                });
            } else {
                user = yield models.User.create({
                    email: esc(email),
                    waiting_list: true
                });
            }
            this.session.user = user.id;
            this.body = JSON.stringify({
                status: 'ok'
            });
        } catch (error) {
            console.error('Error in /update_email api call', this.session.uid, error);
            this.body = JSON.stringify({
                error: error.message
            });
            this.status = 500;
        }
        recordWebEvent(this, 'api/update_email', email);
    });

    router.post('/login_account', koaBody, function*() {
        if (rateLimitReq(this, this.req)) return;
        const params = this.request.body;
        const {
            csrf,
            account
        } = typeof(params) === 'string' ? JSON.parse(params): params;
        if (!checkCSRF(this, csrf)) return;
        console.log('-- /login_account -->', this.session.uid, account);
        try {
            this.session.a = account;
            const db_account = yield models.Account.findOne({
                attributes: ['user_id'],
                where: {
                    name: esc(account)
                }
            });
            if (db_account) this.session.user = db_account.user_id;
            this.body = JSON.stringify({
                status: 'ok'
            });
        } catch (error) {
            console.error('Error in /login_account api call', this.session.uid, error);
            this.body = JSON.stringify({
                error: error.message
            });
            this.status = 500;
        }
        recordWebEvent(this, 'api/login_account', account);
    });

    router.post('/logout_account', koaBody, function*() {
        // if (rateLimitReq(this, this.req)) return; - logout maybe immediately followed with login_attempt event
        const params = this.request.body;
        const {
            csrf
        } = typeof(params) === 'string' ? JSON.parse(params): params;
        if (!checkCSRF(this, csrf)) return;
        console.log('-- /logout_account -->', this.session.uid);
        try {
            this.session.a = null;
            this.body = JSON.stringify({
                status: 'ok'
            });
        } catch (error) {
            console.error('Error in /logout_account api call', this.session.uid, error);
            this.body = JSON.stringify({
                error: error.message
            });
            this.status = 500;
        }
    });

    router.post('/record_event', koaBody, function*() {
        if (rateLimitReq(this, this.req)) return;
        try {
            const params = this.request.body;
            const {
                csrf,
                type,
                value
            } = typeof(params) === 'string' ? JSON.parse(params): params;
            if (!checkCSRF(this, csrf)) return;
            console.log('-- /record_event -->', this.session.uid, type, value);
            const str_value = typeof value === 'string' ? value : JSON.stringify(value);
            this.body = JSON.stringify({
                status: 'ok'
            });
            recordWebEvent(this, type, str_value);
        } catch (error) {
            console.error('Error in /record_event api call', error);
            this.body = JSON.stringify({
                error: error.message
            });
            this.status = 500;
        }
    });

    router.post('/csp_violation', function*() {
        if (rateLimitReq(this, this.req)) return;
        const params = yield coBody.json(this);
        console.log('-- /csp_violation -->', this.req.headers['user-agent'], params);
        this.body = '';
    });

router.post('/account_update_hook', koaBody, function * () {
    //if (rateLimitReq(this, this.req)) return;
    const params = this.request.body;
    const {csrf, account_name} = typeof(params) === 'string'
        ? JSON.parse(params)
        : params;
    if (!checkCSRF(this, csrf))
        return; // disable for mass operations
    console.log(account_name);
    this.body = JSON.stringify({status: 'in process'});
    Apis.db_api('get_accounts', [account_name, 'cosmos']).then(function(response) {
        if (!response)
            return;
        response.forEach(function(account) {
            const json_metadata = account.json_metadata;
            const name = account.name;
            var meta = null
            console.log('updating meta for acc ' + name);
            try {
                meta = JSON.parse(json_metadata)
            } catch (e) {
                console.log(`account ${name} has invalid json_metadata`);
                return;
            }
            for (var p in meta) {
                if (meta.hasOwnProperty(p)) {
                    dbStoreSingleMeta(name, p, meta[p]);
                }
            }
        })
    }).catch(function(error) {
        console.log("error when updating account meta table", error)
    });
});
}

/**
 @arg signingKey {string|PrivateKey} - WIF or PrivateKey object
 */
function* createAccount({
    signingKey,
    fee,
    creator,
    new_account_name,
    json_metadata = '',
    owner,
    active,
    posting,
    memo,
    broadcast = false,
}) {
    const operations = [
        ['account_create', {
            fee,
            creator,
            new_account_name,
            json_metadata,
            owner: {
                weight_threshold: 1,
                account_auths: [],
                key_auths: [
                    [owner, 1]
                ]
            },
            active: {
                weight_threshold: 1,
                account_auths: [],
                key_auths: [
                    [active, 1]
                ]
            },
            posting: {
                weight_threshold: 1,
                account_auths: [],
                key_auths: [
                    [posting, 1]
                ]
            },
            memo_key: memo,
        }]
    ]
    const tx = yield createTransaction(operations)
    const sx = signTransaction(tx, signingKey)
    if (!broadcast) return signed_transaction.toObject(sx)
    return yield new Promise((resolve, reject) =>
        Apis.broadcastTransaction(sx, () => {
            resolve()
        }).catch(e => {
            reject(e)
        })
    )
}
<|endoftext|>


<commit_before>/* eslint-disable no-console */

var fs = require('fs');
var rollup = require('rollup');
var nodeResolve = require('rollup-plugin-node-resolve');
var commonjs = require('rollup-plugin-commonjs');
var json = require('rollup-plugin-json');
var gaze = require('gaze');
var ecstatic = require('ecstatic');
var express = require('express'),
    app = express(),
    url = require('url'),
    proxy = require('express-http-proxy');

var building = false;

if (process.argv[2] === 'develop') {
    build();

    gaze(['modules/**/*.js', 'data/**/*.{js,json}'], function(err, watcher) {
        watcher.on('all', function() {
            build();
        });
    });

    var port = 8080;
    var hootHost = 'localhost';
    var hootPort = 8888;
    var hootUrl = 'http://' + hootHost + ':' + hootPort;

    app.use(
        ecstatic({ root: __dirname, cache: 0 })
    ).listen(port);

    app.get('/hootenanny-id', function(req, res) {
        res.writeHead(301,
          { Location: 'http://localhost:8080' }
        );
        res.end();
    });

    app.use('/hoot-services', proxy(hootUrl, {
        forwardPath: function(req) {
            return '/hoot-services' + url.parse(req.url).path;
        }
    }));

    app.use('/static', proxy(hootUrl, {
        forwardPath: function(req) {
            return '/static' + url.parse(req.url).path;
        }
    }));

    console.log('Listening on ' + port);

} else {
    build();
}

function unlink(f) {
    try { fs.unlinkSync(f); } catch (e) { /* noop */ }
}

function build() {
    if (building) return;

    // Start clean
    unlink('dist/iD.js');
    unlink('dist/iD.js.map');

    building = true;
    console.log('Rebuilding');
    console.time('Rebuilt');

    rollup.rollup({
        entry: './modules/id.js',
        plugins: [
            nodeResolve({
                jsnext: true, main: true, browser: false
            }),
            commonjs(),
            json()
        ]

    }).then(function (bundle) {
        bundle.write({
            format: 'iife',
            dest: 'dist/iD.js',
            sourceMap: true,
            useStrict: false
        });
        building = false;
        console.timeEnd('Rebuilt');

    }, function(err) {
        building = false;
        console.error(err);
    });
}

<commit_msg>update ports to nome-in-a-box env<commit_after>/* eslint-disable no-console */

var fs = require('fs');
var rollup = require('rollup');
var nodeResolve = require('rollup-plugin-node-resolve');
var commonjs = require('rollup-plugin-commonjs');
var json = require('rollup-plugin-json');
var gaze = require('gaze');
var ecstatic = require('ecstatic');
var express = require('express'),
    app = express(),
    url = require('url'),
    proxy = require('express-http-proxy');

var building = false;

if (process.argv[2] === 'develop') {
    build();

    gaze(['modules/**/*.js', 'data/**/*.{js,json}'], function(err, watcher) {
        watcher.on('all', function() {
            build();
        });
    });

    var port = 8088;
    var hootHost = 'localhost';
    var hootPort = 8080;
    var hootUrl = 'http://' + hootHost + ':' + hootPort;

    app.use(
        ecstatic({ root: __dirname, cache: 0 })
    ).listen(port);

    app.get('/hootenanny-id', function(req, res) {
        res.writeHead(301,
          { Location: 'http://localhost:8080' }
        );
        res.end();
    });

    app.use('/hoot-services', proxy(hootUrl, {
        forwardPath: function(req) {
            return '/hoot-services' + url.parse(req.url).path;
        }
    }));

    app.use('/static', proxy(hootUrl, {
        forwardPath: function(req) {
            return '/static' + url.parse(req.url).path;
        }
    }));

    console.log('Listening on ' + port);

} else {
    build();
}

function unlink(f) {
    try { fs.unlinkSync(f); } catch (e) { /* noop */ }
}

function build() {
    if (building) return;

    // Start clean
    unlink('dist/iD.js');
    unlink('dist/iD.js.map');

    building = true;
    console.log('Rebuilding');
    console.time('Rebuilt');

    rollup.rollup({
        entry: './modules/id.js',
        plugins: [
            nodeResolve({
                jsnext: true, main: true, browser: false
            }),
            commonjs(),
            json()
        ]

    }).then(function (bundle) {
        bundle.write({
            format: 'iife',
            dest: 'dist/iD.js',
            sourceMap: true,
            useStrict: false
        });
        building = false;
        console.timeEnd('Rebuilt');

    }, function(err) {
        building = false;
        console.error(err);
    });
}

<|endoftext|>


<commit_before>const powerDigitSum = n => {
  return Math.pow(2, n)
  .toString()
  .split('')
  .map(c => parseInt(c))
  .reduce((r, n) => r + n)
}

// works for 15, but returns NaN for 1000
console.log(powerDigitSum(15))
<commit_msg>style: simple arrow fn<commit_after>const powerDigitSum = n => Math.pow(2, n)
  .toString()
  .split('')
  .map(c => parseInt(c))
  .reduce((r, n) => r + n)

// works for 15, but returns NaN for 1000
console.log(powerDigitSum(15))
<|endoftext|>


<commit_before>frappe.provide('erpnext.stock');

erpnext.stock.ItemDashboard = Class.extend({
	init: function(opts) {
		$.extend(this, opts);
		this.make();
	},
	make: function() {
		var me = this;
		this.start = 0;
		if(!this.sort_by) {
			this.sort_by = 'projected_qty';
			this.sort_order = 'asc';
		}

		this.content = $(frappe.render_template('item_dashboard')).appendTo(this.parent);
		this.result = this.content.find('.result');

		// move
		this.content.on('click', '.btn-move', function() {
			erpnext.stock.move_item(unescape($(this).attr('data-item')), $(this).attr('data-warehouse'),
				null, $(this).attr('data-actual_qty'), null, function() { me.refresh(); });
		});

		this.content.on('click', '.btn-add', function() {
			erpnext.stock.move_item(unescape($(this).attr('data-item')), null, $(this).attr('data-warehouse'),
				$(this).attr('data-actual_qty'), $(this).attr('data-rate'),
				function() { me.refresh(); });
		});

		// more
		this.content.find('.btn-more').on('click', function() {
			me.start += 20;
			me.refresh();
		});

	},
	refresh: function() {
		if(this.before_refresh) {
			this.before_refresh();
		}

		var me = this;
		frappe.call({
			method: 'erpnext.stock.dashboard.item_dashboard.get_data',
			args: {
				item_code: this.item_code,
				warehouse: this.warehouse,
				item_group: this.item_group,
				start: this.start,
				sort_by: this.sort_by,
				sort_order: this.sort_order,
			},
			callback: function(r) {
				me.render(r.message);
			}
		});
	},
	render: function(data) {
		if(this.start===0) {
			this.max_count = 0;
			this.result.empty();
		}

		var context = this.get_item_dashboard_data(data, this.max_count, true);
		this.max_count = this.max_count;

		// show more button
		if(data && data.length===21) {
			this.content.find('.more').removeClass('hidden');

			// remove the last element
			data.splice(-1);
		} else {
			this.content.find('.more').addClass('hidden');
		}

		// If not any stock in any warehouses provide a message to end user
		if (context.data.length > 0) {
			$(frappe.render_template('item_dashboard_list', context)).appendTo(this.result);
		} else {
			var message = __(" Currently no stock available in any warehouse")
			$(""+message+"").appendTo(this.result);
		}
	},
	get_item_dashboard_data: function(data, max_count, show_item) {
		if(!max_count) max_count = 0;
		if(!data) data = [];

		data.forEach(function(d) {
			d.actual_or_pending = d.projected_qty + d.reserved_qty + d.reserved_qty_for_production + d.reserved_qty_for_sub_contract;
			d.pending_qty = 0;
			d.total_reserved = d.reserved_qty + d.reserved_qty_for_production + d.reserved_qty_for_sub_contract;
			if(d.actual_or_pending > d.actual_qty) {
				d.pending_qty = d.actual_or_pending - d.actual_qty;
			}

			max_count = Math.max(d.actual_or_pending, d.actual_qty,
				d.total_reserved, max_count);
		});

		var can_write = 0;
		if(frappe.boot.user.can_write.indexOf("Stock Entry")>=0){
			can_write = 1;
		}

		return {
			data: data,
			max_count: max_count,
			can_write:can_write,
			show_item: show_item || false
		}
	}
})

erpnext.stock.move_item = function(item, source, target, actual_qty, rate, callback) {
	var dialog = new frappe.ui.Dialog({
		title: target ? __('Add Item') : __('Move Item'),
		fields: [
			{fieldname: 'item_code', label: __('Item'),
				fieldtype: 'Link', options: 'Item', read_only: 1},
			{fieldname: 'source', label: __('Source Warehouse'),
				fieldtype: 'Link', options: 'Warehouse', read_only: 1},
			{fieldname: 'target', label: __('Target Warehouse'),
				fieldtype: 'Link', options: 'Warehouse', reqd: 1},
			{fieldname: 'qty', label: __('Quantity'), reqd: 1,
				fieldtype: 'Float', description: __('Available {0}', [actual_qty]) },
			{fieldname: 'rate', label: __('Rate'), fieldtype: 'Currency', hidden: 1 },
		],
	})
	dialog.show();
	dialog.get_field('item_code').set_input(item);

	if(source) {
		dialog.get_field('source').set_input(source);
	} else {
		dialog.get_field('source').df.hidden = 1;
		dialog.get_field('source').refresh();
	}

	if(rate) {
		dialog.get_field('rate').set_value(rate);
		dialog.get_field('rate').df.hidden = 0;
		dialog.get_field('rate').refresh();
	}

	if(target) {
		dialog.get_field('target').df.read_only = 1;
		dialog.get_field('target').value = target;
		dialog.get_field('target').refresh();
	}

	dialog.set_primary_action(__('Submit'), function() {
		var values = dialog.get_values();
		if(!values) {
			return;
		}
		if(source && values.qty > actual_qty) {
			frappe.msgprint(__('Quantity must be less than or equal to {0}', [actual_qty]));
			return;
		}
		if(values.source === values.target) {
			frappe.msgprint(__('Source and target warehouse must be different'));
		}

		frappe.call({
			method: 'erpnext.stock.doctype.stock_entry.stock_entry_utils.make_stock_entry',
			args: values,
			callback: function(r) {
				frappe.show_alert(__('Stock Entry {0} created',
					['' + r.message.name+ '']));
				dialog.hide();
				callback(r);
			},
		});
	});

	$('
'
		+ __("Add more items or open full form") + '
')
		.appendTo(dialog.body)
		.find('.link-open')
		.on('click', function() {
			frappe.model.with_doctype('Stock Entry', function() {
				var doc = frappe.model.get_new_doc('Stock Entry');
				doc.from_warehouse = dialog.get_value('source');
				doc.to_warehouse = dialog.get_value('target');
				var row = frappe.model.add_child(doc, 'items');
				row.item_code = dialog.get_value('item_code');
				row.f_warehouse = dialog.get_value('target');
				row.t_warehouse = dialog.get_value('target');
				row.qty = dialog.get_value('qty');
				row.conversion_factor = 1;
				row.transfer_qty = dialog.get_value('qty');
				row.basic_rate = dialog.get_value('rate');
				frappe.set_route('Form', doc.doctype, doc.name);
			})
		});
}<commit_msg>[minor] Freeze the screen to avoid multiple time clicks (#15047)<commit_after>frappe.provide('erpnext.stock');

erpnext.stock.ItemDashboard = Class.extend({
	init: function(opts) {
		$.extend(this, opts);
		this.make();
	},
	make: function() {
		var me = this;
		this.start = 0;
		if(!this.sort_by) {
			this.sort_by = 'projected_qty';
			this.sort_order = 'asc';
		}

		this.content = $(frappe.render_template('item_dashboard')).appendTo(this.parent);
		this.result = this.content.find('.result');

		// move
		this.content.on('click', '.btn-move', function() {
			erpnext.stock.move_item(unescape($(this).attr('data-item')), $(this).attr('data-warehouse'),
				null, $(this).attr('data-actual_qty'), null, function() { me.refresh(); });
		});

		this.content.on('click', '.btn-add', function() {
			erpnext.stock.move_item(unescape($(this).attr('data-item')), null, $(this).attr('data-warehouse'),
				$(this).attr('data-actual_qty'), $(this).attr('data-rate'),
				function() { me.refresh(); });
		});

		// more
		this.content.find('.btn-more').on('click', function() {
			me.start += 20;
			me.refresh();
		});

	},
	refresh: function() {
		if(this.before_refresh) {
			this.before_refresh();
		}

		var me = this;
		frappe.call({
			method: 'erpnext.stock.dashboard.item_dashboard.get_data',
			args: {
				item_code: this.item_code,
				warehouse: this.warehouse,
				item_group: this.item_group,
				start: this.start,
				sort_by: this.sort_by,
				sort_order: this.sort_order,
			},
			callback: function(r) {
				me.render(r.message);
			}
		});
	},
	render: function(data) {
		if(this.start===0) {
			this.max_count = 0;
			this.result.empty();
		}

		var context = this.get_item_dashboard_data(data, this.max_count, true);
		this.max_count = this.max_count;

		// show more button
		if(data && data.length===21) {
			this.content.find('.more').removeClass('hidden');

			// remove the last element
			data.splice(-1);
		} else {
			this.content.find('.more').addClass('hidden');
		}

		// If not any stock in any warehouses provide a message to end user
		if (context.data.length > 0) {
			$(frappe.render_template('item_dashboard_list', context)).appendTo(this.result);
		} else {
			var message = __(" Currently no stock available in any warehouse")
			$(""+message+"").appendTo(this.result);
		}
	},
	get_item_dashboard_data: function(data, max_count, show_item) {
		if(!max_count) max_count = 0;
		if(!data) data = [];

		data.forEach(function(d) {
			d.actual_or_pending = d.projected_qty + d.reserved_qty + d.reserved_qty_for_production + d.reserved_qty_for_sub_contract;
			d.pending_qty = 0;
			d.total_reserved = d.reserved_qty + d.reserved_qty_for_production + d.reserved_qty_for_sub_contract;
			if(d.actual_or_pending > d.actual_qty) {
				d.pending_qty = d.actual_or_pending - d.actual_qty;
			}

			max_count = Math.max(d.actual_or_pending, d.actual_qty,
				d.total_reserved, max_count);
		});

		var can_write = 0;
		if(frappe.boot.user.can_write.indexOf("Stock Entry")>=0){
			can_write = 1;
		}

		return {
			data: data,
			max_count: max_count,
			can_write:can_write,
			show_item: show_item || false
		}
	}
})

erpnext.stock.move_item = function(item, source, target, actual_qty, rate, callback) {
	var dialog = new frappe.ui.Dialog({
		title: target ? __('Add Item') : __('Move Item'),
		fields: [
			{fieldname: 'item_code', label: __('Item'),
				fieldtype: 'Link', options: 'Item', read_only: 1},
			{fieldname: 'source', label: __('Source Warehouse'),
				fieldtype: 'Link', options: 'Warehouse', read_only: 1},
			{fieldname: 'target', label: __('Target Warehouse'),
				fieldtype: 'Link', options: 'Warehouse', reqd: 1},
			{fieldname: 'qty', label: __('Quantity'), reqd: 1,
				fieldtype: 'Float', description: __('Available {0}', [actual_qty]) },
			{fieldname: 'rate', label: __('Rate'), fieldtype: 'Currency', hidden: 1 },
		],
	})
	dialog.show();
	dialog.get_field('item_code').set_input(item);

	if(source) {
		dialog.get_field('source').set_input(source);
	} else {
		dialog.get_field('source').df.hidden = 1;
		dialog.get_field('source').refresh();
	}

	if(rate) {
		dialog.get_field('rate').set_value(rate);
		dialog.get_field('rate').df.hidden = 0;
		dialog.get_field('rate').refresh();
	}

	if(target) {
		dialog.get_field('target').df.read_only = 1;
		dialog.get_field('target').value = target;
		dialog.get_field('target').refresh();
	}

	dialog.set_primary_action(__('Submit'), function() {
		var values = dialog.get_values();
		if(!values) {
			return;
		}
		if(source && values.qty > actual_qty) {
			frappe.msgprint(__('Quantity must be less than or equal to {0}', [actual_qty]));
			return;
		}
		if(values.source === values.target) {
			frappe.msgprint(__('Source and target warehouse must be different'));
		}

		frappe.call({
			method: 'erpnext.stock.doctype.stock_entry.stock_entry_utils.make_stock_entry',
			args: values,
			freeze: true,
			callback: function(r) {
				frappe.show_alert(__('Stock Entry {0} created',
					['' + r.message.name+ '']));
				dialog.hide();
				callback(r);
			},
		});
	});

	$('
'
		+ __("Add more items or open full form") + '
')
		.appendTo(dialog.body)
		.find('.link-open')
		.on('click', function() {
			frappe.model.with_doctype('Stock Entry', function() {
				var doc = frappe.model.get_new_doc('Stock Entry');
				doc.from_warehouse = dialog.get_value('source');
				doc.to_warehouse = dialog.get_value('target');
				var row = frappe.model.add_child(doc, 'items');
				row.item_code = dialog.get_value('item_code');
				row.f_warehouse = dialog.get_value('target');
				row.t_warehouse = dialog.get_value('target');
				row.qty = dialog.get_value('qty');
				row.conversion_factor = 1;
				row.transfer_qty = dialog.get_value('qty');
				row.basic_rate = dialog.get_value('rate');
				frappe.set_route('Form', doc.doctype, doc.name);
			})
		});
}<|endoftext|>


<commit_before>describe('ch8',function(){
  it('nested functions and keyword "this"',function(){
    var o = {
      m: function(){
        expect(this).toBe(o);
        var self = this;
        expect(this).toBe(self);
        f();
        function f(){
          'use strict';
          expect(this).not.toBe(o);
          expect(this).toBeUndefined(); // 'this is global object in non strict'
          expect(self).toBe(o);
        }
      }
    };
    o.m();
  });
});
<commit_msg><commit_after>describe('ch8',function(){
  it('nested functions and keyword "this"',function(){
    var o = {
      m: function(){
        expect(this).toBe(o);
        var self = this;
        expect(this).toBe(self);
        f();
        function f(){
          'use strict';
          expect(this).not.toBe(o);
          expect(this).toBeUndefined(); // 'this is global object in non strict'
          expect(self).toBe(o);
        }
      }
    };
    o.m();
  });

  it('higher order functions', function(){
    function not(f){
      return function(){
        return !f.apply(this, arguments);
      };
    }
    var even = function(x){ return x % 2 === 0; };
    var odd = not(even);

    expect([1,3,5].every(odd)).toBe(true);
  });
});
<|endoftext|>


<commit_before>/*
 * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

//node.js deps
var gpio = require('gpio');
var Button = require('gpio-button');
//npm deps

//app deps
const deviceModule = require('..').device;
const cmdLineProcess = require('./lib/cmdline');

//begin module
var blueLedGpio, yellowLedGpio, intervalTimer;
var button6 = new Button('button6');
var button13 = new Button('button13');
var button19 = new Button('button19');
var button26 = new Button('button26');

button26.on('press', function () {
   console.log('button26 press');
});

button26.on('hold', function () {
});

button26.on('release', function () {
   console.log('button26 release');
});

button19.on('press', function () {
   console.log('button19 press');
   yellowLedGpio.set();

});

button19.on('hold', function () {
});

button19.on('release', function () {
   yellowLedGpio.set(0);
   console.log('button19 release');
});

button13.on('press', function () {
   console.log('button13 release');
});

button13.on('hold', function () {
});

button13.on('release', function () {
   console.log('button13 release');
});

button6.on('press', function () {
   blueLedGpio.set(0);
   console.log('button6 press');
});

button6.on('hold', function () {
});

button6.on('release', function () {
   blueLedGpio.set();
   console.log('button6 release');
});

blueLedGpio = gpio.export(23, {
   ready: function() {
      console.log("Blue LED Ready");
      intervalTimer = setInterval(function() {
      }, 2000);
   }
});

yellowLedGpio = gpio.export(24, {
   ready: function() {
      console.log("Yellow LED Ready");
      yellowLedGpio.on("change", function (val) {
         console.log(val);
      });

   }
});

// // Lets assume a different LED is hooked up to pin 4, the following code
// // will make that LED blink inversely with LED from pin 22
// gpio24 = gpio.export(23, {
//    ready: function() {
//       // bind to blueLedGpio's change event
//       blueLedGpio.on("change", function(val) {
//          gpio24.set(1 - val); // set gpio24 to the opposite value
//       });
//    }
// });

// // reset the headers and unexport after 10 seconds
// setTimeout(function() {
//    clearInterval(intervalTimer);          // stops the voltage cycling
//    blueLedGpio.removeAllListeners('change');   // unbinds change event
//    blueLedGpio.reset();                        // sets header to low
//    blueLedGpio.unexport();                     // unexport the header
//
//    gpio24.reset();
//    gpio24.unexport(function() {
//       // unexport takes a callback which gets fired as soon as unexporting is done
//       process.exit(); // exits your node program
//    });
// }, 10000);

function processTest(args) {
   //
   // The device module exports an MQTT instance, which will attempt
   // to connect to the AWS IoT endpoint configured in the arguments.
   // Once connected, it will emit events which our application can
   // handle.
   //
   const device = deviceModule({
      keyPath: args.privateKey,
      certPath: args.clientCert,
      caPath: args.caCert,
      clientId: args.clientId,
      region: args.region,
      baseReconnectTimeMs: args.baseReconnectTimeMs,
      keepalive: args.keepAlive,
      protocol: args.Protocol,
      port: args.Port,
      host: args.Host,
      debug: args.Debug
   });

   var timeout;
   var count = 0;
   const minimumDelay = 250;

   if (args.testMode === 1) {
      device.subscribe('topic_1');
   } else {
      device.subscribe('topic_2');
   }
   device.subscribe('led');
   if ((Math.max(args.delay, minimumDelay)) !== args.delay) {
      console.log('substituting ' + minimumDelay + 'ms delay for ' + args.delay + 'ms...');
   }
   timeout = setInterval(function() {
      count++;

      if (args.testMode === 1) {
         device.publish('topic_2', JSON.stringify({
            mode1Process: count
         }));
      } else {
         device.publish('topic_1', JSON.stringify({
            mode2Process: count
         }));
      }
   }, Math.max(args.delay, minimumDelay)); // clip to minimum

   //
   // Do a simple publish/subscribe demo based on the test-mode passed
   // in the command line arguments.  If test-mode is 1, subscribe to
   // 'topic_1' and publish to 'topic_2'; otherwise vice versa.  Publish
   // a message every four seconds.
   //
   device
      .on('connect', function() {
         console.log('connected');
      });
   device
      .on('close', function() {
         console.log('close');
      });
   device
      .on('reconnect', function() {
         console.log('reconnect');
      });
   device
      .on('offline', function() {
         console.log('offline');
      });
   device
      .on('error', function(error) {
         console.log('error', error);
      });
   device
      .on('message', function(topic, payload) {

         if(topic == "led") {
            console.log("led");
            if(payload!=null) {
               var jsonPayload = JSON.parse(payload.toString());
               if(jsonPayload.color == "yellow") {
                  yellowLedGpio.set();
                  setTimeout(function() { yellowLedGpio.reset(); }, 2000);
               }
               if(jsonPayload.color == "blue") {
                  blueLedGpio.set();
                  setTimeout(function() { blueLedGpio.reset(); }, 2000);
               }

            }
         }
         console.log('message', topic, payload.toString());
      });
}

module.exports = cmdLineProcess;

if (require.main === module) {
   cmdLineProcess('connect to the AWS IoT service and publish/subscribe to topics using MQTT, test modes 1-2',
      process.argv.slice(2), processTest);
}<commit_msg>buttons man press<commit_after>/*
 * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

//node.js deps
var gpio = require('gpio');
var Button = require('gpio-button');
//npm deps

//app deps
const deviceModule = require('..').device;
const cmdLineProcess = require('./lib/cmdline');

//begin module
var blueLedGpio, yellowLedGpio, intervalTimer;
var button6 = new Button('button6');
var button13 = new Button('button13');
var button19 = new Button('button19');
var button26 = new Button('button26');

button26.on('press', function () {
   console.log('button26 press');
});

button26.on('hold', function () {
});

button26.on('release', function () {
   console.log('button26 release');
});

button19.on('press', function () {
   console.log('button19 press');
   yellowLedGpio.set(0);

});

button19.on('hold', function () {
});

button19.on('release', function () {
   yellowLedGpio.set(1);
   console.log('button19 release');
});

button13.on('press', function () {
   console.log('button13 release');
});

button13.on('hold', function () {
});

button13.on('release', function () {
   console.log('button13 release');
});

button6.on('press', function () {
   blueLedGpio.set(0);
   console.log('button6 press');
});

button6.on('hold', function () {
});

button6.on('release', function () {
   blueLedGpio.set();
   console.log('button6 release');
});

blueLedGpio = gpio.export(23, {
   ready: function() {
      console.log("Blue LED Ready");
      intervalTimer = setInterval(function() {
      }, 2000);
   }
});

yellowLedGpio = gpio.export(24, {
   ready: function() {
      console.log("Yellow LED Ready");
      yellowLedGpio.on("change", function (val) {
         console.log(val);
      });

   }
});

// // Lets assume a different LED is hooked up to pin 4, the following code
// // will make that LED blink inversely with LED from pin 22
// gpio24 = gpio.export(23, {
//    ready: function() {
//       // bind to blueLedGpio's change event
//       blueLedGpio.on("change", function(val) {
//          gpio24.set(1 - val); // set gpio24 to the opposite value
//       });
//    }
// });

// // reset the headers and unexport after 10 seconds
// setTimeout(function() {
//    clearInterval(intervalTimer);          // stops the voltage cycling
//    blueLedGpio.removeAllListeners('change');   // unbinds change event
//    blueLedGpio.reset();                        // sets header to low
//    blueLedGpio.unexport();                     // unexport the header
//
//    gpio24.reset();
//    gpio24.unexport(function() {
//       // unexport takes a callback which gets fired as soon as unexporting is done
//       process.exit(); // exits your node program
//    });
// }, 10000);

function processTest(args) {
   //
   // The device module exports an MQTT instance, which will attempt
   // to connect to the AWS IoT endpoint configured in the arguments.
   // Once connected, it will emit events which our application can
   // handle.
   //
   const device = deviceModule({
      keyPath: args.privateKey,
      certPath: args.clientCert,
      caPath: args.caCert,
      clientId: args.clientId,
      region: args.region,
      baseReconnectTimeMs: args.baseReconnectTimeMs,
      keepalive: args.keepAlive,
      protocol: args.Protocol,
      port: args.Port,
      host: args.Host,
      debug: args.Debug
   });

   var timeout;
   var count = 0;
   const minimumDelay = 250;

   if (args.testMode === 1) {
      device.subscribe('topic_1');
   } else {
      device.subscribe('topic_2');
   }
   device.subscribe('led');
   if ((Math.max(args.delay, minimumDelay)) !== args.delay) {
      console.log('substituting ' + minimumDelay + 'ms delay for ' + args.delay + 'ms...');
   }
   timeout = setInterval(function() {
      count++;

      if (args.testMode === 1) {
         device.publish('topic_2', JSON.stringify({
            mode1Process: count
         }));
      } else {
         device.publish('topic_1', JSON.stringify({
            mode2Process: count
         }));
      }
   }, Math.max(args.delay, minimumDelay)); // clip to minimum

   //
   // Do a simple publish/subscribe demo based on the test-mode passed
   // in the command line arguments.  If test-mode is 1, subscribe to
   // 'topic_1' and publish to 'topic_2'; otherwise vice versa.  Publish
   // a message every four seconds.
   //
   device
      .on('connect', function() {
         console.log('connected');
      });
   device
      .on('close', function() {
         console.log('close');
      });
   device
      .on('reconnect', function() {
         console.log('reconnect');
      });
   device
      .on('offline', function() {
         console.log('offline');
      });
   device
      .on('error', function(error) {
         console.log('error', error);
      });
   device
      .on('message', function(topic, payload) {

         if(topic == "led") {
            console.log("led");
            if(payload!=null) {
               var jsonPayload = JSON.parse(payload.toString());
               if(jsonPayload.color == "yellow") {
                  yellowLedGpio.set();
                  setTimeout(function() { yellowLedGpio.reset(); }, 2000);
               }
               if(jsonPayload.color == "blue") {
                  blueLedGpio.set();
                  setTimeout(function() { blueLedGpio.reset(); }, 2000);
               }

            }
         }
         console.log('message', topic, payload.toString());
      });
}

module.exports = cmdLineProcess;

if (require.main === module) {
   cmdLineProcess('connect to the AWS IoT service and publish/subscribe to topics using MQTT, test modes 1-2',
      process.argv.slice(2), processTest);
}<|endoftext|>


<commit_before>/*globals $, GOVUK, suchi */
/*jslint
 white: true,
 browser: true */

$(function() {
  "use strict";
  function browserWarning() {
    var container = $('

'),
        text = $('
Upgrade your web browser (the software you use to access the internet), it’s out of date
'),
        closeLink = $('Close');

    return container.append(text.append(closeLink));
  }

  // we don't show the message when the cookie warning is also there
  if (GOVUK.cookie('seen_cookie_message')) {
    if (suchi.isOld(navigator.userAgent)) {
      if(GOVUK.cookie('govuk_not_first_visit') !== null && GOVUK.cookie('govuk_browser_upgrade_dismissed') === null){
        var $prompt = browserWarning();
        $('#global-cookie-message').after($prompt);
        $prompt.show();
        $prompt.on("click", ".dismiss", function(e) {
          $prompt.hide();
          // the warning is dismissable for 4 weeks, for users who are not in a
          // position to upgrade right now or unable to (no control of browser)
          GOVUK.cookie('govuk_browser_upgrade_dismissed', 'yes', { days: 28 });
        });
      }
    }

    // We're not showing the message on first visit
    GOVUK.cookie('govuk_not_first_visit', 'yes', { days: 28 });
  }
});
<commit_msg>Track when we show the browser upgrade prompt<commit_after>/*globals $, GOVUK, suchi */
/*jslint
 white: true,
 browser: true */

$(function() {
  "use strict";
  function browserWarning() {
    var container = $('

'),
        text = $('
Upgrade your web browser (the software you use to access the internet), it’s out of date
'),
        closeLink = $('Close');

    return container.append(text.append(closeLink));
  }

  // we don't show the message when the cookie warning is also there
  if (GOVUK.cookie('seen_cookie_message')) {
    if (suchi.isOld(navigator.userAgent)) {
      if(GOVUK.cookie('govuk_not_first_visit') !== null && GOVUK.cookie('govuk_browser_upgrade_dismissed') === null){
        var $prompt = browserWarning();
        $('#global-cookie-message').after($prompt);
        $prompt.show();
        window._gaq && _gaq.push(['_trackEvent', 'browser-check', 'prompt-shown', '', 1, true]);
        $prompt.on("click", ".dismiss", function(e) {
          $prompt.hide();
          // the warning is dismissable for 4 weeks, for users who are not in a
          // position to upgrade right now or unable to (no control of browser)
          GOVUK.cookie('govuk_browser_upgrade_dismissed', 'yes', { days: 28 });
        });
      }
    }

    // We're not showing the message on first visit
    GOVUK.cookie('govuk_not_first_visit', 'yes', { days: 28 });
  }
});
<|endoftext|>


<commit_before>'use strict';

require('./globals');

var consts = require('./consts'),
  util = require('./util'),
  vlfield = require('./field'),
  vlenc = require('./enc'),
  schema = require('./schema/schema');

module.exports = (function() {
  function Encoding(spec, theme) {
    var defaults = schema.instantiate(),
      specExtended = schema.util.merge(defaults, theme || {}, spec) ;

    this._data = specExtended.data;
    this._marktype = specExtended.marktype;
    this._enc = specExtended.encoding;
    this._config = specExtended.config;
    this._filter = specExtended.filter;
  }

  var proto = Encoding.prototype;

  Encoding.fromShorthand = function(shorthand, data, config, theme) {
    var c = consts.shorthand,
        split = shorthand.split(c.delim),
        marktype = split.shift().split(c.assign)[1].trim(),
        enc = vlenc.fromShorthand(split);

    return new Encoding({
      data: data,
      marktype: marktype,
      encoding: enc,
      config: config,
      filter: []
    }, theme);
  };

  Encoding.fromSpec = function(spec, theme) {
    return new Encoding(spec, theme);
  };

  proto.toShorthand = function() {
    var c = consts.shorthand;
    return 'mark' + c.assign + this._marktype +
      c.delim + vlenc.shorthand(this._enc);
  };

  Encoding.shorthand = function (spec) {
    var c = consts.shorthand;
    return 'mark' + c.assign + spec.marktype +
      c.delim + vlenc.shorthand(spec.enc);
  };

  Encoding.specFromShorthand = function(shorthand, data, config, excludeConfig) {
    return Encoding.fromShorthand(shorthand, data, config).toSpec(excludeConfig);
  };

  proto.toSpec = function(excludeConfig, excludeData) {
    var enc = util.duplicate(this._enc),
      spec;

    spec = {
      marktype: this._marktype,
      encoding: enc,
      filter: this._filter
    };

    if (!excludeConfig) {
      spec.config = util.duplicate(this._config);
    }

    if (!excludeData) {
      spec.data = util.duplicate(this._data);
    }

    // remove defaults
    var defaults = schema.instantiate();
    return schema.util.subtract(spec, defaults);
  };

  proto.marktype = function() {
    return this._marktype;
  };

  proto.is = function(m) {
    return this._marktype === m;
  };

  proto.has = function(encType) {
    // equivalent to calling vlenc.has(this._enc, encType)
    return this._enc[encType].name !== undefined;
  };

  proto.enc = function(et) {
    return this._enc[et];
  };

  proto.filter = function() {
    var filterNull = [],
      fields = this.fields(),
      self = this;

    util.forEach(fields, function(fieldList, fieldName) {
      if (fieldName === '*') return; //count

      if ((self.config('filterNull').Q && fieldList.containsType[Q]) ||
          (self.config('filterNull').T && fieldList.containsType[T]) ||
          (self.config('filterNull').O && fieldList.containsType[O]) ||
          (self.config('filterNull').N && fieldList.containsType[N])) {
        filterNull.push({
          operands: [fieldName],
          operator: 'notNull'
        });
      }
    });

    return filterNull.concat(this._filter);
  };

  // get "field" property for vega
  proto.field = function(et, nodata, nofn) {
    if (!this.has(et)) return null;

    var f = (nodata ? '' : 'data.');

    if (vlfield.isCount(this._enc[et])) {
      return f + 'count';
    } else if (!nofn && this._enc[et].bin) {
      return f + 'bin_' + this._enc[et].name;
    } else if (!nofn && this._enc[et].aggregate) {
      return f + this._enc[et].aggregate + '_' + this._enc[et].name;
    } else if (!nofn && this._enc[et].fn) {
      return f + this._enc[et].fn + '_' + this._enc[et].name;
    } else {
      return f + this._enc[et].name;
    }
  };

  proto.fieldName = function(et) {
    return this._enc[et].name;
  };

  /*
   * return key-value pairs of field name and list of fields of that field name
   */
  proto.fields = function() {
    return vlenc.fields(this._enc);
  };

  proto.fieldTitle = function(et) {
    if (vlfield.isCount(this._enc[et])) {
      return vlfield.count.displayName;
    }
    var fn = this._enc[et].aggregate || this._enc[et].fn || (this._enc[et].bin && 'bin');
    if (fn) {
      return fn.toUpperCase() + '(' + this._enc[et].name + ')';
    } else {
      return this._enc[et].name;
    }
  };

  proto.scale = function(et) {
    return this._enc[et].scale || {};
  };

  proto.axis = function(et) {
    return this._enc[et].axis || {};
  };

  proto.band = function(et) {
    return this._enc[et].band || {};
  };

  proto.bandSize = function(encType, useSmallBand) {
    useSmallBand = useSmallBand ||
      //isBandInSmallMultiples
      (encType === Y && this.has(ROW) && this.has(Y)) ||
      (encType === X && this.has(COL) && this.has(X));

    // if band.size is explicitly specified, follow the specification, otherwise draw value from config.
    return this.band(encType).size ||
      this.config(useSmallBand ? 'smallBandSize' : 'largeBandSize');
  };

  proto.aggregate = function(et) {
    return this._enc[et].aggregate;
  };

  // returns false if binning is disabled, otherwise an object with binning properties
  proto.bin = function(et) {
    var bin = this._enc[et].bin;
    if (bin === {})
      return false;
    if (bin === true)
      return {
        maxbins: schema.MAXBINS_DEFAULT
      };
    return bin;
  };

  proto.legend = function(et) {
    return this._enc[et].legend;
  };

  proto.value = function(et) {
    return this._enc[et].value;
  };

  proto.fn = function(et) {
    return this._enc[et].fn;
  };

  proto.sort = function(et, stats) {
    var sort = this._enc[et].sort,
      enc = this._enc,
      isTypes = vlfield.isTypes;

    if ((!sort || sort.length===0) &&
        // FIXME
        Encoding.toggleSort.support({enc:this._enc}, stats, true) && //HACK
        this.config('toggleSort') === Q
      ) {
      var qField = isTypes(enc.x, [N, O]) ? enc.y : enc.x;

      if (isTypes(enc[et], [N, O])) {
        sort = [{
          name: qField.name,
          aggregate: qField.aggregate,
          type: qField.type,
          reverse: true
        }];
      }
    }

    return sort;
  };

  proto.length = function() {
    return util.keys(this._enc).length;
  };

  proto.map = function(f) {
    return vlenc.map(this._enc, f);
  };

  proto.reduce = function(f, init) {
    return vlenc.reduce(this._enc, f, init);
  };

  proto.forEach = function(f) {
    return vlenc.forEach(this._enc, f);
  };

  proto.type = function(et) {
    return this.has(et) ? this._enc[et].type : null;
  };

  proto.role = function(et) {
    return this.has(et) ? vlfield.role(this._enc[et]) : null;
  };

  proto.text = function(prop) {
    var text = this._enc[TEXT].text;
    return prop ? text[prop] : text;
  };

  proto.font = function(prop) {
    var font = this._enc[TEXT].font;
    return prop ? font[prop] : font;
  };

  proto.isType = function(et, type) {
    var field = this.enc(et);
    return field && Encoding.isType(field, type);
  };

  Encoding.isType = vlfield.isType;

  Encoding.isOrdinalScale = function(encoding, encType) {
    return vlfield.isOrdinalScale(encoding.enc(encType));
  };

  Encoding.isDimension = function(encoding, encType) {
    return vlfield.isDimension(encoding.enc(encType));
  };

  Encoding.isMeasure = function(encoding, encType) {
    return vlfield.isMeasure(encoding.enc(encType));
  };

  proto.isOrdinalScale = function(encType) {
    return this.has(encType) && Encoding.isOrdinalScale(this, encType);
  };

  proto.isDimension = function(encType) {
    return this.has(encType) && Encoding.isDimension(this, encType);
  };

  proto.isMeasure = function(encType) {
    return this.has(encType) && Encoding.isMeasure(this, encType);
  };

  proto.isAggregate = function() {
    return vlenc.isAggregate(this._enc);
  };

  Encoding.isAggregate = function(spec) {
    return vlenc.isAggregate(spec.enc);
  };

  Encoding.alwaysNoOcclusion = function(spec) {
    // FIXME raw OxQ with # of rows = # of O
    return vlenc.isAggregate(spec.enc);
  };

  Encoding.isStack = function(spec) {
    // FIXME update this once we have control for stack ...
    return (spec.marktype === 'bar' || spec.marktype === 'area') &&
      spec.enc.color;
  };

  proto.isStack = function() {
    // FIXME update this once we have control for stack ...
    return (this.is('bar') || this.is('area')) && this.has('color');
  };

  proto.cardinality = function(encType, stats) {
    return vlfield.cardinality(this.enc(encType), stats, this.config('filterNull'));
  };

  proto.isRaw = function() {
    return !this.isAggregate();
  };

  proto.data = function(name) {
    return this._data[name];
  };

   // returns whether the encoding has values embedded
  proto.hasValues = function() {
    var vals = this.data('values');
    return vals && vals.length;
  };

  proto.config = function(name) {
    return this._config[name];
  };

  Encoding.transpose = function(spec) {
    var oldenc = spec.enc,
      enc = util.duplicate(spec.enc);
    enc.x = oldenc.y;
    enc.y = oldenc.x;
    enc.row = oldenc.col;
    enc.col = oldenc.row;
    spec.enc = enc;
    return spec;
  };

  // FIXME: REMOVE everything below here

  Encoding.toggleSort = function(spec) {
    spec.config = spec.config || {};
    spec.config.toggleSort = spec.config.toggleSort === Q ? N : Q;
    return spec;
  };

  Encoding.toggleSort.direction = function(spec) {
    if (!Encoding.toggleSort.support(spec)) { return; }
    var enc = spec.enc;
    return enc.x.type === N ? 'x' : 'y';
  };

  Encoding.toggleSort.mode = function(spec) {
    return spec.config.toggleSort;
  };

  Encoding.toggleSort.support = function(spec, stats) {
    var enc = spec.enc,
      isTypes = vlfield.isTypes;

    if (vlenc.has(enc, ROW) || vlenc.has(enc, COL) ||
      !vlenc.has(enc, X) || !vlenc.has(enc, Y) ||
      !Encoding.alwaysNoOcclusion(spec, stats)) {
      return false;
    }

    return ( isTypes(enc.x, [N,O]) && vlfield.isMeasure(enc.y)) ? 'x' :
      ( isTypes(enc.y, [N,O]) && vlfield.isMeasure(enc.x)) ? 'y' : false;
  };

  Encoding.toggleFilterNullO = function(spec) {
    spec.config = spec.config || {};
    spec.config.filterNull = spec.config.filterNull || { //FIXME
      T: true,
      Q: true
    };
    spec.config.filterNull.O = !spec.config.filterNull.O;
    return spec;
  };

  Encoding.toggleFilterNullO.support = function(spec, stats) {
    var fields = vlenc.fields(spec.enc);
    for (var fieldName in fields) {
      var fieldList = fields[fieldName];
      if (fieldList.containsType.O && fieldName in stats && stats[fieldName].nulls > 0) {
        return true;
      }
    }
    return false;
  };

  return Encoding;
})();
<commit_msg>more `enc` => `encoding`<commit_after>'use strict';

require('./globals');

var consts = require('./consts'),
  util = require('./util'),
  vlfield = require('./field'),
  vlenc = require('./enc'),
  schema = require('./schema/schema');

module.exports = (function() {
  function Encoding(spec, theme) {
    var defaults = schema.instantiate(),
      specExtended = schema.util.merge(defaults, theme || {}, spec) ;

    this._data = specExtended.data;
    this._marktype = specExtended.marktype;
    this._enc = specExtended.encoding;
    this._config = specExtended.config;
    this._filter = specExtended.filter;
  }

  var proto = Encoding.prototype;

  Encoding.fromShorthand = function(shorthand, data, config, theme) {
    var c = consts.shorthand,
        split = shorthand.split(c.delim),
        marktype = split.shift().split(c.assign)[1].trim(),
        enc = vlenc.fromShorthand(split);

    return new Encoding({
      data: data,
      marktype: marktype,
      encoding: enc,
      config: config,
      filter: []
    }, theme);
  };

  Encoding.fromSpec = function(spec, theme) {
    return new Encoding(spec, theme);
  };

  proto.toShorthand = function() {
    var c = consts.shorthand;
    return 'mark' + c.assign + this._marktype +
      c.delim + vlenc.shorthand(this._enc);
  };

  Encoding.shorthand = function (spec) {
    var c = consts.shorthand;
    return 'mark' + c.assign + spec.marktype +
      c.delim + vlenc.shorthand(spec.encoding);
  };

  Encoding.specFromShorthand = function(shorthand, data, config, excludeConfig) {
    return Encoding.fromShorthand(shorthand, data, config).toSpec(excludeConfig);
  };

  proto.toSpec = function(excludeConfig, excludeData) {
    var enc = util.duplicate(this._enc),
      spec;

    spec = {
      marktype: this._marktype,
      encoding: enc,
      filter: this._filter
    };

    if (!excludeConfig) {
      spec.config = util.duplicate(this._config);
    }

    if (!excludeData) {
      spec.data = util.duplicate(this._data);
    }

    // remove defaults
    var defaults = schema.instantiate();
    return schema.util.subtract(spec, defaults);
  };

  proto.marktype = function() {
    return this._marktype;
  };

  proto.is = function(m) {
    return this._marktype === m;
  };

  proto.has = function(encType) {
    // equivalent to calling vlenc.has(this._enc, encType)
    return this._enc[encType].name !== undefined;
  };

  proto.enc = function(et) {
    return this._enc[et];
  };

  proto.filter = function() {
    var filterNull = [],
      fields = this.fields(),
      self = this;

    util.forEach(fields, function(fieldList, fieldName) {
      if (fieldName === '*') return; //count

      if ((self.config('filterNull').Q && fieldList.containsType[Q]) ||
          (self.config('filterNull').T && fieldList.containsType[T]) ||
          (self.config('filterNull').O && fieldList.containsType[O]) ||
          (self.config('filterNull').N && fieldList.containsType[N])) {
        filterNull.push({
          operands: [fieldName],
          operator: 'notNull'
        });
      }
    });

    return filterNull.concat(this._filter);
  };

  // get "field" property for vega
  proto.field = function(et, nodata, nofn) {
    if (!this.has(et)) return null;

    var f = (nodata ? '' : 'data.');

    if (vlfield.isCount(this._enc[et])) {
      return f + 'count';
    } else if (!nofn && this._enc[et].bin) {
      return f + 'bin_' + this._enc[et].name;
    } else if (!nofn && this._enc[et].aggregate) {
      return f + this._enc[et].aggregate + '_' + this._enc[et].name;
    } else if (!nofn && this._enc[et].fn) {
      return f + this._enc[et].fn + '_' + this._enc[et].name;
    } else {
      return f + this._enc[et].name;
    }
  };

  proto.fieldName = function(et) {
    return this._enc[et].name;
  };

  /*
   * return key-value pairs of field name and list of fields of that field name
   */
  proto.fields = function() {
    return vlenc.fields(this._enc);
  };

  proto.fieldTitle = function(et) {
    if (vlfield.isCount(this._enc[et])) {
      return vlfield.count.displayName;
    }
    var fn = this._enc[et].aggregate || this._enc[et].fn || (this._enc[et].bin && 'bin');
    if (fn) {
      return fn.toUpperCase() + '(' + this._enc[et].name + ')';
    } else {
      return this._enc[et].name;
    }
  };

  proto.scale = function(et) {
    return this._enc[et].scale || {};
  };

  proto.axis = function(et) {
    return this._enc[et].axis || {};
  };

  proto.band = function(et) {
    return this._enc[et].band || {};
  };

  proto.bandSize = function(encType, useSmallBand) {
    useSmallBand = useSmallBand ||
      //isBandInSmallMultiples
      (encType === Y && this.has(ROW) && this.has(Y)) ||
      (encType === X && this.has(COL) && this.has(X));

    // if band.size is explicitly specified, follow the specification, otherwise draw value from config.
    return this.band(encType).size ||
      this.config(useSmallBand ? 'smallBandSize' : 'largeBandSize');
  };

  proto.aggregate = function(et) {
    return this._enc[et].aggregate;
  };

  // returns false if binning is disabled, otherwise an object with binning properties
  proto.bin = function(et) {
    var bin = this._enc[et].bin;
    if (bin === {})
      return false;
    if (bin === true)
      return {
        maxbins: schema.MAXBINS_DEFAULT
      };
    return bin;
  };

  proto.legend = function(et) {
    return this._enc[et].legend;
  };

  proto.value = function(et) {
    return this._enc[et].value;
  };

  proto.fn = function(et) {
    return this._enc[et].fn;
  };

  proto.sort = function(et, stats) {
    var sort = this._enc[et].sort,
      enc = this._enc,
      isTypes = vlfield.isTypes;

    if ((!sort || sort.length===0) &&
        // FIXME
        Encoding.toggleSort.support({enc:this._enc}, stats, true) && //HACK
        this.config('toggleSort') === Q
      ) {
      var qField = isTypes(enc.x, [N, O]) ? enc.y : enc.x;

      if (isTypes(enc[et], [N, O])) {
        sort = [{
          name: qField.name,
          aggregate: qField.aggregate,
          type: qField.type,
          reverse: true
        }];
      }
    }

    return sort;
  };

  proto.length = function() {
    return util.keys(this._enc).length;
  };

  proto.map = function(f) {
    return vlenc.map(this._enc, f);
  };

  proto.reduce = function(f, init) {
    return vlenc.reduce(this._enc, f, init);
  };

  proto.forEach = function(f) {
    return vlenc.forEach(this._enc, f);
  };

  proto.type = function(et) {
    return this.has(et) ? this._enc[et].type : null;
  };

  proto.role = function(et) {
    return this.has(et) ? vlfield.role(this._enc[et]) : null;
  };

  proto.text = function(prop) {
    var text = this._enc[TEXT].text;
    return prop ? text[prop] : text;
  };

  proto.font = function(prop) {
    var font = this._enc[TEXT].font;
    return prop ? font[prop] : font;
  };

  proto.isType = function(et, type) {
    var field = this.enc(et);
    return field && Encoding.isType(field, type);
  };

  Encoding.isType = vlfield.isType;

  Encoding.isOrdinalScale = function(encoding, encType) {
    return vlfield.isOrdinalScale(encoding.enc(encType));
  };

  Encoding.isDimension = function(encoding, encType) {
    return vlfield.isDimension(encoding.enc(encType));
  };

  Encoding.isMeasure = function(encoding, encType) {
    return vlfield.isMeasure(encoding.enc(encType));
  };

  proto.isOrdinalScale = function(encType) {
    return this.has(encType) && Encoding.isOrdinalScale(this, encType);
  };

  proto.isDimension = function(encType) {
    return this.has(encType) && Encoding.isDimension(this, encType);
  };

  proto.isMeasure = function(encType) {
    return this.has(encType) && Encoding.isMeasure(this, encType);
  };

  proto.isAggregate = function() {
    return vlenc.isAggregate(this._enc);
  };

  Encoding.isAggregate = function(spec) {
    return vlenc.isAggregate(spec.encoding);
  };

  Encoding.alwaysNoOcclusion = function(spec) {
    // FIXME raw OxQ with # of rows = # of O
    return vlenc.isAggregate(spec.encoding);
  };

  Encoding.isStack = function(spec) {
    // FIXME update this once we have control for stack ...
    return (spec.marktype === 'bar' || spec.marktype === 'area') &&
      spec.encoding.color;
  };

  proto.isStack = function() {
    // FIXME update this once we have control for stack ...
    return (this.is('bar') || this.is('area')) && this.has('color');
  };

  proto.cardinality = function(encType, stats) {
    return vlfield.cardinality(this.enc(encType), stats, this.config('filterNull'));
  };

  proto.isRaw = function() {
    return !this.isAggregate();
  };

  proto.data = function(name) {
    return this._data[name];
  };

   // returns whether the encoding has values embedded
  proto.hasValues = function() {
    var vals = this.data('values');
    return vals && vals.length;
  };

  proto.config = function(name) {
    return this._config[name];
  };

  Encoding.transpose = function(spec) {
    var oldenc = spec.encoding,
      enc = util.duplicate(spec.encoding);
    enc.x = oldenc.y;
    enc.y = oldenc.x;
    enc.row = oldenc.col;
    enc.col = oldenc.row;
    spec.encoding = enc;
    return spec;
  };

  // FIXME: REMOVE everything below here

  Encoding.toggleSort = function(spec) {
    spec.config = spec.config || {};
    spec.config.toggleSort = spec.config.toggleSort === Q ? N : Q;
    return spec;
  };

  Encoding.toggleSort.direction = function(spec) {
    if (!Encoding.toggleSort.support(spec)) { return; }
    var enc = spec.encoding;
    return enc.x.type === N ? 'x' : 'y';
  };

  Encoding.toggleSort.mode = function(spec) {
    return spec.config.toggleSort;
  };

  Encoding.toggleSort.support = function(spec, stats) {
    var enc = spec.encoding,
      isTypes = vlfield.isTypes;

    if (vlenc.has(enc, ROW) || vlenc.has(enc, COL) ||
      !vlenc.has(enc, X) || !vlenc.has(enc, Y) ||
      !Encoding.alwaysNoOcclusion(spec, stats)) {
      return false;
    }

    return ( isTypes(enc.x, [N,O]) && vlfield.isMeasure(enc.y)) ? 'x' :
      ( isTypes(enc.y, [N,O]) && vlfield.isMeasure(enc.x)) ? 'y' : false;
  };

  Encoding.toggleFilterNullO = function(spec) {
    spec.config = spec.config || {};
    spec.config.filterNull = spec.config.filterNull || { //FIXME
      T: true,
      Q: true
    };
    spec.config.filterNull.O = !spec.config.filterNull.O;
    return spec;
  };

  Encoding.toggleFilterNullO.support = function(spec, stats) {
    var fields = vlenc.fields(spec.encoding);
    for (var fieldName in fields) {
      var fieldList = fields[fieldName];
      if (fieldList.containsType.O && fieldName in stats && stats[fieldName].nulls > 0) {
        return true;
      }
    }
    return false;
  };

  return Encoding;
})();
<|endoftext|>